You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:04:55