Android平台下BLE设备程序化配对的实现方案问询
Android BLE设备点击配对实现
我正在开发一个Android Activity,用于搜索BLE设备并在ListView中展示,目前已实现周边BLE设备的搜索功能。现在需要在用户点击ListView中的设备项时,编写代码实现该BLE设备的配对操作。以下是原Activity代码:
import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import it.eresult.silvermountain.databinding.ActivityFindDeviceBinding; import it.eresult.silvermountain.helpers.adapters.DeviceDiscoveredViewAdapter; import it.eresult.silvermountain.model.dataModel.Device; import it.eresult.silvermountain.model.dataModel.TypeDevice; public class FindDeviceActivity extends AppCompatActivity { private ActivityFindDeviceBinding binding; private BluetoothAdapter mBluetoothAdapter; private boolean mScanning; private Handler mHandler; private final static int REQUEST_ENABLE_BT = 1; TypeDevice deviceToFind; List<Device> listDeviceDiscovered; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityFindDeviceBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); listDeviceDiscovered = new ArrayList<Device>(); binding.listViewDeviceDiscovered.setAdapter(new DeviceDiscoveredViewAdapter(getApplicationContext(), listDeviceDiscovered)); binding.listViewDeviceDiscovered.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { Device deviceSelected = listDeviceDiscovered.get(position); Log.v("", deviceSelected.getNome()+" device selezionato per il pairing"); } }); deviceToFind = (TypeDevice) getIntent().getSerializableExtra("device"); if (ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission( this, Manifest.permission.BLUETOOTH_SCAN ) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String [] { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN },1 ); } int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION); if (permissionCheck != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String [] { android.Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN },1 ); } if(areLocationServicesEnabled(this)) { mHandler = new Handler(); if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "BLE Non supportato", Toast.LENGTH_SHORT).show(); finish(); } final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); scanLeDevice(true); } } public boolean areLocationServicesEnabled(Context context) { LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); try { return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); } catch (Exception e) { e.printStackTrace(); return false; } } private void scanLeDevice(final boolean enable) { if (enable) { mScanning = true; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { return; } mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { return; } mBluetoothAdapter.stopLeScan(mLeScanCallback); } } // Device scan callback. 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() { if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } if(device.getName()!=null && device.getName().startsWith(deviceToFind.getAbbreviazione())){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { Log.i("NUOVO DEVICE SCOPERTO",device.getName() + " " +device.getAddress()+" "+device.getAlias()+" "+device.getType()); } //devo mostrare in pagina il nuovo device. addDeviceDiscovered(new Device(device.getName(), device.getAddress())); binding.listViewDeviceDiscovered.setAdapter(new DeviceDiscoveredViewAdapter(getApplicationContext(), listDeviceDiscovered)); } } }); } }; private void addDeviceDiscovered(Device newDevice){ try{ if(listDeviceDiscovered==null) listDeviceDiscovered = new ArrayList<Device>(); boolean find = false; for (Device d: listDeviceDiscovered) { if(d.getMacAddress().equalsIgnoreCase(newDevice.getMacAddress())){ find=true; break; } } if(!find) listDeviceDiscovered.add(newDevice); }catch(Exception e){ Log.e("",e.getMessage()); } } }
修改点击监听实现配对
以下是修改后的ListView点击监听方法,添加配对逻辑:
binding.listViewDeviceDiscovered.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { Device deviceSelected = listDeviceDiscovered.get(position); Log.v("", deviceSelected.getNome() + " device selezionato per il pairing"); // 停止BLE扫描,避免干扰配对流程 scanLeDevice(false); // 检查蓝牙连接权限 if (ActivityCompat.checkSelfPermission(FindDeviceActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { Toast.makeText(FindDeviceActivity.this, "缺少蓝牙连接权限", Toast.LENGTH_SHORT).show(); return; } // 通过MAC地址获取BluetoothDevice实例 BluetoothDevice bluetoothDevice = mBluetoothAdapter.getRemoteDevice(deviceSelected.getMacAddress()); // 调用反射触发配对 try { java.lang.reflect.Method createBondMethod = BluetoothDevice.class.getMethod("createBond"); boolean isInitiated = (boolean) createBondMethod.invoke(bluetoothDevice); if (isInitiated) { Toast.makeText(FindDeviceActivity.this, "配对请求已发起,请在系统弹窗确认", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(FindDeviceActivity.this, "配对请求发起失败", Toast.LENGTH_SHORT).show(); } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); Toast.makeText(FindDeviceActivity.this, "当前设备不支持该配对方式", Toast.LENGTH_SHORT).show(); } } });
添加配对状态监听广播
为了监听配对的成功/失败状态,需要注册广播接收器:
1. 在onCreate中注册广播
在onCreate方法的末尾添加以下代码:
// 注册配对状态变化广播接收器 IntentFilter bondFilter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(mBondStateReceiver, bondFilter);
2. 实现广播接收器
在Activity类中添加广播接收器的实现:
private BroadcastReceiver mBondStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int currentBondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR); switch (currentBondState) { case BluetoothDevice.BOND_BONDING: Log.d("配对状态", device.getName() + " 正在配对"); Toast.makeText(context, "正在配对...", Toast.LENGTH_SHORT).show(); break; case BluetoothDevice.BOND_BONDED: Log.d("配对状态", device.getName() + " 配对成功"); Toast.makeText(context, "配对成功!", Toast.LENGTH_SHORT).show(); // 这里可以添加配对成功后的逻辑,比如跳转至设备连接页面 break; case BluetoothDevice.BOND_NONE: if (previousBondState == BluetoothDevice.BOND_BONDING) { Log.d("配对状态", device.getName() + " 配对取消或失败"); Toast.makeText(context, "配对失败,请重试", Toast.LENGTH_SHORT).show(); } break; } } } };
3. 在onDestroy中注销广播
在Activity销毁时记得注销广播接收器,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); scanLeDevice(false); if (mBondStateReceiver != null) { unregisterReceiver(mBondStateReceiver); } }
注意事项
- 权限检查:确保已经申请并获取
BLUETOOTH_CONNECT权限(Android 12及以上版本必需),原代码中的权限申请逻辑已包含,但需确保用户授权。 - 系统配对弹窗:配对操作会触发系统级的确认弹窗,若设备有配对码,用户需手动输入确认。
- 反射兼容性:
createBond()是系统隐藏方法,部分厂商定制ROM可能存在兼容性问题,需多设备测试。 - 停止扫描:配对前停止BLE扫描可减少系统资源占用,避免干扰配对流程。
内容的提问来源于stack exchange,提问作者bircastri
相关产品推荐
相关产品推荐

