Android藍(lán)牙通信之搜索藍(lán)牙設(shè)備
一:注意事項(xiàng)
1:android6.0使用藍(lán)牙時(shí),需要開(kāi)啟gps定位權(quán)限,不然無(wú)法搜索其它藍(lán)牙設(shè)備。
二:權(quán)限
1:權(quán)限配置
<!--允許程序連接到已配對(duì)的藍(lán)牙設(shè)備--> <uses-permission android:name="android.permission.BLUETOOTH" /> <!-- 允許程序發(fā)現(xiàn)和配對(duì)藍(lán)牙設(shè)備 --> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!--android 6.0 涉及到的權(quán)限--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 在SDCard中創(chuàng)建與刪除文件的權(quán)限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SDCard寫(xiě)入數(shù)據(jù)的權(quán)限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2:動(dòng)態(tài)權(quán)限代碼
由于需要用到存儲(chǔ)卡,定位等,android6.0以上需要代碼動(dòng)態(tài)設(shè)置。
a)獲取定位設(shè)置
if (Build.VERSION.SDK_INT >= 23) { boolean isLocat = isLocationOpen(getApplicationContext()); Toast.makeText(mContext, "isLo:" + isLocat, Toast.LENGTH_LONG).show(); //開(kāi)啟位置服務(wù),支持獲取ble藍(lán)牙掃描結(jié)果 if (!isLocat) { Intent enableLocate = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivityForResult(enableLocate, 1); } } /** * 判斷位置信息是否開(kāi)啟 * * @param context * @return */ private static boolean isLocationOpen(final Context context) { LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); //gps定位 boolean isGpsProvider = manager.isProviderEnabled(LocationManager.GPS_PROVIDER); //網(wǎng)絡(luò)定位 boolean isNetWorkProvider = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); return isGpsProvider || isNetWorkProvider; }
b)存儲(chǔ)卡權(quán)限設(shè)置
if (Build.VERSION.SDK_INT >= 23) { int write = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); int read = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE); //動(dòng)態(tài)請(qǐng)求讀寫(xiě)sd卡權(quán)限 if (write != PackageManager.PERMISSION_GRANTED || read != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, SD_CARD); } }
然后通過(guò)onRequestPermissionsResult()方法獲取動(dòng)態(tài)權(quán)限的結(jié)果:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode){ case SD_CARD: if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){ //允許訪問(wèn) }else{ Toast.makeText(mContext,"您拒絕了程序訪問(wèn)存儲(chǔ)卡",Toast.LENGTH_LONG).show(); } break; case COARES_LOCATION: break; } }
三:藍(lán)牙搜索
android.bluetooth.BluetoothAdapter 是藍(lán)牙開(kāi)發(fā)用得比較多,并且比較重要的一個(gè)類(lèi),可以設(shè)備藍(lán)牙名稱(chēng),打開(kāi),關(guān)閉,搜索等常規(guī)操作。
1 藍(lán)牙打開(kāi),以及搜索
藍(lán)牙打開(kāi)和關(guān)閉信息使用BluetoothAdapter.ACTION_STATE_CHANGED去接收廣播
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter.setName("blueTestPhone"); //判斷藍(lán)牙是否打開(kāi) boolean originalBluetooth = (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()); if (originalBluetooth) { mBluetoothAdapter.startDiscovery(); } else if (originalBluetooth == false) { mBluetoothAdapter.enable(); }
藍(lán)牙打開(kāi)后,我們可以獲取設(shè)備的藍(lán)牙信息
StringBuilder sb = new StringBuilder(); //獲取本機(jī)藍(lán)牙名稱(chēng) String name = mBluetoothAdapter.getName(); //獲取本機(jī)藍(lán)牙地址 String address = mBluetoothAdapter.getAddress();
搜索完成后,通過(guò)BluetoothDevice.ACTION_FOUND廣播去接收結(jié)果,廣播代碼如下(注意:可能出現(xiàn)設(shè)備搜索不到的情況,設(shè)備需要開(kāi)啟允許周?chē)O(shè)備搜索,或者通過(guò)程序來(lái)控制允許搜索的時(shí)間范圍)
/*確保藍(lán)牙被發(fā)現(xiàn),在榮耀8手機(jī)上,設(shè)置了還是默認(rèn)的2分鐘,所以以下幾句代碼程序中沒(méi)有,*/ Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); //設(shè)置可見(jiàn)狀態(tài)的持續(xù)時(shí)間為300秒,但是最多是300秒 discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE_BLUETOOTH); private void initSearchBroadcast() { IntentFilter intentFilter = new IntentFilter(); //發(fā)現(xiàn)設(shè)備 intentFilter.addAction(BluetoothDevice.ACTION_FOUND); //設(shè)備配對(duì)狀態(tài)改變 intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //藍(lán)牙設(shè)備狀態(tài)改變 intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //開(kāi)始掃描 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //結(jié)束掃描 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //其它設(shè)備請(qǐng)求配對(duì) intentFilter.addAction(ACTION_PAIRING_REQUEST); //intentFilter.addAction(BluetoothAdapter.CONNECTION_STATE_CHANGED); registerReceiver(bluetoothReceiver, intentFilter); } private BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Logger.e(TAG + "mBluetoothReceiver action =" + action); try { if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {//開(kāi)始掃描 setProgressBarIndeterminateVisibility(true); log1.setText("正在掃描設(shè)備,請(qǐng)稍候..."); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//結(jié)束掃描 Logger.e(TAG + "設(shè)備搜索完畢"); setProgressBarIndeterminateVisibility(false); log1.setText("掃描完成"); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); scanStatus = false; } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {//發(fā)現(xiàn)設(shè)備 findDevice(intent); } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//藍(lán)牙配對(duì)狀態(tài)的廣播 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Logger.e(TAG + device.getName() + "藍(lán)牙配對(duì)廣播:" + device.getBondState()); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDING: Logger.e(TAG + device.getName() + "藍(lán)牙配對(duì)廣播 正在配對(duì)......"); break; case BluetoothDevice.BOND_BONDED: Logger.e(TAG + device.getName() + "藍(lán)牙配對(duì)廣播 完成配對(duì),本機(jī)自動(dòng)配對(duì)"); bondDevices.add(device); unbondDevices.remove(device); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); break; case BluetoothDevice.BOND_NONE: Logger.e(TAG + device.getName() + "藍(lán)牙配對(duì)廣播 取消配對(duì)"); unbondDevices.add(device); bondDevices.remove(device); unbondAdapter.notifyDataSetChanged(); bondAdapter.notifyDataSetChanged(); default: break; } } else if (action.equals(ACTION_PAIRING_REQUEST)) {//其它設(shè)備藍(lán)牙配對(duì)請(qǐng)求 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //當(dāng)前的配對(duì)的狀態(tài) try { String path = Environment.getExternalStorageDirectory() + "/blueTest/"; String deviceName = btDevice.getName(); Logger.e(TAG + "藍(lán)牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); //1.確認(rèn)配對(duì),高版本無(wú)效,藍(lán)牙配對(duì)不是zuk的問(wèn)題,而是安卓6.0的bug,凡是遇到藍(lán)牙適配問(wèn)題的,請(qǐng)同時(shí)打開(kāi)藍(lán)牙和定位,再去配對(duì),基本90%都沒(méi)有問(wèn)題了。 Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true); //2.終止有序廣播,如果沒(méi)有將廣播終止,則會(huì)出現(xiàn)一個(gè)一閃而過(guò)的配對(duì)框。 abortBroadcast(); //3.調(diào)用setPin方法進(jìn)行配對(duì)... boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show(); } } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {//藍(lán)牙開(kāi)關(guān)狀態(tài) // BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int statue = mBluetoothAdapter.getState(); switch (statue) { case BluetoothAdapter.STATE_OFF: Logger.e("藍(lán)牙狀態(tài):,藍(lán)牙關(guān)閉"); ClsUtils.closeDiscoverableTimeout(mBluetoothAdapter); break; case BluetoothAdapter.STATE_ON: Logger.e("藍(lán)牙狀態(tài):,藍(lán)牙打開(kāi)"); ClsUtils.setDiscoverableTimeout(1000 * 60, mBluetoothAdapter); scanBluetooth(); break; case BluetoothAdapter.STATE_TURNING_OFF: Logger.e("藍(lán)牙狀態(tài):,藍(lán)牙正在關(guān)閉"); mBluetoothAdapter.cancelDiscovery(); break; case BluetoothAdapter.STATE_TURNING_ON: Logger.e("藍(lán)牙狀態(tài):,藍(lán)牙正在打開(kāi)"); break; } } } catch (Exception e) { e.printStackTrace(); } } }; //發(fā)現(xiàn)設(shè)備的代碼如下 private void findDevice(Intent intent) throws Exception{ //獲取到設(shè)備對(duì)象 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String str = device.getName() + "|" + device.getAddress(); Logger.e("掃描到設(shè)備:" + str); if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//判斷當(dāng)前設(shè)備地址下的device是否已經(jīng)配對(duì) if (!bondDevices.contains(device)) { bondDevices.add(device); } } else { if (!unbondDevices.contains(device)) { unbondDevices.add(device); } if (device.getName().equals(TEST_DEVICE_NAME)) { boolean bondStatus = ClsUtils.createBond(device.getClass(), device); Logger.i(TAG + " bondStatus:" + bondStatus); } } Log.e("error", "搜索完畢,準(zhǔn)備刷新!"); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); }
四:藍(lán)牙配對(duì)
正常情況下,藍(lán)牙匹配需要彈出一個(gè)匹配確認(rèn)框,如下圖,但我想實(shí)現(xiàn)的是,匹配其中一方,不能手動(dòng)點(diǎn)擊配對(duì),因?yàn)榘l(fā)起藍(lán)牙連接的設(shè)備是android設(shè)備,是不能觸摸的,所以就要通過(guò)程序來(lái)解決這個(gè)問(wèn)題,特別聲明:(測(cè)試的android設(shè)備,版本為5.x,并且已經(jīng)root,沒(méi)有root的設(shè)備,或者不是android5.x不清楚能否實(shí)現(xiàn)自動(dòng)匹配,因?yàn)槲抑挥羞@個(gè)測(cè)試設(shè)備)。
1 當(dāng)我們搜索到目標(biāo)手機(jī)的藍(lán)牙后,android設(shè)備主動(dòng)發(fā)起連接請(qǐng)求,代碼如下
if (device.getName().equals(TEST_DEVICE_NAME)) { boolean bondStatus = ClsUtils.createBond(device.getClass(), device); Logger.i(TAG + " bondStatus:" + bondStatus); } //發(fā)起藍(lán)牙匹配請(qǐng)求 public boolean createBond(Class btClass, BluetoothDevice btDevice) throws Exception { Method createBondMethod = btClass.getMethod("createBond"); Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice); return returnValue.booleanValue(); }
2 當(dāng)被匹配方點(diǎn)擊配對(duì)后,系統(tǒng)會(huì)通過(guò)BluetoothDevice.ACTION_BOND_STATE_CHANGED廣播告訴android設(shè)備,此時(shí)android設(shè)備就可以自動(dòng)確認(rèn),通過(guò)這個(gè)流程來(lái)完成整個(gè)藍(lán)牙的配對(duì),具體代碼如下
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //當(dāng)前的配對(duì)的狀態(tài) try { String path = Environment.getExternalStorageDirectory() + "/blueTest/"; String deviceName = btDevice.getName(); Logger.e(TAG + "藍(lán)牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); if(deviceName.equals(TEST_DEVICE_NAME)){//TEST_DEVICE_NAME 為被匹配藍(lán)牙設(shè)備的名稱(chēng),自己手動(dòng)定義 Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true); abortBroadcast(); boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show(); } //確認(rèn)配對(duì) public Object setPairingConfirmation(Class<?> btClass, BluetoothDevice device, boolean isConfirm) throws Exception { Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class); Object object = setPairingConfirmation.invoke(device, isConfirm); return object; } //配對(duì)需要調(diào)用的方法 public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice, String str) throws Exception { try { Method removeBondMethod = btClass.getDeclaredMethod("setPin", new Class[] {byte[].class}); Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, new Object[] {str.getBytes()}); Log.e("returnValue", "" + returnValue); } catch (SecurityException e) { // throw new RuntimeException(e.getMessage()); e.printStackTrace(); } catch (IllegalArgumentException e) { // throw new RuntimeException(e.getMessage()); e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return true; }
到目前為止,藍(lán)牙權(quán)限,以及動(dòng)態(tài)權(quán)限,藍(lán)牙的打開(kāi),關(guān)閉,搜索,以及自動(dòng)配對(duì)(特別聲明:(自動(dòng)配對(duì)的android設(shè)備,版本為5.x,并且已經(jīng)root,沒(méi)有root的設(shè)備,或者不是android5.x不清楚能否實(shí)現(xiàn)自動(dòng)匹配,因?yàn)槲抑挥羞@個(gè)測(cè)試設(shè)備)。)代碼至此結(jié)束。
demo代碼下載:github
總結(jié)
以上所述是小編給大家介紹的Android藍(lán)牙通信之搜索藍(lán)牙設(shè)備,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android啟動(dòng)頁(yè)優(yōu)化之實(shí)現(xiàn)應(yīng)用秒開(kāi)
現(xiàn)在很多應(yīng)用都會(huì)在進(jìn)入主界面之前,添加一個(gè)啟動(dòng)頁(yè),然后加入幾秒鐘的廣告,我覺(jué)得這個(gè)不能算是 “真正意義上的 “ 啟動(dòng)頁(yè),應(yīng)該叫廣告頁(yè)。2021-05-05android數(shù)據(jù)存儲(chǔ)之文件存儲(chǔ)方法
本篇文章主要介紹了android數(shù)據(jù)存儲(chǔ)之文件存儲(chǔ)的方法,具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11詳解如何在Flutter中獲取設(shè)備標(biāo)識(shí)符
這篇文章主要為大家介紹了幾種通過(guò)Flutter讀取設(shè)備信息的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下2022-04-04Android實(shí)現(xiàn)下載m3u8視頻文件問(wèn)題解決
這篇文章主要介紹了Android實(shí)現(xiàn)下載m3u8視頻文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01Android 開(kāi)發(fā)之dataBinding與ListView及事件
這篇文章主要介紹了Android 開(kāi)發(fā)之dataBinding與ListView及事件的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10APP添加CNZZ統(tǒng)計(jì)插件教程 Android版添加phonegap
這篇文章主要介紹了APP添加CNZZ統(tǒng)計(jì)插件教程,Android版添加phonegap,感興趣的小伙伴們可以參考一下2015-12-12Android 使用registerReceiver注冊(cè)BroadcastReceiver案例詳解
這篇文章主要介紹了Android 使用registerReceiver注冊(cè)BroadcastReceiver案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08Android GPS室內(nèi)定位問(wèn)題的解決方法(location為null)
這篇文章主要為大家詳細(xì)介紹了Android GPS室內(nèi)定位問(wèn)題的解決方法,location為null,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02flutter FadeTransition實(shí)現(xiàn)透明度漸變動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了flutter FadeTransition實(shí)現(xiàn)透明度漸變動(dòng)畫(huà),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07