crash报错如下:
08-13 23:47:39.701 9026 9026 E AndroidRuntime: FATAL EXCEPTION: main
08-13 23:47:39.701 9026 9026 E AndroidRuntime: Process: com.android.systemui, PID: 9026
08-13 23:47:39.701 9026 9026 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method ‘void com.android.systemui.qs.external.TileServiceManager.clearPendingBind()’ on a null object reference
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at com.android.systemui.qs.external.TileServices.onStartSuccessful(TileServices.java:210)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.service.quicksettings.TileService$H.handleMessage(TileService.java:421)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6699)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:501)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:951)
相关代码摘录如下:
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@Override
public void onStartSuccessful(IBinder token) {
CustomTile customTile = getTileForToken(token);
if (customTile != null) {
verifyCaller(customTile);
synchronized (mServices) {
final TileServiceManager tileServiceManager = mServices.get(customTile);
tileServiceManager.clearPendingBind();
}
customTile.refreshState();
}
}
private CustomTile getTileForToken(IBinder token) {
synchronized (mServices) {
return mTokenMap.get(token);
}
}
可以看到,涉及到此空指针问题的有2个操作:
1. 根据参数token,从mTokenMap中找到对应的CustomTile对象, 如果能找到, 执行下面的第2个操作;
2. 根据第1个操作中找到的CustomTile对象,从mServices中查找对应的TileServiceManager对象
空指针问题的原因在于第1个操作找到了CustomTile对象,而第2个操作没有找到该CustomTile对象对应的TileServiceManager对象。
再看下mTokenMap和mServices 这2个ArrayMap的数据添加、删除及更新逻辑:
private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
private final ArrayMap<IBinder, CustomTile> mTokenMap = new ArrayMap<>();
public TileServiceManager getTileWrapper(CustomTile tile) {
ComponentName component = tile.getComponent();
TileServiceManager service = onCreateTileService(component, tile.getQsTile());
synchronized (mServices) {
mServices.put(tile, service);
mTiles.put(component, tile);
mTokenMap.put(service.getToken(), tile);
}
return service;
}
public void freeService(CustomTile tile, TileServiceManager service) {
synchronized (mServices) {
service.setBindAllowed(false);
service.handleDestroy();
mServices.remove(tile);
mTokenMap.remove(service.getToken());
mTiles.remove(tile.getComponent());
final String slot = tile.getComponent().getClassName();
// TileServices doesn't know how to add more than 1 icon per slot, so remove all
mMainHandler.post(() -> mHost.getIconController()
.removeAllIconsForSlot(slot));
}
}
源码中涉及的mServices和mTokenMap这2个数据结构的全部更新逻辑如上,其它都是访问及使用的逻辑。
从上面2段代码看, mService和mTokenMap的数据更新操作是一起进行的,且都在mServices同步锁中,其中的数据也是通过一个相同的CustomTile对象连接在一起,那为什么会出现有一个CustomTile对象在mTokenMap中存在,但在mServices中不存在的现象呢?
加log打印后复测发现,原因在于, 空指针所在的onStartSuccessful函数中, getTileForToken(token)操作和mServices.get(customTile)操作不在同一个锁中,不具有原子性, 如果这2个操作中间有一个freeService操作,就会导致上述空指针问题。
完整log如下:
—-主线程 添加 CustomTile@96363d1
08-13 23:47:36.915 9026 9026 W System.err: java.lang.Exception: Stack trace
08-13 23:47:36.916 9026 9026 W System.err: at java.lang.Thread.dumpStack(Thread.java:1348)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.external.TileServices.getTileWrapper(TileServices.java:93)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.external.CustomTile.<init>(CustomTile.java:83)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.external.CustomTile.create(CustomTile.java:359)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.tileimpl.QSFactoryImpl.createTileInternal(QSFactoryImpl.java:105)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.tileimpl.QSFactoryImpl.createTile(QSFactoryImpl.java:57)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.QSTileHost.createTile(QSTileHost.java:285)
08-13 23:47:36.916 9026 9026 W System.err: at com.android.systemui.qs.QSTileHost.onTuningChanged(QSTileHost.java:204)
08-13 23:47:36.917 9026 9026 W System.err: at com.android.systemui.qs.external.TileServices$3.onReceive(TileServices.java:363)
08-13 23:47:36.917 9026 9026 W System.err: at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1391)
08-13 23:47:36.917 9026 9026 W System.err: at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
08-13 23:47:36.917 9026 9026 W System.err: at android.os.Handler.handleCallback(Handler.java:873)
08-13 23:47:36.917 9026 9026 W System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
08-13 23:47:36.917 9026 9026 W System.err: at android.os.Looper.loop(Looper.java:193)
08-13 23:47:36.917 9026 9026 W System.err: at android.app.ActivityThread.main(ActivityThread.java:6699)
08-13 23:47:36.917 9026 9026 W System.err: at java.lang.reflect.Method.invoke(Native Method)
08-13 23:47:36.917 9026 9026 W System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:501)
08-13 23:47:36.917 9026 9026 W System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:951)
08-13 23:47:36.918 9026 9026 I TileServices: cx getTileWrapper com.android.systemui.qs.external.CustomTile@96363d1 service com.android.systemui.qs.external.TileServiceManager@c615e36 service.getToken() android.os.Binder@3edc137
— 主线程,收到onStartSuccesful回调,此时CustomTile@96363d1还没有被删除
08-13 23:47:39.690 9026 9026 I TileServices: onStartSuccessful customTile com.android.systemui.qs.external.CustomTile@96363d1 token android.os.Binder@3edc137
—-子线程9115抢到mServices锁, 删除了CustomTile@96363d1
08-13 23:47:39.689 9026 9115 W System.err: java.lang.Exception: Stack trace
08-13 23:47:39.689 9026 9115 W System.err: at java.lang.Thread.dumpStack(Thread.java:1348)
08-13 23:47:39.689 9026 9115 W System.err: at com.android.systemui.qs.external.TileServices.freeService(TileServices.java:108)
08-13 23:47:39.689 9026 9115 W System.err: at com.android.systemui.qs.external.CustomTile.handleDestroy(CustomTile.java:236)
08-13 23:47:39.690 9026 9115 W System.err: at com.android.systemui.qs.tileimpl.QSTileImpl$H.handleMessage(QSTileImpl.java:469)
08-13 23:47:39.690 9026 9115 W System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
08-13 23:47:39.690 9026 9115 W System.err: at android.os.Looper.loop(Looper.java:193)
08-13 23:47:39.690 9026 9115 W System.err: at android.os.HandlerThread.run(HandlerThread.java:65)
08-13 23:47:39.690 9026 9115 I TileServices: cx freeServices tile com.android.systemui.qs.external.CustomTile@96363d1 service com.android.systemui.qs.external.TileServiceManager@c615e36 service.getToken android.os.Binder@3edc137
—- 主线程, 抢到mServices锁,继续执行onStartSuccessful,就会报空指针了
08-13 23:47:39.701 9026 9026 E AndroidRuntime: FATAL EXCEPTION: main
08-13 23:47:39.701 9026 9026 E AndroidRuntime: Process: com.android.systemui, PID: 9026
08-13 23:47:39.701 9026 9026 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method ‘void com.android.systemui.qs.external.TileServiceManager.clearPendingBind()’ on a null object reference
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at com.android.systemui.qs.external.TileServices.onStartSuccessful(TileServices.java:210)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.service.quicksettings.TileService$H.handleMessage(TileService.java:421)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6699)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:501)
08-13 23:47:39.701 9026 9026 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:951)
上述都是androidP原生的, 应该是原生代码疏漏。
总结:强关联的多个数据的操作要保证原子性,不止增删时需要,在访问时也要保证原子性。