这个软件现在遇到最大的问题 就是慢 几乎每次连上相机都需要接近10秒的时间 让人不耐
经查 主要消耗时间在ble的discoverServices()上 因为我确定 其实并不需要每次都discoverServices() 而看下面底层调用Gatt service的源码 证实保存的BluetoothGattCharacteristic是可用再用的 因为只需要用的是其getInstanceId()得到的Gatt Handle号 这个其实对本应用是不变的
Java public int writeCharacteristic(
@NonNull BluetoothGattCharacteristic characteristic,
@NonNull byte[] value,
@WriteType int writeType) {
.....
try {
for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
requestStatus =
mService.writeCharacteristic(
mClientIf,
device.getAddress(),
characteristic.getInstanceId(),
writeType,
AUTHENTICATION_NONE,
value,
mAttributionSource);
于是 我决定再次连上 如果需要的Characteristic还在 就不进行discoverServices()了
Kotlinoverride fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
......
if (faithMode && characteristicGpsData!=null) {
gattConnected=true
cameraInitialized=false
lastSyncTime=null
loopActor.trySend(Unit)
} else {
gatt.discoverServices()
}
然而并没有尽全用 有时快有时慢
抓蓝牙包发现 即使我没有明式地调用discoverServices() 它仍然可能发生 有趣的是 即使第一次连接 需要明式地调用discoverServices() 它反而有可能不发生 显然后台有一个不可控的cache之类的东西
只能看AOSP源代码了 看看连接后发生了什么 底层是怎么搞cache的
C++void bta_gattc_conn(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
......
/* start database cache if needed */
if (p_clcb->p_srcb->gatt_database.IsEmpty() || p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) {
if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
// Consider the case that if GATT Server is changed, but no service
// changed indication is received, the database might be out of date. So
// if robust caching is known to be supported, always check the db hash
// first, before loading the stored database.
// Only load the database if we are bonded, since the device cache is
// meaningless otherwise (as we need to do rediscovery regardless)
gatt::Database db = btm_sec_is_a_bonded_dev(p_clcb->bda)
? bta_gattc_cache_load(p_clcb->p_srcb->server_bda)
: gatt::Database();
auto robust_caching_support = GetRobustCachingSupport(p_clcb, db);
log::info("Connected to {}, robust caching support is {}",
p_clcb->bda.ToRedactedStringForLogging(), robust_caching_support);
if (!db.IsEmpty()) {
p_clcb->p_srcb->gatt_database = db;
}
if (db.IsEmpty() || robust_caching_support != RobustCachingSupport::UNSUPPORTED) {
// If the peer device is expected to support robust caching, or if we
// don't know its services yet, then we should do discovery (which may
// short-circuit through a hash match, but might also do the full
// discovery).
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
/* set true to read database hash before service discovery */
p_clcb->p_srcb->srvc_hdl_db_hash = true;
/* cache load failure, start discovery */
bta_gattc_start_discover(p_clcb, NULL);
} else {
p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
}
} else { /* cache is building */
p_clcb->state = BTA_GATTC_DISCOVER_ST;
}
} else {
/* a pending service handle change indication */
if (p_clcb->p_srcb->srvc_hdl_chg) {
p_clcb->p_srcb->srvc_hdl_chg = false;
/* set true to read database hash before service discovery */
p_clcb->p_srcb->srvc_hdl_db_hash = true;
/* start discovery */
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
}
}
这里我要blame一下这个cache的思想了
如果高层程序员需要cache 自然会自己写 如果底层有cache 则应该提供一些控制接口 诸如允许或者不允许 现在就是政令不通 底层有自己的想法 队伍不好带了的感觉 需要discoverServices()的时候 它可能过于自信 未必真去刷新 不需要的似乎它反而去刷新 (甚至没有等待connection update过程结束 导致了Different Transaction Collision)
设备如果是刚开始连接 反而会有可能使用缓存的数据
C++ if (p_clcb->p_srcb->gatt_database.IsEmpty() ...) {
.....
gatt::Database db = btm_sec_is_a_bonded_dev(p_clcb->bda)
? bta_gattc_cache_load(p_clcb->p_srcb->server_bda)
: gatt::Database();
这也是为什么我第一次连接 需要discoverServices()的时候 经常没有实际发生 速度很快
但是之后HAL的db不空(因为我为了及时发现相机连接 使用了自动连接 状态是保留且持续的) 再次连接就需要看有没有发生service changed 如果发生了 会再次start discovery
C++else {
/* a pending service handle change indication */
if (p_clcb->p_srcb->srvc_hdl_chg) {
p_clcb->p_srcb->srvc_hdl_chg = false;
/* set true to read database hash before service discovery */
p_clcb->p_srcb->srvc_hdl_db_hash = true;
/* start discovery */
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
}
令人遗憾的是 Sony相机是个保守主义者 它有事没事都经常会发service changed 大抵是认出这个连接者来过 并执意让它不使用缓存
上面的流程 除非改写并编译自己的手机固件 没有什么改变的余地 从上往下撕个口子一下真的很难 我没有找到什么方法透过service/JNI直达底层 但我并没有放弃 还是看看这个蛋有没有条缝可用让我叮一下的
C++/** Start a discovery on server */
void bta_gattc_start_discover(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* /* p_data */) {
....
/* set all srcb related clcb into discovery ST */
bta_gattc_set_discover_st(p_clcb->p_srcb);
.....
/* clear the service change mask */
p_clcb->p_srcb->srvc_hdl_chg = false;
p_clcb->p_srcb->update_count = 0;
.....
可以看到 discovery并不是一个立即执行的过程 只是往状态机里设置了将要进入的状态 不过话又说回来 即使立即发出了查询包 也不影响我叮这颗蛋的姿势
我的做法很简单 设置一个2秒的超时 一般写蓝牙Characteristic不会花那么多时间 如果堵塞了 说明正在等待discovery结束 那么 我就disconnect 再马上connect 因为实际上 虽然discovery没成功完成 但是srvc_hdl_chg已经先被设为false了!!!! 这就是关键!!!!
Kotlinprivate suspend fun gattWrite(characteristic: BluetoothGattCharacteristic, bytes: ByteArray) : Boolean? {
return withTimeoutOrNull (if (faithMode) 2000L else 20000L) {
while (gattStatus!=null) delay(100)
suspendCancellableCoroutine<Boolean> { cont ->
cont.invokeOnCancellation {
gattStatus=null
Log.e("MainService.gattWrite", "timeout, try to reconnect")
// this will break discovery progress in backend for faith mode, and speed up the response
gatt?.disconnect()
gatt?.connect()
}
gattStatus=cont
.....
实测有效 discovery进程被打断 虽然重连了一下 但是时间由原来的9秒变成了4秒 对于即时拍摄就容易接受了
如果在onConnectionStateChange()的时候 有手段知道底层会不会discovery 立即重连 这个时间有望减到2秒
如果新的android底层发现这个事务型漏洞并弥补上 我的程序可能会陷入不断re-connect 但是这也是我为什么专门设置一个可以开关的faith mode的原因 加速源于hack 算是真正的"黑科技了" 但是我想 至少到Android15这个服方法都奏效 而且Android开发人员不会来看这个贴子(那我得喷他一脸 一个不可控得cache是为了啥) 编程中也不可能什么都能以安全事务的形态进行 那程序会上升到不可思议的难度
200字以内,仅用于支线交流,主线讨论请采用回复功能。