[小折腾] SharedPreferenceImpl$EditorImpl#apply引发的ANR场景

书本《Android工程化最佳实践》(作者金凯)中谈到SharedPreference会有ANR的可能,以及贴出关键源码(api19)和相关的log日志,读者可以先行看看。

目前可以确定的是,若写文件真的耗时,不管在哪个线程执行apply,都有可能引发ANR。

来个时序图,基于android api26

可以看到,如果work耗时长,UI线程必定会阻塞等待,越长越容易引发ANR

想要折腾验证,本人是通过一些代码,实现work做一些耗时的动作,在写文件时sleep当前线程,源码在最下方

日志:

看看thread14在做什么

可以看到queued-work-looper其实就是QueuedWork自己开的线程HandlerThread,这个线程还在执行耗时任务,任务是写数据到本地文件

 

这和书上的不太一样,书上的是阻塞在CountDownLatch.await,而这里却是阻塞在synchronize(sProcessWork)中,其实原因是androidApi26不一样了,那个版本开始变本人还没看,只看了androidApi19,Api19的QueuedWork源码和书本中介绍的是一样的,读者可以自行阅读一下。本人基于androidApi19实践后的结果:

日志:

而QueuedWork也不一样,用的是SingleThreadExecutor执行任务的

 

个人看法:

  • apply会阻塞一下ui线程,哪怕数据小影响不大,目前本人能力限制还不知道如何保证避免太大影响,所以还是在子线程中做commit的操作,避免使用apply吧。除非心中有数。
  • 自行封装SharedPreferences或者使用第三方库,避免直接在ui中commit。

 

折腾用的源码:

protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstance);
    
    new Thread(() -> {
            try {
                SharedPreferences spImpl = getSharedPreferences("test", 0);
                spImpl.getAll();
                Field mMapFid = Class.forName("android.app.SharedPreferencesImpl").getDeclaredField("mMap");
                mMapFid.setAccessible(true);
                Log.d("testProxyHandler", "mapField: " + mMapFid.getType());
                Map<String, Object> map = new HashMap<>();
                ProxyHandler handler = new ProxyHandler(map);
                Class<?>[] interfaces = HashMap.class.getInterfaces();
                boolean containsMapInterf = false;
                for (Class<?> interf : interfaces) {
                    if (interf.toString().equals(Map.class.toString())) {
                        containsMapInterf = true;
                        break;
                    }
                }
                if (!containsMapInterf) {// it will be false on androidApi19
                    Log.d("testProxyHandler", "containsMapInterf:" + containsMapInterf);
                    Class<?>[] newInterfaces = new Class<?>[interfaces.length + 1];
                    System.arraycopy(interfaces, 0, newInterfaces, 1, interfaces.length);
                    newInterfaces[0] = Map.class;
                    interfaces = newInterfaces;
                }
                ClassLoader cl = handler.getClass().getClassLoader();
                StringBuilder interfsStr = new StringBuilder();
                for (int i = 0; i < interfaces.length; ++i) {
                    interfsStr.append(interfaces[i].toString());
                    interfsStr.append(";");
                }
                Log.d("testProxyHandler", "interfaces: " + interfsStr.toString());
                mMapFid.set(spImpl, (Map<String, Object>) Proxy.newProxyInstance(cl, interfaces, handler));

                spImpl.edit().putBoolean("test", false).apply();
            } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }).start();

        getWindow().getDecorView().postDelayed(() -> {
            startService(new Intent(this, MyService.class));
        }, 1000);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值