Android藍牙通信之搜索藍牙設備
一:注意事項
1:android6.0使用藍牙時,需要開啟gps定位權限,不然無法搜索其它藍牙設備。
二:權限
1:權限配置
<!--允許程序連接到已配對的藍牙設備--> <uses-permission android:name="android.permission.BLUETOOTH" /> <!-- 允許程序發(fā)現(xiàn)和配對藍牙設備 --> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!--android 6.0 涉及到的權限--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 在SDCard中創(chuàng)建與刪除文件的權限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SDCard寫入數(shù)據(jù)的權限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2:動態(tài)權限代碼
由于需要用到存儲卡,定位等,android6.0以上需要代碼動態(tài)設置。
a)獲取定位設置
if (Build.VERSION.SDK_INT >= 23) {
boolean isLocat = isLocationOpen(getApplicationContext());
Toast.makeText(mContext, "isLo:" + isLocat, Toast.LENGTH_LONG).show();
//開啟位置服務,支持獲取ble藍牙掃描結(jié)果
if (!isLocat) {
Intent enableLocate = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(enableLocate, 1);
}
}
/**
* 判斷位置信息是否開啟
*
* @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)絡定位
boolean isNetWorkProvider = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
return isGpsProvider || isNetWorkProvider;
}
b)存儲卡權限設置
if (Build.VERSION.SDK_INT >= 23) {
int write = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
int read = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
//動態(tài)請求讀寫sd卡權限
if (write != PackageManager.PERMISSION_GRANTED || read != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, SD_CARD);
}
}
然后通過onRequestPermissionsResult()方法獲取動態(tài)權限的結(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){
//允許訪問
}else{
Toast.makeText(mContext,"您拒絕了程序訪問存儲卡",Toast.LENGTH_LONG).show();
}
break;
case COARES_LOCATION:
break;
}
}
三:藍牙搜索
android.bluetooth.BluetoothAdapter 是藍牙開發(fā)用得比較多,并且比較重要的一個類,可以設備藍牙名稱,打開,關閉,搜索等常規(guī)操作。
1 藍牙打開,以及搜索
藍牙打開和關閉信息使用BluetoothAdapter.ACTION_STATE_CHANGED去接收廣播
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.setName("blueTestPhone");
//判斷藍牙是否打開
boolean originalBluetooth = (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
if (originalBluetooth) {
mBluetoothAdapter.startDiscovery();
} else if (originalBluetooth == false) {
mBluetoothAdapter.enable();
}
藍牙打開后,我們可以獲取設備的藍牙信息
StringBuilder sb = new StringBuilder(); //獲取本機藍牙名稱 String name = mBluetoothAdapter.getName(); //獲取本機藍牙地址 String address = mBluetoothAdapter.getAddress();
搜索完成后,通過BluetoothDevice.ACTION_FOUND廣播去接收結(jié)果,廣播代碼如下(注意:可能出現(xiàn)設備搜索不到的情況,設備需要開啟允許周圍設備搜索,或者通過程序來控制允許搜索的時間范圍)

/*確保藍牙被發(fā)現(xiàn),在榮耀8手機上,設置了還是默認的2分鐘,所以以下幾句代碼程序中沒有,*/
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//設置可見狀態(tài)的持續(xù)時間為300秒,但是最多是300秒
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE_BLUETOOTH);
private void initSearchBroadcast() {
IntentFilter intentFilter = new IntentFilter();
//發(fā)現(xiàn)設備
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
//設備配對狀態(tài)改變
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//藍牙設備狀態(tài)改變
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
//開始掃描
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
//結(jié)束掃描
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
//其它設備請求配對
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)) {//開始掃描
setProgressBarIndeterminateVisibility(true);
log1.setText("正在掃描設備,請稍候...");
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//結(jié)束掃描
Logger.e(TAG + "設備搜索完畢");
setProgressBarIndeterminateVisibility(false);
log1.setText("掃描完成");
bondAdapter.notifyDataSetChanged();
unbondAdapter.notifyDataSetChanged();
scanStatus = false;
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {//發(fā)現(xiàn)設備
findDevice(intent);
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//藍牙配對狀態(tài)的廣播
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Logger.e(TAG + device.getName() + "藍牙配對廣播:" + device.getBondState());
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING:
Logger.e(TAG + device.getName() + "藍牙配對廣播 正在配對......");
break;
case BluetoothDevice.BOND_BONDED:
Logger.e(TAG + device.getName() + "藍牙配對廣播 完成配對,本機自動配對");
bondDevices.add(device);
unbondDevices.remove(device);
bondAdapter.notifyDataSetChanged();
unbondAdapter.notifyDataSetChanged();
break;
case BluetoothDevice.BOND_NONE:
Logger.e(TAG + device.getName() + "藍牙配對廣播 取消配對");
unbondDevices.add(device);
bondDevices.remove(device);
unbondAdapter.notifyDataSetChanged();
bondAdapter.notifyDataSetChanged();
default:
break;
}
} else if (action.equals(ACTION_PAIRING_REQUEST)) {//其它設備藍牙配對請求
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //當前的配對的狀態(tài)
try {
String path = Environment.getExternalStorageDirectory() + "/blueTest/";
String deviceName = btDevice.getName();
Logger.e(TAG + "藍牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state);
//1.確認配對,高版本無效,藍牙配對不是zuk的問題,而是安卓6.0的bug,凡是遇到藍牙適配問題的,請同時打開藍牙和定位,再去配對,基本90%都沒有問題了。
Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
//2.終止有序廣播,如果沒有將廣播終止,則會出現(xiàn)一個一閃而過的配對框。
abortBroadcast();
//3.調(diào)用setPin方法進行配對...
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)) {//藍牙開關狀態(tài)
// BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int statue = mBluetoothAdapter.getState();
switch (statue) {
case BluetoothAdapter.STATE_OFF:
Logger.e("藍牙狀態(tài):,藍牙關閉");
ClsUtils.closeDiscoverableTimeout(mBluetoothAdapter);
break;
case BluetoothAdapter.STATE_ON:
Logger.e("藍牙狀態(tài):,藍牙打開");
ClsUtils.setDiscoverableTimeout(1000 * 60, mBluetoothAdapter);
scanBluetooth();
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Logger.e("藍牙狀態(tài):,藍牙正在關閉");
mBluetoothAdapter.cancelDiscovery();
break;
case BluetoothAdapter.STATE_TURNING_ON:
Logger.e("藍牙狀態(tài):,藍牙正在打開");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
//發(fā)現(xiàn)設備的代碼如下
private void findDevice(Intent intent) throws Exception{
//獲取到設備對象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String str = device.getName() + "|" + device.getAddress();
Logger.e("掃描到設備:" + str);
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//判斷當前設備地址下的device是否已經(jīng)配對
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", "搜索完畢,準備刷新!");
bondAdapter.notifyDataSetChanged();
unbondAdapter.notifyDataSetChanged();
}
四:藍牙配對
正常情況下,藍牙匹配需要彈出一個匹配確認框,如下圖,但我想實現(xiàn)的是,匹配其中一方,不能手動點擊配對,因為發(fā)起藍牙連接的設備是android設備,是不能觸摸的,所以就要通過程序來解決這個問題,特別聲明:(測試的android設備,版本為5.x,并且已經(jīng)root,沒有root的設備,或者不是android5.x不清楚能否實現(xiàn)自動匹配,因為我只有這個測試設備)。

1 當我們搜索到目標手機的藍牙后,android設備主動發(fā)起連接請求,代碼如下
if (device.getName().equals(TEST_DEVICE_NAME)) {
boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
Logger.i(TAG + " bondStatus:" + bondStatus);
}
//發(fā)起藍牙匹配請求
public boolean createBond(Class btClass, BluetoothDevice btDevice)
throws Exception {
Method createBondMethod = btClass.getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
2 當被匹配方點擊配對后,系統(tǒng)會通過BluetoothDevice.ACTION_BOND_STATE_CHANGED廣播告訴android設備,此時android設備就可以自動確認,通過這個流程來完成整個藍牙的配對,具體代碼如下
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //當前的配對的狀態(tài)
try {
String path = Environment.getExternalStorageDirectory() + "/blueTest/";
String deviceName = btDevice.getName();
Logger.e(TAG + "藍牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state);
if(deviceName.equals(TEST_DEVICE_NAME)){//TEST_DEVICE_NAME 為被匹配藍牙設備的名稱,自己手動定義
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();
}
//確認配對
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;
}
//配對需要調(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;
}
到目前為止,藍牙權限,以及動態(tài)權限,藍牙的打開,關閉,搜索,以及自動配對(特別聲明:(自動配對的android設備,版本為5.x,并且已經(jīng)root,沒有root的設備,或者不是android5.x不清楚能否實現(xiàn)自動匹配,因為我只有這個測試設備)。)代碼至此結(jié)束。
demo代碼下載:github
總結(jié)
以上所述是小編給大家介紹的Android藍牙通信之搜索藍牙設備,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關文章
Android啟動頁優(yōu)化之實現(xiàn)應用秒開
現(xiàn)在很多應用都會在進入主界面之前,添加一個啟動頁,然后加入幾秒鐘的廣告,我覺得這個不能算是 “真正意義上的 “ 啟動頁,應該叫廣告頁。2021-05-05
Android 開發(fā)之dataBinding與ListView及事件
這篇文章主要介紹了Android 開發(fā)之dataBinding與ListView及事件的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-10-10
APP添加CNZZ統(tǒng)計插件教程 Android版添加phonegap
這篇文章主要介紹了APP添加CNZZ統(tǒng)計插件教程,Android版添加phonegap,感興趣的小伙伴們可以參考一下2015-12-12
Android 使用registerReceiver注冊BroadcastReceiver案例詳解
這篇文章主要介紹了Android 使用registerReceiver注冊BroadcastReceiver案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08
Android GPS室內(nèi)定位問題的解決方法(location為null)
這篇文章主要為大家詳細介紹了Android GPS室內(nèi)定位問題的解決方法,location為null,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02
flutter FadeTransition實現(xiàn)透明度漸變動畫
這篇文章主要為大家詳細介紹了flutter FadeTransition實現(xiàn)透明度漸變動畫,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07

