詳解Android——藍(lán)牙技術(shù) 帶你實(shí)現(xiàn)終端間數(shù)據(jù)傳輸
藍(lán)牙技術(shù)在智能硬件方面有很多用武之地,今天我就為大家分享一下藍(lán)牙在Android系統(tǒng)下的使用方法技巧,并實(shí)現(xiàn)一下兩個(gè)終端間數(shù)據(jù)的傳輸。
藍(lán)牙(Bluetooth)是一種短距離的無(wú)線通信技術(shù)標(biāo)準(zhǔn),藍(lán)牙協(xié)議分為4層,即核心協(xié)議層、電纜替代協(xié)議層、電話控制協(xié)議層和采納的其它協(xié)議層。
這4種協(xié)議中最重要的是核心協(xié)議。藍(lán)牙的核心協(xié)議包括基帶、鏈路管理、邏輯鏈路控制和適應(yīng)協(xié)議四部分。其中鏈路管理(LMP)負(fù)責(zé)藍(lán)牙組件間連接的建立。邏輯鏈路控制與適應(yīng)協(xié)議(L2CAP)位于基帶協(xié)議層上,屬于數(shù)據(jù)鏈路層,是一個(gè)為高層傳輸和應(yīng)用層協(xié)議屏蔽基帶協(xié)議的適配協(xié)議。
1.打開(kāi)和關(guān)閉藍(lán)牙
第一種方法相對(duì)簡(jiǎn)單,直接調(diào)用系統(tǒng)對(duì)話框啟動(dòng)藍(lán)牙:
在AndroidManifest文件中添加需要的權(quán)限,高版本也不需要?jiǎng)討B(tài)授權(quán):
<uses-permission android:name="android.permission.BLUETOOTH" />
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);
如果不想讓用戶看到這個(gè)對(duì)話框,那么我們還可以選擇第二種方法,進(jìn)行靜默開(kāi)啟藍(lán)牙。
第二種方法,靜默開(kāi)啟,不會(huì)有方法一的對(duì)話框:
在AndroidManifest文件中添加需要的權(quán)限:
<!-- 已適配Android6.0 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
由于藍(lán)牙所需要的權(quán)限包含Dangerous Permissions,所以我們需要在Java代碼中進(jìn)行動(dòng)態(tài)授權(quán)處理:
private static final int REQUEST_BLUETOOTH_PERMISSION=10; private void requestBluetoothPermission(){ //判斷系統(tǒng)版本 if (Build.VERSION.SDK_INT >= 23) { //檢測(cè)當(dāng)前app是否擁有某個(gè)權(quán)限 int checkCallPhonePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION); //判斷這個(gè)權(quán)限是否已經(jīng)授權(quán)過(guò) if(checkCallPhonePermission != PackageManager.PERMISSION_GRANTED){ //判斷是否需要 向用戶解釋,為什么要申請(qǐng)?jiān)摍?quán)限 if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION)) Toast.makeText(this,"Need bluetooth permission.", Toast.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this ,new String[] {Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_BLUETOOTH_PERMISSION); return; }else{ } } else { } }
接下來(lái)我們就可以靜默開(kāi)啟藍(lán)牙了:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter.enable(); //開(kāi)啟 //mBluetoothAdapter.disable(); //關(guān)閉
下面我們來(lái)看一下如何通過(guò)代碼搜索藍(lán)牙設(shè)備。
2.通過(guò)代碼搜索藍(lán)牙設(shè)備
搜索分為主動(dòng)搜索和被動(dòng)搜索。
我們開(kāi)始進(jìn)行主動(dòng)搜索:
(1)創(chuàng)建BluetoothAdapter對(duì)象
TextView tvDevices = (TextView)findViewById(R.id.tv_devices); BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
(2)我們先獲取并顯示一下已經(jīng)配對(duì)的藍(lán)牙設(shè)備列表
//獲取已經(jīng)配對(duì)的藍(lán)牙設(shè)備 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { tvDevices.append(device.getName() + ":" + device.getAddress()); } }
(3)下面我們定義廣播接收器
// 設(shè)置廣播信息過(guò)濾 IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_FOUND);//每搜索到一個(gè)設(shè)備就會(huì)發(fā)送一個(gè)該廣播 filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//當(dāng)全部搜索完后發(fā)送該廣播 filter.setPriority(Integer.MAX_VALUE);//設(shè)置優(yōu)先級(jí) // 注冊(cè)藍(lán)牙搜索廣播接收者,接收并處理搜索結(jié)果 this.registerReceiver(receiver, filter);
藍(lán)牙設(shè)備的廣播接收器如下:
/** * 定義廣播接收器 */ private final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { tvDevices.append(device.getName() + ":"+ device.getAddress()); } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //已搜素完成 } } };
(4)我們創(chuàng)建一個(gè)Button按鈕,當(dāng)點(diǎn)擊Button時(shí)進(jìn)行搜索,Button點(diǎn)擊事件如下:
//如果當(dāng)前在搜索,就先取消搜索 if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } //開(kāi)啟搜索 mBluetoothAdapter.startDiscovery();
3.藍(lán)牙的UUID
兩個(gè)藍(lán)牙設(shè)備進(jìn)行連接時(shí)需要使用同一個(gè)UUID。但很多讀者可能發(fā)現(xiàn),有很多型號(hào)的手機(jī)(可能是非Android系統(tǒng)的手機(jī))之間使用了不同的程序也可以使用藍(lán)牙進(jìn)行通訊。從表面上看,它們之間幾乎不可能使用同一個(gè)UUID。
UUID的格式如下:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UUID的格式被分成5段,其中中間3段的字符數(shù)相同,都是4,第1段是8個(gè)字符,最后一段是12個(gè)字符。所以UUID實(shí)際上是一個(gè)8-4-4-4-12的字符串。
實(shí)際上,UUID和TCP的端口一樣,也有一些默認(rèn)的值。例如,將藍(lán)牙模擬成串口的服務(wù)就使用了一個(gè)標(biāo)準(zhǔn)的UUID:
00001101-0000-1000-8000-00805F9B34FB
除此之外,還有很多標(biāo)準(zhǔn)的UUID,如下面就是兩個(gè)標(biāo)準(zhǔn)的UUID:
信息同步服務(wù):00001104-0000-1000-8000-00805F9B34FB
文件傳輸服務(wù):00001106-0000-1000-8000-00805F9B34FB
4.藍(lán)牙終端間數(shù)據(jù)傳輸
通過(guò)藍(lán)牙傳輸數(shù)據(jù)與Socket類似。在網(wǎng)絡(luò)中使用Socket和ServerSocket控制客戶端和服務(wù)端的數(shù)據(jù)讀寫(xiě)。而藍(lán)牙通訊也由客戶端和服務(wù)端Socket來(lái)完成。藍(lán)牙客戶端Socket是BluetoothSocket,藍(lán)牙服務(wù)端Socket是BluetoothServerSocket。這兩個(gè)類都在android.bluetooth包中。
無(wú)論是BluetoothSocket,還是BluetoothServerSocket,都需要一個(gè)UUID(全局唯一標(biāo)識(shí)符,Universally Unique Identifier),UUID相當(dāng)于Socket的端口,而藍(lán)牙地址相當(dāng)于Socket的IP。
我們開(kāi)始進(jìn)行模擬一個(gè)藍(lán)牙數(shù)據(jù)的傳輸:
首先來(lái)看客戶端:
(1)定義全局常量變量
private ListView lvDevices; private BluetoothAdapter mBluetoothAdapter; private List<String> bluetoothDevices = new ArrayList<String>(); private ArrayAdapter<String> arrayAdapter; private final UUID MY_UUID = UUID .fromString("abcd1234-ab12-ab12-ab12-abcdef123456");//隨便定義一個(gè) private BluetoothSocket clientSocket; private BluetoothDevice device; private OutputStream os;//輸出流
(2)在onCreate方法中做初始化操作
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); lvDevices = (ListView) findViewById(R.id.lv_devices); //獲取已經(jīng)配對(duì)的藍(lán)牙設(shè)備 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { bluetoothDevices.add(device.getName() + ":"+ device.getAddress()); } } arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1,bluetoothDevices); lvDevices.setAdapter(arrayAdapter); lvDevices.setOnItemClickListener(this);//Activity實(shí)現(xiàn)OnItemClickListener接口 //每搜索到一個(gè)設(shè)備就會(huì)發(fā)送一個(gè)該廣播 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(receiver, filter); //當(dāng)全部搜索完后發(fā)送該廣播 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(receiver, filter);
藍(lán)牙設(shè)備的廣播接收器如下:
/** * 定義廣播接收器 */ private final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { bluetoothDevices.add(device.getName() + ":" + device.getAddress()); arrayAdapter.notifyDataSetChanged();//更新適配器 } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //已搜素完成 } } };
(4)我們創(chuàng)建一個(gè)Button按鈕,當(dāng)點(diǎn)擊Button時(shí)進(jìn)行搜索,Button點(diǎn)擊事件如下:
//如果當(dāng)前在搜索,就先取消搜索 if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } //開(kāi)啟搜索 mBluetoothAdapter.startDiscovery();
(5)接下來(lái)我們?cè)O(shè)置列表的點(diǎn)擊事件:
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String s = arrayAdapter.getItem(position); String address = s.substring(s.indexOf(":") + 1).trim();//把地址解析出來(lái) //主動(dòng)連接藍(lán)牙服務(wù)端 try { //判斷當(dāng)前是否正在搜索 if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } try { if (device == null) { //獲得遠(yuǎn)程設(shè)備 device = mBluetoothAdapter.getRemoteDevice(address); } if (clientSocket == null) { //創(chuàng)建客戶端藍(lán)牙Socket clientSocket = device.createRfcommSocketToServiceRecord(MY_UUID); //開(kāi)始連接藍(lán)牙,如果沒(méi)有配對(duì)則彈出對(duì)話框提示我們進(jìn)行配對(duì) clientSocket.connect(); //獲得輸出流(客戶端指向服務(wù)端輸出文本) os = clientSocket.getOutputStream(); } } catch (Exception e) { } if (os != null) { //往服務(wù)端寫(xiě)信息 os.write("藍(lán)牙信息來(lái)了".getBytes("utf-8")); } } catch (Exception e) { } }
接下來(lái)看服務(wù)端:
服務(wù)端使用的是另一部手機(jī),接受上面手機(jī)通過(guò)藍(lán)牙發(fā)送過(guò)來(lái)的信息并顯示。
(1)定義全局常量變量:
private BluetoothAdapter mBluetoothAdapter; private AcceptThread acceptThread; private final UUID MY_UUID = UUID .fromString("abcd1234-ab12-ab12-ab12-abcdef123456");//和客戶端相同的UUID private final String NAME = "Bluetooth_Socket"; private BluetoothServerSocket serverSocket; private BluetoothSocket socket; private InputStream is;//輸入流
(2)定義服務(wù)端線程類:
private Handler handler = new Handler() { public void handleMessage(Message msg) { Toast.makeText(getApplicationContext(), String.valueOf(msg.obj), Toast.LENGTH_LONG).show(); super.handleMessage(msg); } }; //服務(wù)端監(jiān)聽(tīng)客戶端的線程類 private class AcceptThread extends Thread { public AcceptThread() { try { serverSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (Exception e) { } } public void run() { try { socket = serverSocket.accept(); is = socket.getInputStream(); while(true) { byte[] buffer =new byte[1024]; int count = is.read(buffer); Message msg = new Message(); msg.obj = new String(buffer, 0, count, "utf-8"); handler.sendMessage(msg); } } catch (Exception e) { } } }
(3)在onCreate方法中初始化線程類并開(kāi)啟
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); acceptThread = new AcceptThread(); acceptThread.start();
我們運(yùn)行程序看一下效果圖:
點(diǎn)擊“搜索藍(lán)牙設(shè)備”按鈕,就會(huì)搜索到另一臺(tái)手機(jī)的藍(lán)牙信息,我們點(diǎn)擊條目,另一臺(tái)手機(jī)會(huì)出現(xiàn)如下變化:
彈出Toast,此時(shí)證明我們的藍(lán)牙數(shù)據(jù)已經(jīng)傳輸過(guò)來(lái)了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 如何正確實(shí)現(xiàn)Android啟動(dòng)屏畫(huà)面的方法(避免白屏)
- Android編程之簡(jiǎn)單啟動(dòng)畫(huà)面實(shí)現(xiàn)方法
- Android簡(jiǎn)單實(shí)現(xiàn)啟動(dòng)畫(huà)面的方法
- Android編程中調(diào)用Camera時(shí)預(yù)覽畫(huà)面有旋轉(zhuǎn)問(wèn)題的解決方法
- Android啟動(dòng)畫(huà)面的實(shí)現(xiàn)方法
- Android開(kāi)機(jī)畫(huà)面的具體修改方法
- android Socket實(shí)現(xiàn)簡(jiǎn)單聊天功能以及文件傳輸
- Android實(shí)時(shí)獲取攝像頭畫(huà)面?zhèn)鬏斨罰C端思路詳解
相關(guān)文章
AndroidStudio3 支持 Java8 了請(qǐng)問(wèn)你敢用嗎
Google 發(fā)布了 AS 3.0,以及一系列的 Support 包,有意思的新東西挺多,AS3里面有一個(gè)亮眼的特性就是支持J8。接下來(lái)通過(guò)本文給大家分享AndroidStudio3 支持 Java8 的相關(guān)內(nèi)容,感興趣的朋友一起看看吧2017-11-11Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解
這篇文章主要介紹了Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解,文中主要借助Bitmap的canvas.drawPath的api來(lái)實(shí)現(xiàn),需要的朋友可以參考下2016-03-03Android布局(RelativeLayout、TableLayout等)使用方法
這篇文章主要介紹了Android布局使用方法及各種屬性介紹,包括RelativeLayout、TableLayout等,感興趣的朋友可以參考一下2016-03-03Unity3D游戲引擎實(shí)現(xiàn)在Android中打開(kāi)WebView的實(shí)例
這篇文章主要介紹了Unity3D游戲引擎在Android中打開(kāi)WebView的實(shí)例,需要的朋友可以參考下2014-07-07Android多設(shè)備多module打包fat-aar(最新推薦)
這篇文章主要介紹了Android多設(shè)備多module打包(fat-aar),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03Android實(shí)現(xiàn)LED發(fā)光字效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)LED發(fā)光字效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03Android的TextView與Html相結(jié)合的具體方法
Android的TextView與Html相結(jié)合的具體方法,需要的朋友可以參考一下2013-06-06Android自定義View繪圖實(shí)現(xiàn)拖影動(dòng)畫(huà)
這篇文章主要介紹了Android自定義View繪圖實(shí)現(xiàn)拖影動(dòng)畫(huà),,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09