1个多线程导致的crash问题

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原生的, 应该是原生代码疏漏。

总结:强关联的多个数据的操作要保证原子性,不止增删时需要,在访问时也要保证原子性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值