Android 连接BLE设备

该代码示例展示了如何在Android应用中使用FastBle库初始化BLE,扫描设备,建立连接,读取服务和特性,以及处理写入和通知回调。它还包含了权限检查和连接状态管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

依赖FastBle开源库
implementation ‘com.github.Jasonchenlijian:FastBle:2.4.0’

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_OPEN_GPS = 1;
    private static final int REQUEST_CODE_PERMISSION_LOCATION = 2;

    private DeviceAdapter mDeviceAdapter;
    private BleDevice bleDevice;
    private String serviceUUID, notifyUUID, writeUUID;

    int a = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        BleInstance.getInstance().initBle();

        Button btn_scan = (Button) findViewById(R.id.btn_scan);
        btn_scan.setText("开始扫描");
        btn_scan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (btn_scan.getText().equals("开始扫描")) {
                    checkPermissions();
                } else if (btn_scan.getText().equals("停止扫描")) {
                    BleManager.getInstance().cancelScan();
                }
            }
        });

        Button btn_check = (Button) findViewById(R.id.btn_check);
        btn_check.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                BleInstance.getInstance().bleTestConnect(bleDevice,serviceUUID,writeUUID);
//                BleInstance.getInstance().heartbeat(bleDevice,serviceUUID,writeUUID);
//                if (a == 1){
//                    a = 2;
//                    BleInstance.getInstance().timeBack(bleDevice,serviceUUID,writeUUID);
//                } else {
//                    a = 1;
//                    BleInstance.getInstance().cancelTimeBack(bleDevice,serviceUUID,writeUUID);
//                }
                BleInstance.getInstance().getBleData(bleDevice, serviceUUID, writeUUID);
            }
        });

        mDeviceAdapter = new DeviceAdapter(this);
        mDeviceAdapter.setOnDeviceClickListener(new DeviceAdapter.OnDeviceClickListener() {
            @Override
            public void onConnect(BleDevice bleDevice) {
                if (!BleManager.getInstance().isConnected(bleDevice)) {
                    BleManager.getInstance().cancelScan();
                    connect(bleDevice);
                }
            }

            @Override
            public void onDisConnect(final BleDevice bleDevice) {
                if (BleManager.getInstance().isConnected(bleDevice)) {
                    BleManager.getInstance().disconnect(bleDevice);
                }
            }

            @Override
            public void onDetail(BleDevice bleDevice) {
                BleInstance.getInstance().heartbeat(bleDevice, serviceUUID, writeUUID);
//                if (BleManager.getInstance().isConnected(bleDevice)) {
//                    Intent intent = new Intent(MainActivity.this, OperationActivity.class);
//                    intent.putExtra(OperationActivity.KEY_DATA, bleDevice);
//                    startActivity(intent);
//                }
            }
        });
        ListView listView_device = (ListView) findViewById(R.id.list_device);
        listView_device.setAdapter(mDeviceAdapter);
    }

    private void connect(final BleDevice bleDevice) {
        BleManager.getInstance().connect(bleDevice, gattCallback);
    }

    /**
     * 连接回调 获取特性值信息
     */
    private BleGattCallback gattCallback = new BleGattCallback() {
        @Override
        public void onStartConnect() {
//                progressDialog.show();
        }

        @Override
        public void onConnectFail(BleDevice bleDevice, BleException exception) {
//                img_loading.clearAnimation();
//                img_loading.setVisibility(View.INVISIBLE);
//                btn_scan.setText(getString(R.string.start_scan));
//                progressDialog.dismiss();
            Toast.makeText(MainActivity.this, getString(R.string.connect_fail), Toast.LENGTH_LONG).show();
        }

        @Override
        public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
//                progressDialog.dismiss();
            mDeviceAdapter.addDevice(bleDevice);
            mDeviceAdapter.notifyDataSetChanged();
            Toast.makeText(MainActivity.this, getString(R.string.connect_success), Toast.LENGTH_LONG).show();

            for (int i = 0; i < gatt.getServices().size(); i++) {
                BluetoothGattService service = gatt.getServices().get(i);
                for (int j = 0; j < service.getCharacteristics().size(); j++) {
                    BluetoothGattCharacteristic characteristic = service.getCharacteristics().get(j);
                    Log.i("haha", "======characteristic.getProperties()==========>" + characteristic.getProperties());
                    int charaProp = characteristic.getProperties();
                    if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
                        writeUUID = characteristic.getUuid().toString();
                        Log.i("haha", "======writeUUID==========>" + writeUUID);
                    }
                    if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                        Log.i("haha", "======service==========>" + service.getUuid());
                        Log.i("haha", "======characteristic==========>" + characteristic.getUuid());
                        MainActivity.this.bleDevice = bleDevice;
                        serviceUUID = service.getUuid().toString();
                        notifyUUID = characteristic.getUuid().toString();
                        //启用通知
                        BleManager.getInstance().notify(bleDevice, serviceUUID, notifyUUID, BleInstance.getInstance().notifyCallback);
                        //验证连接状态
                        new Handler().postDelayed(() -> BleInstance.getInstance().bleTestConnect(bleDevice, serviceUUID, writeUUID), 200);
                    }
                }
            }
        }

        @Override
        public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
//                progressDialog.dismiss();

            mDeviceAdapter.removeDevice(bleDevice);
            mDeviceAdapter.notifyDataSetChanged();

            if (isActiveDisConnected) {
                Toast.makeText(MainActivity.this, getString(R.string.active_disconnected), Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(MainActivity.this, getString(R.string.disconnected), Toast.LENGTH_LONG).show();
//                    ObserverManager.getInstance().notifyObserver(bleDevice);
            }
        }
    };

    private void checkPermissions() {
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (!bluetoothAdapter.isEnabled()) {
            Toast.makeText(this, "请先打开蓝牙", Toast.LENGTH_LONG).show();
            return;
        }
        String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
        List<String> permissionDeniedList = new ArrayList<>();
        for (String permission : permissions) {
            int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
            if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
                onPermissionGranted(permission);
            } else {
                permissionDeniedList.add(permission);
            }
        }
        if (!permissionDeniedList.isEmpty()) {
            String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
            ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);
        }
    }

    @Override
    public final void onRequestPermissionsResult(int requestCode,
                                                 @NonNull String[] permissions,
                                                 @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_CODE_PERMISSION_LOCATION:
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                            onPermissionGranted(permissions[i]);
                        }
                    }
                }
                break;
        }
    }

    private void onPermissionGranted(String permission) {
        switch (permission) {
            case Manifest.permission.ACCESS_FINE_LOCATION:
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) {
                    new AlertDialog.Builder(this)
                            .setTitle(R.string.notifyTitle)
                            .setMessage(R.string.gpsNotifyMsg)
                            .setNegativeButton(R.string.cancel,
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            finish();
                                        }
                                    })
                            .setPositiveButton(R.string.setting,
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                                            startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);
                                        }
                                    })

                            .setCancelable(false)
                            .show();
                } else {
                    startScan();
                }
                break;
        }
    }

    private boolean checkGPSIsOpen() {
        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        if (locationManager == null)
            return false;
        return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_OPEN_GPS) {
            if (checkGPSIsOpen()) {
                startScan();
            }
        }
    }

    private void startScan() {
        BleInstance.getInstance().setScanRule();
        BleInstance.getInstance().startScan(scanCallback);
    }

    private BleScanCallback scanCallback = new BleScanCallback() {
        @Override
        public void onScanStarted(boolean success) {
            mDeviceAdapter.clearScanDevice();
            mDeviceAdapter.notifyDataSetChanged();
//                img_loading.startAnimation(operatingAnim);
//                img_loading.setVisibility(View.VISIBLE);
//                btn_scan.setText(getString(R.string.stop_scan));
            Log.i("lala", "=onScanStarted====>" + success);
        }

        @Override
        public void onLeScan(BleDevice bleDevice) {
            super.onLeScan(bleDevice);
//            Log.i("lala", "=onLeScan==Name====>" + bleDevice.getName());
        }

        @Override
        public void onScanning(BleDevice bleDevice) {
            mDeviceAdapter.addDevice(bleDevice);
            mDeviceAdapter.notifyDataSetChanged();
            Log.e("lala", "=onScanning==Name====>" + bleDevice.getName());
        }

        @Override
        public void onScanFinished(List<BleDevice> scanResultList) {
//                img_loading.clearAnimation();
//                img_loading.setVisibility(View.INVISIBLE);
//                btn_scan.setText(getString(R.string.start_scan));
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        BleInstance.getInstance().closeBle();
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/rl_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <Button
            android:id="@+id/btn_scan"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开始扫描" />

        <Button
            android:id="@+id/btn_check"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="测试按钮" />

        <ImageView
            android:id="@+id/img_loading"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:src="@mipmap/ic_loading"
            android:visibility="invisible" />

    </RelativeLayout>

    <ListView
        android:id="@+id/list_device"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:divider="@android:color/darker_gray"
        android:dividerHeight="0.5dp"
        android:paddingStart="5dp"
        android:paddingEnd="5dp"
        android:scrollbars="none"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/rl_title"/>

</androidx.constraintlayout.widget.ConstraintLayout>

BleInstance.java

public class BleInstance {

    private List<Byte> mBytes = new ArrayList<>();

    public static BleInstance getInstance() {
        return BleInstance.BleManagerHolder.sBleManager;
    }

    private static class BleManagerHolder {
        private static final BleInstance sBleManager = new BleInstance();
    }

    public void initBle() {
        BleManager.getInstance().init(App.getInstance());
        BleManager.getInstance()
                .enableLog(true)
                .setReConnectCount(1, 5000)
                .setConnectOverTime(20000)
                .setOperateTimeout(5000);
    }

    public void closeBle() {
        BleManager.getInstance().disconnectAllDevice();
        BleManager.getInstance().destroy();
    }

    public void setScanRule() {
//        String[] uuids;
//        String str_uuid = et_uuid.getText().toString();
//        if (TextUtils.isEmpty(str_uuid)) {
//            uuids = null;
//        } else {
//            uuids = str_uuid.split(",");
//        }
//        UUID[] serviceUuids = null;
//        if (uuids != null && uuids.length > 0) {
//            serviceUuids = new UUID[uuids.length];
//            for (int i = 0; i < uuids.length; i++) {
//                String name = uuids[i];
//                String[] components = name.split("-");
//                if (components.length != 5) {
//                    serviceUuids[i] = null;
//                } else {
//                    serviceUuids[i] = UUID.fromString(uuids[i]);
//                }
//            }
//        }
//
//        String[] names;
//        String str_name = et_name.getText().toString();
//        if (TextUtils.isEmpty(str_name)) {
//            names = null;
//        } else {
//            names = str_name.split(",");
//        }
//
//        String mac = et_mac.getText().toString();
//
//        boolean isAutoConnect = sw_auto.isChecked();

        BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
//                .setServiceUuids(serviceUuids)      // 只扫描指定的服务的设备,可选
                .setDeviceName(true, "AIXQ")   // 只扫描指定广播名的设备,可选
//                .setDeviceMac(mac)                  // 只扫描指定mac的设备,可选
//                .setAutoConnect(isAutoConnect)      // 连接时的autoConnect参数,可选,默认false
//                .setScanTimeOut(10000)              // 扫描超时时间,可选,默认10秒
                .build();
        BleManager.getInstance().initScanRule(scanRuleConfig);
    }

    public void startScan(BleScanCallback scanCallback) {
        BleManager.getInstance().scan(scanCallback);
    }

    /**
     * 写数据
     *
     * @param bleDevice
     * @param serviceUUID
     * @param writeUUID
     * @param data
     */
    public void writeData(BleDevice bleDevice, String serviceUUID, String writeUUID, byte[] data) {
        BleManager.getInstance().write(bleDevice, serviceUUID, writeUUID, data, writeCallback);
    }

    /**
     * ble 写数据回调
     */
    private BleWriteCallback writeCallback = new BleWriteCallback() {
        @Override
        public void onWriteSuccess(int current, int total, byte[] justWrite) {
            Log.i("haha", "======onWriteSuccess==========>" + Arrays.toString(justWrite));
        }

        @Override
        public void onWriteFailure(BleException exception) {
            Log.i("haha", "======onWriteFailure==========>" + exception.getDescription());
        }
    };

    /**
     * ble 设置通知回调
     */
    public BleNotifyCallback notifyCallback = new BleNotifyCallback() {
        @Override
        public void onNotifySuccess() {
            Log.i("haha", "======onNotifySuccess==========>");
        }

        @Override
        public void onNotifyFailure(BleException exception) {
            Log.i("haha", "======onNotifyFailure==========>");
        }

        @Override
        public void onCharacteristicChanged(byte[] data) {
            String ble = ByteUtil.hexBytesToString(data).trim();
            Log.i("haha", "======onCharacteristicChanged==========>" + ble);
            receiveData(data);
        }
    };

    /**
     * 解析 ble data
     *
     * @param data
     */
    private void receiveData(byte[] data) {
        if (data.length >= 2 && data[0] == (byte) 0x5A && data[1] == (byte) 0xA5) {
            mBytes.clear();
        }
        mBytes.addAll(Bytes.asList(data));
        Log.i("haha", "======mBytes==========>" + mBytes.toString());
        if (mBytes.size() >= 7 && mBytes.get(2) + mBytes.get(3) * 256 == mBytes.size() - 5 && mBytes.get(mBytes.size() - 1) == (byte) 0x0D) {
            if (mBytes.get(4) == (byte) 0x45) {//接收特定的指令号
                if (judgeByte(mBytes)) {
                    analysisBleData(mBytes);
                    mBytes.clear();
                } else {
                    mBytes.clear();
                }
            }
        } else if (mBytes.size() >= 4 && mBytes.get(2) + mBytes.get(3) * 256 > mBytes.size() - 5) {//没接收完
//            "心跳没接收完".loge()
        } else if (mBytes.size() < 4) {//没接收完
//            "心跳没接收完".loge()
        } else {
//            "心跳接收:数据长度不匹配,${bytes2HexString(mBytes.toByteArray())}".loge()
            mBytes.clear();
        }
    }

    /**
     * 判断是否符合校验
     * 和校验、与校验
     * @param data
     * @return
     */
    private boolean judgeByte(List<Byte> data) {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        byte xor = data.get(0);
        byte and = data.get(0);
        try {
            for (int i = 1; i < data.size() - 3; i++) {
                xor = (byte) (xor ^ data.get(i));
                and = (byte) (and + data.get(i));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        if (data.get(data.size() - 3) == xor && data.get(data.size() - 2) == and) {
            return true;
        }
        return false;
    }

    /**
     * 包装要发送的数据
     * @param num
     * @param valueByteList
     * @return
     */
    private byte[] build(int num, List<Byte> valueByteList) {
        int payloadLength = 0;
        if (valueByteList != null && valueByteList.size() > 0) {
            payloadLength = valueByteList.size();
        }
        int length = payloadLength + 2 + 2 + 2 + 1;
        byte[] typeArray = new byte[length];
        int indexOfTypeArray = 0;
        typeArray[indexOfTypeArray++] = 0x5A;
        typeArray[indexOfTypeArray++] = (byte) 0xA5;
        int low = (length - 4) % 256;
        int high = (length - 4) / 256;
        typeArray[indexOfTypeArray++] = (byte) low;
        typeArray[indexOfTypeArray++] = (byte) high;
        typeArray[indexOfTypeArray++] = (byte) num;
        if (valueByteList != null && valueByteList.size() > 0) {
            for (int i = 0; i < valueByteList.size(); i++) {
                typeArray[indexOfTypeArray++] = valueByteList.get(i);
            }
        }
        byte xor = typeArray[0];
        byte and = typeArray[0];
        for (int i = 1; i < length - 2; i++) {
            xor = (byte) (xor ^ typeArray[i]);
            and = (byte) (and + typeArray[i]);
        }
        typeArray[indexOfTypeArray++] = xor;
        typeArray[indexOfTypeArray++] = and;
        return typeArray;
    }

    /**
     * 测试ble 连接 (设备“嘀”声响)
     *
     * @param bleDevice
     * @param serviceUUID
     * @param writeUUID
     */
    public void bleTestConnect(BleDevice bleDevice, String serviceUUID, String writeUUID) {
        byte[] data = build(0x43, new ArrayList<>());
        writeData(bleDevice, serviceUUID, writeUUID, data);
    }

    /**
     * 给设备发心跳
     * @param bleDevice
     * @param serviceUUID
     * @param writeUUID
     */
    public void heartbeat(BleDevice bleDevice, String serviceUUID, String writeUUID) {
        byte[] data = build(0x4C, new ArrayList<>());
        writeData(bleDevice, serviceUUID, writeUUID, data);
    }

    /**
     * 查询设备数据
     *
     * @param bleDevice
     * @param serviceUUID
     * @param writeUUID
     */
    public void getBleData(BleDevice bleDevice, String serviceUUID, String writeUUID) {
        byte[] data = build(0x45, new ArrayList<>());
        writeData(bleDevice, serviceUUID, writeUUID, data);
    }

    /**
     * 定时回传使能
     *
     * @param bleDevice
     * @param serviceUUID
     * @param writeUUID
     */
    public void timeBack(BleDevice bleDevice, String serviceUUID, String writeUUID) {
        ArrayList<Byte> array = new ArrayList<>();
        array.add(0, (byte) 0X01);
        //10ms
        array.add(1, (byte) 10);
        array.add(2, (byte) 0X00);

        byte[] data = build(0x47, array);
        writeData(bleDevice, serviceUUID, writeUUID, data);
    }

    /**
     * 取消定时回传使能
     *
     * @param bleDevice
     * @param serviceUUID
     * @param writeUUID
     */
    public void cancelTimeBack(BleDevice bleDevice, String serviceUUID, String writeUUID) {
        ArrayList<Byte> array = new ArrayList<>();
        array.add(0, (byte) 0X00);
        array.add(1, (byte) 0X08);
        array.add(2, (byte) 0X00);

        byte[] data = build(0x47, array);
        writeData(bleDevice, serviceUUID, writeUUID, data);
    }
}```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值