问题:
项目需要使用蓝牙外接设备,但每次重启开机到launcher显示后,蓝牙设备均需要等待20秒以上才能操作,体验非常差。
分析:
蓝牙的回连是由framework层实现。BluetoothManagerService相关代码如下,可以看到,自动打开蓝牙的流程是接收开机广播实现的。
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
......省略
} else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
synchronized(mReceiver) {
if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
//Enable
if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
sendEnableMsg(mQuietEnableExternal);
}
}
......省略
}
}
};
BluetoothManagerService(Context context) {
......省略
IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
registerForAirplaneMode(filter);
mContext.registerReceiver(mReceiver, filter);
loadStoredNameAndAddress();
if (isBluetoothPersistedStateOn()) {
mEnableExternal = true;
}
......省略
}
继续分析开机流程,可以发现,BluetoothManagerService在08:00:23才接收到开机广播,那么是开机广播发的迟了,还是接收慢了?
可以看到,开机广播在08:00:07已经发出,也确实有其他APK接收到了,那么原因就是开机广播接收慢了。
那为什么有的APK又能接收到呢?原因有可能是广播接收顺序不同导致:
1、如果是有序广播,那么是按Receiver的优先级来发送的;
2、如果是无序广播(普通广播),那么如果广播是动态注册的,接收时是并行发送,静态注册则也是串行发送,顺序是根据APK启动的顺序来的。
很明显我们这个不可能是第二种情况了,那么也就是说开机广播是一个有序广播,我们的优先级太低了,是这样吗?
查看开机广播发送代码,可以看到其是使用broadcastIntentLocked发送,倒数第五个参数表示是否为有序广播,所以确实开机广播是有序广播,那么问题原因就已经很明确了。
final void finishBooting() {
......省略
for (int i=0; i
UserStartedState uss = mStartedUsers.valueAt(i);
if (uss.mState == UserStartedState.STATE_BOOTING) {
uss.mState = UserStartedState.STATE_RUNNING;
final int userId = mStartedUsers.keyAt(i);
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
broadcastIntentLocked(null, null, intent, null,
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
requestPssAllProcsLocked(SystemClock.uptimeMillis(),
true, false);
}
}
},
0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID,
userId);
Slog.e(TAG, "RECEIVE_BOOT_COMPLETE");
}
}
}
}
}
解决方法:
原因已明确,那么只需要提高BluetoothManagerService对开机广播的优先级即可,如下:
setPriority 可设置优先级为-1000~1000,越高则优先级越高,但实际上是可以设置到Integer.MAX_VALUE,目前暂不需要这么改,设置为1000,即IntentFilter.SYSTEM_HIGH_PRIORITY即可。
IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY/*Integer.MAX_VALUE*/);//提高优先级
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
registerForAirplaneMode(filter);
mContext.registerReceiver(mReceiver, filter);