算是做了n年的智能穿戴BLE开发了, 首先对国内的安卓开发者提醒下 , BLE开发是真的很坑, 特别是安卓, ios端也坑, 但没安卓坑 因为国产有很多手机 各种奇葩兼容都有,
其实这些方案我很早就写到云笔记里了,一直没公开, 这里的解决方案大部分都是网上搜不到 或者网上搜到类似的问题, 但回复基本上是回答[无法解决] 或 [重启手机解决]等 没意义的解决办法,让我很无语…

以下内容可能涉及到各种系统类源码
你可以通过这里阅读 https://www.androidos.net.cn/

废话不多说, 希望对你们有用

1.刷新蓝牙app的状态

#####问题描述:
某些手机用久了会出现扫描不到任何设备的bug,
此时是因为手机误认为本app不是[ble类] app , f**k!!! 还有这种操作???
但值得注意的是, 这只是一种原因,[ 扫描不到任何设备的bug] 有很多种原因, 详情请看第3点
#####解决方案:
[目前网上没有与我类似的解决办法, 所以具体副作用自测]
参考 IBluetoothManager.aidl 系统源码

出现该问题时于是通过查看系统源码找到isBleAppPresent 方法 ,反射调用其后居然返回false ,
换了一台能正常使用的手机 调用该方法 返回true 因此证实了这个问题,
然后发现系统有私有的updateBleAppCount方法, 可以刷新ble类app的状态,反射调用之…
因此解决了 [偶尔ble设备扫描不出来]的bug),
通过传入你的app包名 以 刷新 蓝牙app的错误状态

 public static void refreshBleAppFromSystem(Context context, String packageName) {//6.0以上才有该功能,不是6.0以上就算了if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {return;}BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();if (adapter == null) {return;}if (!adapter.isEnabled()) {return;}try {Object mIBluetoothManager = getIBluetoothManager(adapter);Method isBleAppPresentM = mIBluetoothManager.getClass().getDeclaredMethod("isBleAppPresent");isBleAppPresentM.setAccessible(true);boolean isBleAppPresent = (Boolean) isBleAppPresentM.invoke(mIBluetoothManager);if (isBleAppPresent) {return;}Field mIBinder = BluetoothAdapter.class.getDeclaredField("mToken");mIBinder.setAccessible(true);Object mToken = mIBinder.get(adapter);//刷新偶尔系统无故把app视为非 BLE应用 的错误标识 导致无法扫描设备if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0+ (部分手机是7.1.2 也是如此)Method updateBleAppCount = mIBluetoothManager.getClass().getDeclaredMethod("updateBleAppCount", IBinder.class, boolean.class, String.class);updateBleAppCount.setAccessible(true);//关一下 再开updateBleAppCount.invoke(mIBluetoothManager, mToken, false, packageName);updateBleAppCount.invoke(mIBluetoothManager, mToken, true, packageName);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {try {//6.0~7.1.1Method updateBleAppCount = mIBluetoothManager.getClass().getDeclaredMethod("updateBleAppCount", IBinder.class, boolean.class);updateBleAppCount.setAccessible(true);//关一下 再开updateBleAppCount.invoke(mIBluetoothManager, mToken, false);updateBleAppCount.invoke(mIBluetoothManager, mToken, true);} catch (NoSuchMethodException e) {//8.0+ (部分手机是7.1.2 也是如此)try {Method updateBleAppCount = mIBluetoothManager.getClass().getDeclaredMethod("updateBleAppCount", IBinder.class, boolean.class, String.class);updateBleAppCount.setAccessible(true);//关一下 再开updateBleAppCount.invoke(mIBluetoothManager, mToken, false, packageName);updateBleAppCount.invoke(mIBluetoothManager, mToken, true, packageName);} catch (NoSuchMethodException e1) {e1.printStackTrace();}}}} catch (Throwable e) {e.printStackTrace();}}

2.明明gatt.disconnect() 断开蓝牙了,甚至关闭了手机的蓝牙,甚至飞行模式了, 设备仍然在 [已连接] 状态!!! 设备离手机远了才断开,说明,这压根就没断开啊!

看到标题知道国产手机奇葩了吧? 而且我相信不少人都遇到这个问题, 这个问题经常出现在华为>小米>魅族>(VIVO|OPPO) 上,

问题描述

首先导致这个原因可能是:
1.操作:gatt.disconnect/connect 断开,连接,断开,连接,断开… 反复重试n次, 有一定的几率导致系统残留了该gatt的引用, app这边没有拿到这个引用 (app操作蓝牙api是通过 remote aidl 操作远程的 系统service ), 系统蓝牙app 也没有了这个引用 ,于是即使你 直接关闭手机蓝牙, 也没有断开连接… 有些手机直到开启飞行模式 才会断开, 而有些手机 即使开启飞行模式也不会断开! 这得看这些手机 的 飞行模式 的实现代码的区别了,暂时没去研究.
2. 猜测连接设备后 被系统杀掉/ 或手动杀掉 也会导致这种情况

解决方案

######1.首先你可以获取真正的连接状态:
[目前网上没有与我类似的解决办法, 所以具体副作用自测]
参看 IBluetooth.aidl 系统源码
出现这种假断开问题时, 笔者曾经尝试 各种 gatt.getConnectionState() ,BluetoothManager.getConnectionState都是 给我们开发者返回 已断开!, 但实际上没有断开, 经过一番研究后 发现 判断内部连接状态可以通过另一个办法 而不通过 gatt,
则 BluetoothDevice 类 内部的 isConnected() 方法
这个方法被标记为@SystemApi和@hide, 不能直接使用.
并且在低版本的手机上没有, 查看了源码 isConnected是由IBluetooth.getConnectionState() 实现的, 低版本有getConnectionState

    @SystemApipublic boolean isConnected() {final IBluetooth service = sService;if (service == null) {// BT is not enabled, we cannot be connected.return false;}try {return service.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED;} catch (RemoteException e) {Log.e(TAG, "", e);return false;}}

于是我们可以通过反射
BluetoothDevice.isConnected/ IBluetooth .getConnectionState实现内部连接状态的判断
高低版本兼容的代码如下:

public static final int CONNECTION_STATE_DISCONNECTED = 0;
public static final int CONNECTION_STATE_CONNECTED = 1;
public static final int CONNECTION_STATE_UN_SUPPORT = -1;@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@SuppressLint("PrivateApi")public static int getInternalConnectionState(String mac) {//该功能是在21 (5.1.0)以上才支持, 5.0 以及以下 都 不支持if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {return CONNECTION_STATE_UN_SUPPORT;}if(Build.MANUFACTURER.equalsIgnoreCase("OPPO")){//OPPO勿使用这种办法判断, OPPO无解return CONNECTION_STATE_UN_SUPPORT;}BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();BluetoothDevice remoteDevice = adapter.getRemoteDevice(mac);Object mIBluetooth = null;try {Field sService = BluetoothDevice.class.getDeclaredField("sService");sService.setAccessible(true);mIBluetooth = sService.get(null);} catch (Exception e) {return CONNECTION_STATE_UN_SUPPORT;}if (mIBluetooth == null) return CONNECTION_STATE_UN_SUPPORT;boolean isConnected;try {Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected");isConnectedMethod.setAccessible(true);isConnected = (Boolean) isConnectedMethod.invoke(remoteDevice);isConnectedMethod.setAccessible(false);} catch (Exception e) {//如果找不到,说明不兼容isConnected, 尝试去使用getConnectionState 判断try {Method getConnectionState = mIBluetooth.getClass().getDeclaredMethod("getConnectionState", BluetoothDevice.class);getConnectionState.setAccessible(true);int state = (Integer) getConnectionState.invoke(mIBluetooth, remoteDevice);getConnectionState.setAccessible(false);isConnected = state == CONNECTION_STATE_CONNECTED;} catch (Exception e1) {return CONNECTION_STATE_UN_SUPPORT;}}return isConnected ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED;}

######2.尝试断开
[目前网上没有与我类似的解决办法, 所以具体副作用自测]
参考AdapterService.java 系统源码
仍然是从 IBluetooth 入手, 因为 应用层 能拿到的东西不多
研究源码发现 有两个函数可以尝试让 ble 服务关闭和启动,分别是onLeServiceUp / onBrEdrDown
示例

    @RequiresApi(api = Build.VERSION_CODES.M)public static void setLeServiceEnable(boolean isEnable) {Object mIBluetooth;try {Field sService = BluetoothDevice.class.getDeclaredField("sService");sService.setAccessible(true);mIBluetooth = sService.get(null);} catch (Exception e) {return;}if (mIBluetooth == null) return;try {if (isEnable) {Method onLeServiceUp = mIBluetooth.getClass().getDeclaredMethod("onLeServiceUp");onLeServiceUp.setAccessible(true);onLeServiceUp.invoke(mIBluetooth);} else {Method onLeServiceUp = mIBluetooth.getClass().getDeclaredMethod("onBrEdrDown");onLeServiceUp.setAccessible(true);onLeServiceUp.invoke(mIBluetooth);}} catch (Exception e) {e.printStackTrace();}}

但该方法可能在某些手机上仍然无效,原因是 很多国产手机都重新修改了 蓝牙底层相关代码, 为了所谓的省电, 所以单靠看原生系统的源码可能是无意义的
之后琢磨出另一个解决办法 那就是… 尝试连接~然后断开!
方法很简单,直接通过 gatt.connectGatt() 等待连接成功后 disconnect 一次, 此时设备终于断开了!
原因可能是connect后 刷新了残留的gatt引用 于是app又重新拿到了最新的引用, 此时可以操作设备断开了

...
gatt.connectGatt();
...
onConnectionStateChange(final BluetoothGatt gatt, final int status, int newState){if (newState == BluetoothProfile.STATE_CONNECTED){gatt.disconnect();}
}

不过你要注意下不要和你的正常连接逻辑冲突

以上操作,手机显示的蓝牙图标一直是关闭的, 你可能想问我 : 那手机蓝牙关了 怎么反射让他显示开… 这个你只能问这个手机的相关工程师为啥这么脑残了… 无解, 我们只考虑app问题,系统脑残管不了

3.多次打开app/退出app/后台被杀等, 导致扫描不到设备,并返回ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED 错误!

问题描述

但值得注意的是, 这只是第二种原因,[ 扫描不到任何设备的bug]还有其他原因, 详情请看第4点
扫描周围的BLE设备时某些手机会遇到 GATT_Register: cant Register GATT client, MAX client reached!
或者回调中的 onScanFailed 返回了 errorCode =2 则: ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED
具体表现则为 明明周围有很多设备,但是扫描不到任何东西
查到
1.https://blog.csdn.net/chy555chy/article/details/53788748
2.https://stackoverflow.com/questions/27516399/solution-for-ble-scans-scan-failed-application-registration-fail
3.http://detercode121.blogspot.com/2012/04/bluetooth-lowenergy-solution-for-ble.html
等等 没有一个是正常的解决办法,上面这些修复方案是用代码实现关闭蓝牙然后重新打开蓝牙来释放 可是 国产的手机会弹出蓝牙授权的 比如我们要后台扫描重连设备时遇到这种情况 难道要弹出授权让用户确定? 那还要后台重连功能干啥…
而且,有些手机即使关闭蓝牙再打开 也无法释放,有些手机关闭蓝牙后 再打开会卡死系统, 导致蓝牙图标一直卡在那很久 才打开了蓝牙…

解决方案

[目前网上没有与我类似的解决办法, 所以具体副作用自测]

参考IBluetoothGatt.aidl
参考BluetoothLeScanner.java
参考ScanManager.java
参考GattService.java

问题就在于 一些手机在startScan扫描的过程中还没来得及stopScan ,就被系统强制杀掉了, 导致mClientIf未被正常释放,实例和相关蓝牙对象已被残留到系统蓝牙服务中,
打开app后又重新初始化ScanCallback多次被注册,导致每次的扫描mClientIf的值都在递增, 于是mClientIf的值
在增加到一定程度时(最大mClientIf数量视国产系统而定 不做深究),onScanFailed 返回了 errorCode =2 至今网上无任何正常的解决办法
于是 我查看了系统源码 发现关键位置BluetoothLeScanner类下的 BleScanCallbackWrapper#startRegistration() 扫描是通过 registerClient 传入 mClientIf 来实现的,
在stopScan时调用了iGatt.stopScan() 和 iGatt.unregisterClient() 进行解除注册. 了解该原理后 我们就可以反射调用这个方法 , 至于解除mClientIf哪个值 需要你自己做存储记录
这里我写的是解除全部客户端 mClientIf的范围是 0~40
问题至此完美解决 这可能是目前全网唯一不用关闭/开启蓝牙就能完美解决该问题的方案

 public static boolean releaseAllScanClient() {try {Object mIBluetoothManager = getIBluetoothManager(BluetoothAdapter.getDefaultAdapter());if (mIBluetoothManager == null) return false;Object iGatt = getIBluetoothGatt(mIBluetoothManager);if (iGatt == null) return false;Method unregisterClient = getDeclaredMethod(iGatt, "unregisterClient", int.class);Method stopScan;int type;try {type = 0;stopScan = getDeclaredMethod(iGatt, "stopScan", int.class, boolean.class);} catch (Exception e) {type = 1;stopScan = getDeclaredMethod(iGatt, "stopScan", int.class);}for (int mClientIf = 0; mClientIf <= 40; mClientIf++) {if (type == 0) {try {stopScan.invoke(iGatt, mClientIf, false);} catch (Exception ignored) {}}if (type == 1) {try {stopScan.invoke(iGatt, mClientIf);} catch (Exception ignored) {}}try {unregisterClient.invoke(iGatt, mClientIf);} catch (Exception ignored) {}}stopScan.setAccessible(false);unregisterClient.setAccessible(false);BLESupport.getDeclaredMethod(iGatt, "unregAll").invoke(iGatt);return true;} catch (Exception e) {e.printStackTrace();}return false;}

其中如果你想获得 mClientIf 的值,方便研究该问题 可以尝参考以下代码
其中参数 ScanCallback 类 是安卓6.0扫描回调类

@TargetApi(Build.VERSION_CODES.LOLLIPOP)public static boolean isScanClientInitialize(ScanCallback callback) {try {Field mLeScanClientsField = getDeclaredField(BluetoothLeScanner.class, "mLeScanClients");//  HashMap<ScanCallback, BleScanCallbackWrapper>()HashMap callbackList = (HashMap) mLeScanClientsField.get(BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner());int size = callbackList == null ? 0 : callbackList.size();if (size > 0) {Iterator iterator = callbackList.entrySet().iterator();while (iterator.hasNext()) {Map.Entry entry = (Map.Entry) iterator.next();Object key = entry.getKey();Object val = entry.getValue();if (val != null && key != null && key == callback) {int mClientIf = 0;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {Field mScannerIdField = getDeclaredField(val, "mScannerId");mClientIf = mScannerIdField.getInt(val);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {Field mClientIfField = getDeclaredField(val, "mClientIf");mClientIf = mClientIfField.getInt(val);}System.out.println("mClientIf=" + mClientIf);return true;}}} else {if (callback != null) {return false;}}} catch (Exception ignored) {}return true;}

#4.扫描不到设备
前面说了好几个扫描不到设备的原因, 这里还有呢…
1 未开启位置访问权限Manifest.permission.ACCESS_COARSE_LOCATION 如果你是6.0系统 则需要申请该权限 才能扫描设备, 检查和申请网上有 这里不重复说了

2.检查GPS的 LOCATION_MODE是否开启,否则在OPPO/VIVO等手机 无法扫描设备

//代码 反编译 nrfconnect 参考得来:
public static boolean hasLocationEnablePermission(Context context) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {return true;}int locationMode = Settings.Secure.LOCATION_MODE_OFF;try {locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);} catch (Exception ignored) {}if (locationMode != Settings.Secure.LOCATION_MODE_OFF) {return true;}return false;
}
//没有权限则跳转到 gps界面授权
if(!hasLocationEnablePermission(this)){Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);
}

3.安卓7.0不允许在30s内连续扫描5次,否则无法扫描到任何设备,只能重启app, 你可以写一个算法 比如每次先延时30/5=6秒 才开始扫描, 以防止用户一直点扫描按钮 , 或者使用动态计算 以减少用户等待时间

5.其他注意点

#####1.为了加快连接设备的速度 , 你可以不扫描设备直接通过mac地址连接 ,使用 gatt.connectGatt ,但有时候连接不上是因为 设备信息可能变化了, 但系统缓存没变,所以一直连接不上, 即使连接上了 马上返回各种-133 -192等错误, 解决办法是 你需要 重新扫描这个mac一下,找到了mac ,再连接
#####2 扫描设备的时候 要切记, 扫描到了后 先停止扫描, 过1秒左右 再连接, 避免扫描的时候连接, 导致连接过程中 缓存再次被刷新

#####3.无任何原因, app扫描不到该设备,但能搜索到其他设备, 而另一个手机却都能搜索到, 试试下载 nrf的 nrfconnect 去搜索测试(同一台手机), 若 nrfconnect 能搜索到 则是app代码问题, 否则检测 设备蓝牙晶振频率 是否不支持该手机的蓝牙频率发现范围! 联系相关开发人员解决
#####4.距离防丢功能, 通过rssi可以拿到设备距离手机的信号值来判断 设备是否远离手机 触发防丢警报, 但rssi信号 受各种环境因素影响, 所以有点坑 , 建议做延迟处理 ,意思是达到防丢rssi值时 延时n秒 才警报, 若在n秒内 又恢复, 说明只是信号突然弱了一下. 无需 警报. 还有就是因素太多了 ,和手机的蓝牙模块有关, 和设备的蓝牙天线有关, 功率有关等, 建议在app内添加 一个用户可以设定的rssi防丢范围, 因为程序没法精准计算
#####5.扫描蓝牙设备callback回调时 建议丢到另外一个线程用队列去处理, 不要在扫描回调里处理耗时逻辑., 同理 在onCharacteristicChanged 中接收设备notify通知返回的数据时, 不要在此方法内进行耗时处理, 否则大量数据过来时会100%丢包!!! 解决办法和前面的一样. (话说某BLE开源框架就有这个问题,还好我用我自己写的)
#####6.同步大量数据时, 某些手机完美正常, 某些手机出现丢包严重,建议修改连接间隔 同步前 使用gatt.requestConnectionPriority(CONNECTION_PRIORITY_HIGH) , 同步完成后 恢复原来的连接间隔CONNECTION_PRIORITY_BALANCED
#####7. 对设备进行OTA升级后,直接使用mac来连接, 连接不上, 原因是系统缓存没刷新, 你需要扫描后停止扫描再连接
#####8.连接之前建议先把 gatt.close一下
#####9.使用gatt.discoverServices()发现服务之前,建议先 sleep 500 毫秒, 因为刚刚连接上, 系统有些东西需要刷新,同理,遇到任何问题 延时一下看看能否解决, 因为有些系统的蓝牙很慢很卡,甚至手动关闭蓝牙 都卡死在那 ,偶尔还死机重启了…

6.补充

贴出一些上面缺失的函数,因为方便和减少代码重复量, 所以上面没贴

@SuppressLint("PrivateApi")public static Object getIBluetoothGatt(Object mIBluetoothManager) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {Method getBluetoothGatt = getDeclaredMethod(mIBluetoothManager, "getBluetoothGatt");return getBluetoothGatt.invoke(mIBluetoothManager);}@SuppressLint("PrivateApi")public static Object getIBluetoothManager(BluetoothAdapter adapter) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {Method getBluetoothManager = getDeclaredMethod(BluetoothAdapter.class, "getBluetoothManager");return getBluetoothManager.invoke(adapter);}public static Field getDeclaredField(Class<?> clazz, String name) throws NoSuchFieldException {Field declaredField = clazz.getDeclaredField(name);declaredField.setAccessible(true);return declaredField;}public static Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException {Method declaredMethod = clazz.getDeclaredMethod(name, parameterTypes);declaredMethod.setAccessible(true);return declaredMethod;}public static Field getDeclaredField(Object obj, String name) throws NoSuchFieldException {Field declaredField = obj.getClass().getDeclaredField(name);declaredField.setAccessible(true);return declaredField;}public static Method getDeclaredMethod(Object obj, String name, Class<?>... parameterTypes) throws NoSuchMethodException {Method declaredMethod = obj.getClass().getDeclaredMethod(name, parameterTypes);declaredMethod.setAccessible(true);return declaredMethod;}

你可能还想问, 还有呢 还有最重要的 连接时总是返回 -133 -86 -192 这些怎么办啊 怎么解决啊
我只想和你说, 别抱着希望了 你可能在网上看到很多解决办法 但最终你使用了解决代码, 仍然无法解决…
放弃吧, 换一种思路, 遇到这种错误, 直接断开+sleep+扫描+重连, , 若检测到无数次返回这种错误,没一次连接成功的情况 记录下次数, 达到一定数量时 提示让用户关闭/开启飞行模式 然后重试吧. 这种因素很多 有手机蓝牙辣鸡的,有代码有问题的比如不扫描就连接, 有蓝牙设备有问题的 各种因素都有.

#7.调试
调试设备的工具 有 ios的lightblue,
安卓的推荐nrf芯片公司开发的 nrf connect 调试工具]

我也写了两个小应用,有兴趣可以下来看看
BLE调试器
https://www.coolapk.com/apk/com.toshiba.ble
BLE指令协议窃取工具(需要xposed), 可以窃取手机上的某app和其对应的ble设备 正在进行的数据通讯, 你可以理解为蓝牙协议抓包 (仅供学习用途)
https://www.coolapk.com/apk/com.tos.bledetector

8.趁早弃坑

安卓BLE开发是个巨坑, 现在安卓9.0快出来了, 说不能反射使用非SDK API, 上面的代码可能在9.0 都会出问题.
国产很多辣鸡智障工程师开发的 系统, 我直接点名了: 比如小米 华为 魅族 蓝绿厂等 ,平时为了兼容它们,只能反射反射这样子, 现在再加上谷歌9.0不能反射 的夹攻, 我们该何去何从?
所以 趁早弃坑吧…

9.2019-09-25补充 9.0反射解决办法(凑合用)

    //绕过Android P对非SDK接口的限制 http://weishu.me/2018/06/07/free-reflection-above-android-p/implementation 'me.weishu:free_reflection:1.2.0'

然后在application的 attachBaseContext 方法中初始化

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0Reflection.unseal(base)
}

Android BLE4.+ 蓝牙开发国产手机兼容性解决方案相关推荐

  1. Android BLE蓝牙开发知识总结

    Android BLE蓝牙开发知识总结 1.蓝牙介绍 1.1什么是蓝牙?    蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换(使用2 ...

  2. 【Android】蓝牙开发——经典蓝牙:配对与解除配对 实现配对或连接时不弹出配对框

    目录 一.配对方法 二.解除配对方法 三.配对/解除配对结果 四.justwork配对模式下,不弹出配对框 五.pincode配对模式下,不弹出配对框 六.小结 在之前的文章[Android]蓝牙开发 ...

  3. Android Bluetooth蓝牙开发:发现Bluetooth蓝牙设备(1)

     Android Bluetooth蓝牙开发:发现Bluetooth蓝牙设备(1) Android Bluetooth蓝牙作为设备,要与其他蓝牙设备互联,那么先决条件就是已经被发现,本文先简介An ...

  4. Android 低功耗蓝牙开发

    初识低功耗蓝牙 Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序通过这些 API 扫 ...

  5. 蓝牙配对模式 java_【Android】蓝牙开发—— 经典蓝牙配对介绍(Java代码实现演示)附Demo源码...

    目录 前言 一.连接&配对方法介绍 二.演示:第一次连接蓝牙设备  &  直接与蓝牙设备建立配对 三.总结 四.补充 五.Demo案例源码地址: 前言 前面两篇文章[Android]蓝 ...

  6. 【Android】蓝牙开发—— 经典蓝牙配对介绍(Java代码实现演示)附Demo源码

    目录 前言 一.连接&配对方法介绍 二.演示:第一次连接蓝牙设备  &  直接与蓝牙设备建立配对 三.总结 四.补充 五.Demo案例源码地址: 前言 前面两篇文章[Android]蓝 ...

  7. Android Ble蓝牙开发总结

    Android Ble蓝牙开发总结 前言 本文总结了ble的搜索,连接,读写操作.以及在开发过程中可能遇到的坑. 首先我们需要知道,什么是ble. 蓝牙发展至今经历了8个版本的更新.1.1.1.2.2 ...

  8. 【Android】蓝牙开发——BLE(低功耗蓝牙)(附完整Demo)

    目录 目录 前言 一.相关概念介绍 二.实战开发 三.项目演示 四.Demo案例源码地址 五.更新记录 1.2020/12/29 :修改 setupService()中错误 2.2021/05/14 ...

  9. Android经典蓝牙开发全流程

    一.基本介绍   所谓蓝牙(Bluetooth)技术,实际上是一种短距离无线电技术,最初是由爱立信公司公司发明的.技术始于爱立信公司 1994 方案,它是研究在移动电话和其他配件间进行低功耗.低成本无 ...

最新文章

  1. ZedGraph在项目中的应用
  2. 如何解决border的重叠问题
  3. 【Flutter】Dart 数据类型 Map 类型 ( 创建 Map 集合 | 初始化 Map 集合 | 遍历 Map 集合 )
  4. 数据存储之-SQLite数据库二
  5. C#Winform调用wsdl接口webservice#http
  6. PHP中 magic_quotes_gpc 和 magic_quotes_runtime 区别及其反斜线转义问题
  7. JAVA进阶教学之(foreach)
  8. HTML期末学生大作业-乒乓球网页作业html+css+javascript
  9. 修改数据_如何批量修改数据库中的特定记录数据
  10. MFC_CFileDialog_选择单一文件
  11. Partition分析
  12. LoadRunner11的安装与破解
  13. js排序的时间复杂度_各种排序算法时间复杂度
  14. DPDK (1) - PMD驱动方案
  15. 微信公众号h5开发流程
  16. 计算机中汉字的顺序有什么排列,汉字演变过程的时间排序是什么?
  17. 计算机二级报名时间2020年3月山西,2020年3月山西计算机二级报名及考试时间
  18. TokenUtil工具类(生成token和解析token)
  19. 你好 Redis,能回答我 7 个问题吗?
  20. 点击率(CTR)平滑手段

热门文章

  1. 《平衡掌控者 游戏数值战斗设计》学习笔记(二)人物基础属性设计
  2. Android应用中保存网络图片功能实现详解
  3. 全国计算机一级ms office考试题型,全国计算机考试一级MS Office考试大纲(2017年)
  4. 关于音视频直播技术的总结
  5. 初探~对ipa包进行混淆处理
  6. AutoCAD .NET 二次开发实例:批量文本查找替换
  7. FILCO Majestouch Convertible 2 键盘连接电脑说明
  8. 初识软件工程-软件工程的产生与发展
  9. Apple现行公开的framework简介
  10. 第5章 远程控制软件的编写