{"id":15027547,"url":"https://github.com/dingjikerbo/android-bluetoothkit","last_synced_at":"2025-05-14T22:08:54.330Z","repository":{"id":37431100,"uuid":"43876166","full_name":"dingjikerbo/Android-BluetoothKit","owner":"dingjikerbo","description":"Android BLE蓝牙通信库","archived":false,"fork":false,"pushed_at":"2023-04-25T06:02:54.000Z","size":3989,"stargazers_count":3646,"open_issues_count":115,"forks_count":748,"subscribers_count":106,"default_branch":"master","last_synced_at":"2025-05-14T22:08:47.737Z","etag":null,"topics":["android-ble","beacon","ble"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dingjikerbo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-10-08T09:07:33.000Z","updated_at":"2025-05-13T03:48:29.000Z","dependencies_parsed_at":"2023-01-17T14:45:11.159Z","dependency_job_id":"bc434ea2-add1-42c4-a348-0b75d0cc0f9b","html_url":"https://github.com/dingjikerbo/Android-BluetoothKit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dingjikerbo%2FAndroid-BluetoothKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dingjikerbo%2FAndroid-BluetoothKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dingjikerbo%2FAndroid-BluetoothKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dingjikerbo%2FAndroid-BluetoothKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dingjikerbo","download_url":"https://codeload.github.com/dingjikerbo/Android-BluetoothKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254235700,"owners_count":22036964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-ble","beacon","ble"],"created_at":"2024-09-24T20:06:39.185Z","updated_at":"2025-05-14T22:08:49.317Z","avatar_url":"https://github.com/dingjikerbo.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"BluetoothKit---Android Bluetooth Framework\n===========================\n\n这个库用于Android蓝牙BLE设备通信，支持设备扫描，连接，读写，通知。\n\n\n## 这套框架存在的意义\n\n一、统一解决Android蓝牙通信过程中的兼容性问题\n\n二、提供尽可能简单易用的接口，屏蔽蓝牙通信中的技术细节，只开放连接，读写，通知等语义。\n\n三、实现串行化任务队列，统一处理蓝牙通信中的失败以及超时，支持可配置的容错处理\n\n四、统一管理连接句柄，避免句柄泄露\n\n五、方便监控各设备连接状态，在尽可能维持连接的情况下，将最不活跃的设备自动断开。\n\n六、便于多进程APP架构下蓝牙连接的统一管理\n\n七、支持拦截所有对蓝牙原生接口的调用\n\n\n## 本框架源码讲解，可参考 **[Android BLE蓝牙通信教程](https://study.163.com/course/introduction/1006381079.htm)**\n\n# **用法**\n\n1、在Android Studio的build.gradle中，在dependencies里添加一行:\n\n```groovy\ncompile 'com.inuker.bluetooth:library:1.4.0'\n```\n\n如果是Eclipse，可以导入bluetoothkit.jar，在AndroidManifest.xml中添加如下：\n```\n\u003cuses-permission android:name=\"android.permission.BLUETOOTH\" /\u003e\n\u003cuses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" /\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" /\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" /\u003e\n\n\u003cuses-feature\n    android:name=\"android.hardware.bluetooth_le\"\n    android:required=\"true\" /\u003e\n\n\u003capplication\n    android:label=\"@string/app_name\"\u003e\n\n    \u003cservice\n        android:name=\"com.inuker.bluetooth.library.BluetoothService\" /\u003e\n\u003c/application\u003e\n```\n\n2、创建一个BluetoothClient，建议作为一个全局单例，管理所有BLE设备的连接。 \n\n```Java\nBluetoothClient mClient = new BluetoothClient(context);\n```\n\n所有接口都通过BluetoothClient调用，涉及的常量如回调的错误码都在Constants类中。\n\n## **设备扫描** \n\n支持经典蓝牙和BLE设备混合扫描，可自定义扫描策略。每次扫描都要创建新的SearchRequest，不能复用。\n\n```Java\nSearchRequest request = new SearchRequest.Builder()\n        .searchBluetoothLeDevice(3000, 3)   // 先扫BLE设备3次，每次3s\n        .searchBluetoothClassicDevice(5000) // 再扫经典蓝牙5s\n        .searchBluetoothLeDevice(2000)      // 再扫BLE设备2s\n        .build();\n\nmClient.search(request, new SearchResponse() {\n    @Override\n    public void onSearchStarted() {\n\n    }\n\n    @Override\n    public void onDeviceFounded(SearchResult device) {\n        Beacon beacon = new Beacon(device.scanRecord);\n        BluetoothLog.v(String.format(\"beacon for %s\\n%s\", device.getAddress(), beacon.toString()));\n    }\n\n    @Override\n    public void onSearchStopped() {\n\n    }\n\n    @Override\n    public void onSearchCanceled() {\n\n    }\n});\n```\n\n如果扫描不出来，可将targetSdk调到低于6.0.\n\n可以随时停止扫描:\n\n```Java\nmClient.stopSearch();\n```\n\n## **蓝牙开关**\n\n打开关闭蓝牙：\n\n```\nmClient.openBluetooth();\nmClient.closeBluetooth();\n```\n\n判断蓝牙是否打开：\n\n```\nmClient.isBluetoothOpened();\n```\n\n蓝牙打开或关闭需要一段时间，可以注册回调监听状态，回调的参数如果是true表示蓝牙已打开，false表示蓝牙关闭\n\n```\nmClient.registerBluetoothStateListener(mBluetoothStateListener);\n\nprivate final BluetoothStateListener mBluetoothStateListener = new BluetoothStateListener() {\n    @Override\n    public void onBluetoothStateChanged(boolean openOrClosed) {\n        \n    }\n\n};\n\nmClient.unregisterBluetoothStateListener(mBluetoothStateListener);\n```\n\n## **设备配对**\n\n监听设备配对状态变化\n\n```\nprivate final BluetoothBondListener mBluetoothBondListener = new BluetoothBondListener() {\n    @Override\n    public void onBondStateChanged(String mac, int bondState) {\n        // bondState = Constants.BOND_NONE, BOND_BONDING, BOND_BONDED\n    }\n};\n\nmClient.registerBluetoothBondListener(mBluetoothBondListener);\nmClient.unregisterBluetoothBondListener(mBluetoothBondListener);\n```\n\n## **Beacon解析**\n\n可以在广播中携带设备的自定义数据，用于设备识别，数据广播，事件通知等，这样手机端无需连接设备就可以获取设备推送的数据。\n\n扫描到的beacon数据为byte[]，在SearchResult的scanRecord中，按如下形式生成Beacon对象，\n\n```\nBeacon beacon = new Beacon(device.scanRecord);\n```\n\nBeacon数据结构如下:\n\n```\npublic class Beacon {\n\n    public byte[] mBytes;\n\n    public List\u003cBeaconItem\u003e mItems;\n}\n```\n\nBeaconItem是按type来区分的，\n\n```\npublic class BeaconItem {\n    /**\n     * 广播中声明的长度\n     */\n    public int len;\n\n    /**\n     * 广播中声明的type\n     */\n    public int type;\n\n    /**\n     * 广播中的数据部分\n     */\n    public byte[] bytes;\n}\n```\n\n然后根据自定义的协议，解析对应的BeaconItem中的bytes，首先创建一个BeaconParser，传入对应的BeaconItem，然后根据协议不断读取数据，\n如果协议中某个字段占1个字节，则调用readByte，若占用两个字节则调用readShort，如果要取某个字节的某个bit则调用getBit。注意parser\n每读一次数据，指针就会相应向后移动，可以调用setPosition设置当前指针的位置。\n\n```\nBeaconItem beaconItem; // 设置成beacon中对应的item\nBeaconParser beaconParser = new BeaconParser(beaconItem);\nint firstByte = beaconParser.readByte(); // 读取第1个字节\nint secondByte = beaconParser.readByte(); // 读取第2个字节\nint productId = beaconParser.readShort(); // 读取第3,4个字节\nboolean bit1 = beaconParser.getBit(firstByte, 0); // 获取第1字节的第1bit\nboolean bit2 = beaconParser.getBit(firstByte, 1); // 获取第1字节的第2bit\nbeaconParser.setPosition(0); // 将读取起点设置到第1字节处\n```\n\n## **BLE设备通信** \n### **● 连接**\n\n连接过程包括了普通的连接(connectGatt)和发现服务(discoverServices)，这里收到回调时表明服务发现已完成。回调参数BleGattProfile包括了所有的service和characteristic的uuid。返回的code表示操作状态，包括成功，失败或超时等，所有常量都在Constants类中。\n\n```Java\nmClient.connect(MAC, new BleConnectResponse() {\n    @Override\n    public void onResponse(int code, BleGattProfile profile) {\n        if (code == REQUEST_SUCCESS) {\n        \n        }\n    }\n});\n```\n\n可以配置连接参数如下，\n\n```\nBleConnectOptions options = new BleConnectOptions.Builder()\n        .setConnectRetry(3)   // 连接如果失败重试3次\n        .setConnectTimeout(30000)   // 连接超时30s\n        .setServiceDiscoverRetry(3)  // 发现服务如果失败重试3次\n        .setServiceDiscoverTimeout(20000)  // 发现服务超时20s\n        .build();\n\nmClient.connect(MAC, options, new BleConnectResponse() {\n    @Override\n    public void onResponse(int code, BleGattProfile data) {\n\n    }\n});\n```\n\n### **● 连接状态**\n\n如果要监听蓝牙连接状态可以注册回调，只有两个状态：连接和断开。\n\n```\nmClient.registerConnectStatusListener(MAC, mBleConnectStatusListener);\n\nprivate final BleConnectStatusListener mBleConnectStatusListener = new BleConnectStatusListener() {\n\n    @Override\n    public void onConnectStatusChanged(String mac, int status) {\n        if (status == STATUS_CONNECTED) {\n\n        } else if (status == STATUS_DISCONNECTED) {\n\n        }\n    }\n};\n\nmClient.unregisterConnectStatusListener(MAC, mBleConnectStatusListener);\n```\n\n也可以主动获取连接状态：\n\n```\nint status = mClient.getConnectStatus(MAC);\n// Constants.STATUS_UNKNOWN\n// Constants.STATUS_DEVICE_CONNECTED\n// Constants.STATUS_DEVICE_CONNECTING\n// Constants.STATUS_DEVICE_DISCONNECTING\n// Constants.STATUS_DEVICE_DISCONNECTED\n```\n\n### **● 断开连接**\n```Java\nmClient.disconnect(MAC);\n```\n\n### **● 读Characteristic**\n```Java\nmClient.read(MAC, serviceUUID, characterUUID, new BleReadResponse() {\n    @Override\n    public void onResponse(int code, byte[] data) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 写Characteristic**\n\n要注意这里写的byte[]不能超过20字节，如果超过了需要自己分成几次写。建议的办法是第一个byte放剩余要写的字节的长度。\n\n```Java\nmClient.write(MAC, serviceUUID, characterUUID, bytes, new BleWriteResponse() {\n    @Override\n    public void onResponse(int code) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n这个写是带了WRITE_TYPE_NO_RESPONSE标志的，实践中发现比普通的write快2~3倍，建议用于固件升级。\n\n```Java\nmClient.writeNoRsp(MAC, serviceUUID, characterUUID, bytes, new BleWriteResponse() {\n    @Override\n    public void onResponse(int code) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 读Descriptor**\n\n```Java\nmClient.readDescriptor(MAC, serviceUUID, characterUUID, descriptorUUID, new BleReadResponse() {\n    @Override\n    public void onResponse(int code, byte[] data) {\n\n    }\n});\n```\n\n### **● 写Descriptor**\n\n```Java\nmClient.writeDescriptor(MAC, serviceUUID, characterUUID, descriptorUUID, bytes, new BleWriteResponse() {\n    @Override\n    public void onResponse(int code) {\n\n    }\n});\n```\n\n### **● 打开Notify**\n\n这里有两个回调，onNotify是接收通知的。\n\n```Java\nmClient.notify(MAC, serviceUUID, characterUUID, new BleNotifyResponse() {\n    @Override\n    public void onNotify(UUID service, UUID character, byte[] value) {\n        \n    }\n\n    @Override\n    public void onResponse(int code) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 关闭Notify**\n```Java\nmClient.unnotify(MAC, serviceUUID, characterUUID, new BleUnnotifyResponse() {\n    @Override\n    public void onResponse(int code) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 打开Indicate**\n\n和Notify类似，\n\n```Java\nmClient.indicate(MAC, serviceUUID, characterUUID, new BleNotifyResponse() {\n    @Override\n    public void onNotify(UUID service, UUID character, byte[] value) {\n        \n    }\n\n    @Override\n    public void onResponse(int code) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 关闭Indicate**\n\n```Java\nmClient.unindicate(MAC, serviceUUID, characterUUID, new BleUnnotifyResponse() {\n    @Override\n    public void onResponse(int code) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 读Rssi**\n```Java\nmClient.readRssi(MAC, new BleReadRssiResponse() {\n    @Override\n    public void onResponse(int code, Integer rssi) {\n        if (code == REQUEST_SUCCESS) {\n\n        }\n    }\n});\n```\n\n### **● 清理请求队列**\n\n如果发送给设备的请求设备来不及处理，则这些请求会保存到队列中，如果在某些场景下需要清除这些请求，可以调用\n\n```\nmClient.clearRequest(MAC, clearType);\n// Constants.REQUEST_READ，所有读请求\n// Constants.REQUEST_WRITE，所有写请求\n// Constants.REQUEST_NOTIFY，所有通知相关的请求\n// Constants.REQUEST_RSSI，所有读信号强度的请求\n```\n\nclearType表示要清除的请求类型，如果要清除多种请求，可以将多种类型取或，如果要清除所有请求，则传入0。\n\n### **● 刷新缓存**\n\n```\nmClient.refreshCache(MAC);\n```\n\n---\n有问题或建议可以给我邮件，到我的博客留言，或者加QQ群\n\n - Email: dingjikerbo@gmail.com\n\n - Blog: http://blog.csdn.net/dingjikerbo\n\n - QQ群: 112408886","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdingjikerbo%2Fandroid-bluetoothkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdingjikerbo%2Fandroid-bluetoothkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdingjikerbo%2Fandroid-bluetoothkit/lists"}