apply与commit有什么区别
还是从源码分析来说明问题:
image.png
image.png
接上图标记B1处,看下:
image.png
所以这个handler发送消息后,handleMessage会被调用看下:
image.png
image.png
上图提到的Runnable对象是哪个呢??就是enqueueDiskWrite方法里的writeToDiskRunnable对象:
image.png
接着分析上图提到的标记C1处:
image.png
上上图提到到写完文件操作之后,会调用postWriteRunnable的run来移除任务:
所以对于apply要有一个重要的认识就是:会创建一个子线程来执行写操作,这个子线程是支持消息循环机制的。
这里还有一个疑问就是在上文开始处标记A提到的,为什么入队进行写操作之前要先 QueuedWork.addFinisher(awaitCommit);,而在写操作完成后又调用 QueuedWork.removeFinisher(awaitCommit),关于这一点你可以这么理解:QueuedWork.addFinisher(awaitCommit)可以理解为标记开始写操作状态,QueuedWork.removeFinisher(awaitCommit)则理解为标记写操作结束状态,为何要如此标记呀?要做标记肯定是要给人看的,到底给谁看呢?这里我举一个例子当Activity生命周期要回调onStop的时候,会调用ActivityThread中的handleStopActivity方法,关于这一点你不明白的话可以参考我的文章
Activity生命周期回调是如何被回调的?:
image.png
看到上图会调用QueueWork的waitToFinish,而这个方法则是会执行没有完成的写操作,导致主线程被阻塞。所以这里可以得到一个结论apply虽然是另开线程执行写操作,仍然有可能阻塞主线程,上面的例子就是情景之一。如果在Activity Stop的时候,已经写入完毕了,那么万事大吉,不会有任何等待,这个函数会立马返回。但是,如果你使用了太多次的apply,那么意味着写入队列会有很多写入任务,而那里就只有一个线程在写。
如何解决apply导致的ANR问题
下面是头条的方案,主要的原理就是Hook ActivityThread中的Handler 对象,这个handler对象是static,
然后再为这个handler对象设置callback,这样handler会调用callback对象的handleMessage方法,只要我们重写这个callback方法逻辑,就可以跳过原来系统代码,详情参考
头条解决多次调用apply产生ANR的方案
接下来看看commit方法:
image.png
image.png
image.png
到此两个方法都分析完了,做下总结如下:
如果是在主线程直接使用建议使用apply
apply和commit都可以导致ANR,apply虽然在处理写文件操作时会在另一个线程来处理,但是多次调用情况下可能导致ANR,而commit则是直接在当前执行文件写操作。
作者:钟离四郎
链接:https://www.jianshu.com/p/d093cd2486d6