最近项目需求要做蓝牙自动配对,也就是在首次配对的时候跳过用户输入PIN码。网上有很多分享的如何实现自动配对。非常感谢大牛们的无私分享。。。 以下做一个记录,方便以后查阅。 注意: 蓝牙开启需要开启蓝牙的相关权限还有 定位 权限
在AndroidManifest.xml中添加
并且开启运行时权限;
private void initPermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {
//判断是否需要向用户解释为何要此权限
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.READ_CONTACTS)) {
showMessageOKCancel("你必须允许这个权限,否则无法搜索到BLE设备", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},100);
}
});
return;
}
//请求权限
requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS},100);
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", okListener)
.create()
.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 100) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1
//这里进行授权被允许的处理
scanLeDevice(true);
Toast.makeText(MainActivity.this, "谢谢!", Toast.LENGTH_SHORT).show();
} else {
//这里进行权限被拒绝的处理,就跳转到本应用的程序管理器
Toast.makeText(MainActivity.this, "请开启位置权限", Toast.LENGTH_SHORT).show();
Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
String pkg = "com.android.settings";
String cls = "com.android.settings.applications.InstalledAppDetails";
i.setComponent(new ComponentName(pkg, cls));
i.setData(Uri.parse("package:" + getPackageName()));
startActivity(i);
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
/** * 两种获取BluetoothAdapter方式 **/ //方式一:通过BluetoothManager获取 BluetoothManager mBm = (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = mBm.getAdapter(); //方式二:通过getDefaultAdapter()获取 //mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null) { LogUtils.e("没有蓝牙模块"); ToastUtils.showShort("没有蓝牙模块"); return; }
/** * 打开蓝牙的两种方式 */ //一:直接打开,不通过用户 // if (!mBluetoothAdapter.isEnabled()) { // mBluetoothAdapter.enable(); // LogUtils.e("开启蓝牙~"); // } //二:优雅的打开 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enabIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabIntent, 100); LogUtils.e("开启蓝牙~");
}
private Handler mHandler = new Handler(); //15秒搜索时间 private static final long SCAN_PERIOD = 15000; private void scanLeDevice(final boolean enable) { mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索 mHandler.postDelayed(new Runnable() { @Override public void run() { mBluetoothAdapter.stopLeScan(mLeScanCallback); } }, SCAN_PERIOD); } private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { //在这里可以把搜索到的设备保存起来 //device.getName();获取蓝牙设备名字 //device.getAddress();获取蓝牙设备mac地址 //这里的rssi即信号强度,即手机与设备之间的信号强度。 list.add(device); mBleAdapter.setNewData(list); LogUtils.e("name:" + device.getName()); } }); } };
mBleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { //ToastUtils.showShort("点击了:" + position); //这里只需要createBond就行了 BluetoothDevice device = list.get(position); try { //创建createBond ClsUtils.createBond(device.getClass(), device); } catch (Exception e) { e.printStackTrace(); } //建立蓝牙连接 mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback); } });
public class BluetoothConnectReceiver extends BroadcastReceiver {
String strPsw = "111111";
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
try {
/**
* cancelPairingUserInput()取消用户输入密钥框,
* 个人觉得一般情况下不要和setPin(setPasskey、setPairingConfirmation、
* setRemoteOutOfBandData)一起用,
* 这几个方法都会remove掉map里面的key:value(<<<<<也就是互斥的>>>>>>)。
*/
//1.确认配对
//ClsUtils.setPairingConfirmation(device.getClass(), device, true);
ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
//ClsUtils.setPasskey(device.getClass(), device, strPsw);
//ClsUtils.cancelPairingUserInput(device.getClass(), device); //一般调用不成功,前言里面讲解过了
ToastUtils.showShort("配对信息===>>>>好像成功了~");
abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
} catch (Exception e) {
LogUtils.e("反射异常:"+e);
// TODO Auto-generated catch block
ToastUtils.showShort("请求连接错误");
}
}
}
}
}
android:name=".receiver.BluetoothConnectReceiver" >
android:priority="1000">
android:name="android.bluetooth.device.action.PAIRING_REQUEST"/>
android:name="android.bluetooth.device.action.FOUND" />
public class MainActivity extends AppCompatActivity { @BindView(R.id.list_view) RecyclerView listView; BleAdapter mBleAdapter; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; private BluetoothAdapter mBluetoothAdapter; Listlist; private BluetoothGatt mBluetoothGatt; private BluetoothGattCharacteristic mCharacteristic; private BluetoothGattCharacteristic rssiCharacteristic; @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // View注入 ButterKnife.bind(this); initPermission(); initView(); init(); // 注册Receiver来获取蓝牙设备相关的结果 IntentFilter intent = new IntentFilter(); intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果 intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(searchDevices, intent); } private void initView() { list = new ArrayList<>(); mBleAdapter = new BleAdapter(R.layout.item_ble_view, list); // 设置布局管理器 LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); listView.setLayoutManager(linearLayoutManager); listView.setAdapter(mBleAdapter); mBleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { //ToastUtils.showShort("点击了:" + position); //这里只需要createBond就行了 BluetoothDevice device = list.get(position); try { //创建createBond ClsUtils.createBond(device.getClass(), device); } catch (Exception e) { e.printStackTrace(); } //建立蓝牙连接 mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback); } }); //下拉刷新 swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { //停止刷新 swipeRefreshLayout.setRefreshing(false); list.clear(); } }); } private void init() { /** * 两种获取BluetoothAdapter方式 **/ //方式一:通过BluetoothManager获取 BluetoothManager mBm = (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = mBm.getAdapter(); //方式二:通过getDefaultAdapter()获取 //mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null) { LogUtils.e("没有蓝牙模块"); ToastUtils.showShort("没有蓝牙模块"); return; } /** * 打开蓝牙的两种方式 */ //一:直接打开,不通过用户 // if (!mBluetoothAdapter.isEnabled()) { // mBluetoothAdapter.enable(); // LogUtils.e("开启蓝牙~"); // } //二:优雅的打开 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enabIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabIntent, 100); LogUtils.e("开启蓝牙~"); } } private boolean mScanning;//是否正在搜索 private Handler mHandler = new Handler(); //15秒搜索时间 private static final long SCAN_PERIOD = 15000; private void scanLeDevice(final boolean enable) { mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索 mHandler.postDelayed(new Runnable() { @Override public void run() { mBluetoothAdapter.stopLeScan(mLeScanCallback); } }, SCAN_PERIOD); } private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { //在这里可以把搜索到的设备保存起来 //device.getName();获取蓝牙设备名字 //device.getAddress();获取蓝牙设备mac地址 //这里的rssi即信号强度,即手机与设备之间的信号强度。 list.add(device); mBleAdapter.setNewData(list); LogUtils.e("name:" + device.getName()); } }); } }; @Override protected void onResume() { list.clear(); scanLeDevice(true); super.onResume(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100) { Toast.makeText(this, "蓝牙已启用", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "蓝牙未启用", Toast.LENGTH_SHORT).show(); } } @RequiresApi(api = Build.VERSION_CODES.M) private void initPermission() { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { //判断是否需要向用户解释为何要此权限 if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.READ_CONTACTS)) { showMessageOKCancel("你必须允许这个权限,否则无法搜索到BLE设备", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 100); } }); return; } //请求权限 requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS}, 100); } } private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(MainActivity.this) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", okListener) .create() .show(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == 100) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1 //这里进行授权被允许的处理 //可以弹个Toast,感谢用户爸爸允许了。 scanLeDevice(true); Toast.makeText(MainActivity.this, "谢谢!", Toast.LENGTH_SHORT).show(); } else { //这里进行权限被拒绝的处理,就跳转到本应用的程序管理器 Toast.makeText(MainActivity.this, "请开启位置权限", Toast.LENGTH_SHORT).show(); Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS"); String pkg = "com.android.settings"; String cls = "com.android.settings.applications.InstalledAppDetails"; i.setComponent(new ComponentName(pkg, cls)); i.setData(Uri.parse("package:" + getPackageName())); startActivity(i); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } /** * gatt连接结果的返回 */ private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (status == BluetoothProfile.STATE_DISCONNECTED) { //蓝牙连接 LogUtils.e("onConnectionStateChange" + "连接成功"); ToastUtils.showShort("连接成功"); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); } /** * Callback triggered as a result of a remote characteristic notification. * * @param gatt * @param characteristic */ @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { System.out.println("onCharacteristicChanged"); LogUtils.e("onCharacteristicChanged"); } /** * 写入数据时操作 * @param gatt * @param characteristic * @param status */ @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { LogUtils.e("onCharacteristicWrite"); } /** * 读取返回值时操作 * @param gatt * @param characteristic * @param status */ @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { LogUtils.e("onCharacteristicRead"); } /** * Callback indicating the result of a descriptor write operation. * * @param gatt * @param descriptor * @param status */ @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { LogUtils.e("onDescriptorWrite"); } }; /** * 搜索BroadcastReceiver */ private final BroadcastReceiver searchDevices = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Bundle b = intent.getExtras(); Object[] lstName = b.keySet().toArray(); // 显示所有收到的消息及其细节 for (int i = 0; i < lstName.length; i++) { String keyName = lstName.toString(); LogUtils.e(keyName, String.valueOf(b.get(keyName))); } BluetoothDevice device = null; // 搜索设备时,取得设备的MAC地址 if (BluetoothDevice.ACTION_FOUND.equals(action)) { device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() == BluetoothDevice.BOND_NONE) { String str = " 未配对|" + device.getName() + "|" + device.getAddress(); LogUtils.e(str); // if (lstDevices.indexOf(str) == -1)// 防止重复添加 // lstDevices.add(str); // 获取设备名称和mac地址 // adtDevices.notifyDataSetChanged(); } } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDING: LogUtils.e("BlueToothTestActivity", "正在配对......"); break; case BluetoothDevice.BOND_BONDED: LogUtils.e("BlueToothTestActivity", "完成配对"); //connect(device);//连接设备 break; case BluetoothDevice.BOND_NONE: LogUtils.e("BlueToothTestActivity", "取消配对"); default: break; } } } }; }
public class ClsUtils { static BluetoothDevice remoteDevice; /** * 与设备配对 参考源码:platform/packages/apps/Settings.git * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java */ static public boolean createBond(Class btClass, BluetoothDevice btDevice) throws Exception { Method createBondMethod = btClass.getMethod("createBond"); Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice); return returnValue.booleanValue(); } /** * 与设备解除配对 参考源码:platform/packages/apps/Settings.git * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java */ static public boolean removeBond(Class btClass, BluetoothDevice btDevice) throws Exception { Method removeBondMethod = btClass.getMethod("removeBond"); Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice); return returnValue.booleanValue(); } static public boolean setPin(Class 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; } static public boolean setPasskey (Class btClass, BluetoothDevice btDevice,String str) throws Exception { try { Method removeBondMethod = btClass.getDeclaredMethod("setPasskey", 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; } // 取消用户输入 static public boolean cancelPairingUserInput(Class btClass,BluetoothDevice device)throws Exception { Method createBondMethod = btClass.getMethod("cancelPairingUserInput"); // cancelBondProcess() Boolean returnValue = (Boolean) createBondMethod.invoke(device); return returnValue.booleanValue(); } // 取消配对 static public boolean cancelBondProcess(Class btClass, BluetoothDevice device)throws Exception { Method createBondMethod = btClass.getMethod("cancelBondProcess"); Boolean returnValue = (Boolean) createBondMethod.invoke(device); return returnValue.booleanValue(); } //确认配对 static public void setPairingConfirmation(Class> btClass, BluetoothDevice device, boolean isConfirm) throws Exception { Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class); setPairingConfirmation.invoke(device, isConfirm); } /** * @param clsShow */ static public void printAllInform(Class clsShow) { try { // 取得所有方法 Method[] hideMethod = clsShow.getMethods(); int i = 0; for (; i < hideMethod.length; i++) { Log.e("method name", hideMethod[i].getName() + ";and the i is:" + i); } // 取得所有常量 Field[] allFields = clsShow.getFields(); for (i = 0; i < allFields.length; i++) { Log.e("Field name", allFields[i].getName()); } } 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(); } } // 执行时直接使用: public static boolean pair(String strAddr, String strPsw) { boolean result = false; BluetoothAdapter bluetoothAdapter = BluetoothAdapter .getDefaultAdapter(); bluetoothAdapter.cancelDiscovery(); if (!bluetoothAdapter.isEnabled()) { bluetoothAdapter.enable(); } if (!BluetoothAdapter.checkBluetoothAddress(strAddr)) { // 检查蓝牙地址是否有效 Log.d("mylog", "devAdd un effient!"); } BluetoothDevice device = bluetoothAdapter.getRemoteDevice(strAddr); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { try { Log.d("mylog", "NOT BOND_BONDED"); ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对 ClsUtils.createBond(device.getClass(), device); remoteDevice = device; // 配对完毕就把这个设备对象传给全局的remoteDevice result = true; } catch (Exception e) { // TODO Auto-generated catch block Log.d("mylog", "setPiN failed!"); e.printStackTrace(); } // } else { Log.d("mylog", "HAS BOND_BONDED"); try { ClsUtils.createBond(device.getClass(), device); ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对 ClsUtils.createBond(device.getClass(), device); remoteDevice = device; // 如果绑定成功,就直接把这个设备对象传给全局的remoteDevice result = true; } catch (Exception e) { // TODO Auto-generated catch block Log.d("mylog", "setPiN failed!"); e.printStackTrace(); } } return result; } }
以上在红米not4x 、 华为荣耀8 、 华为荣耀青春版(不知道具体啥型号)、小米5、vivoX20、华为p9、华为荣耀9、华为Mate 10、小米5x、红米3X、红米not5A测试OK。听说在三星上不行,到时候找个测试下。
写的不好或者有错误的地方,请留言提出。谢谢~