由Looper引发的crash及Looper源代码笔记

Crash

项目线上有一个crash,Exception与此相同。但出问题的原因经过分析,并不是多线程操作Adapter导致的,而是androidannotation的@UIThread标签导致的。
@UIThread实际上是给MainLooper post了一个Runnable,如果有两个都是@UIThread的函数嵌套调用,如:

@UIThread
void notifyChange(){
}
@UIThread
void bar(){
change();
notifyChange();
foo();
}

看起来bar()的执行顺序是:change()-notifyChange()-foo(),实际上执行顺序是:change()-MainLooper.post(notifyChange())-foo()-something-notifyChange()。bar与notifyChange直接可能有其他操作插入
UI线程之所以存在,就是为了所见顺序即运行顺序,避免多线程执行被打断的情况(即上述something处)。然而乱用Looper使得打断成为了可能。

一个不错的系统讲解

Handler

  • 所有时间基础都是SystemClock.uptimeMillis(),原因
  • 可以使用runWithScissors运行同步程序块,未列入api
  • 非静态内部的Handler子类可能造成内存泄露(Lint扫描)。因为Looper会持有Message,Message是一个Active Command,持有Handler以自执行。如果Message一直不被释放,则Handler所在类的引用也一直不会被GC

Looper

  • 用ThreadLocal保证当前Looper只有一个线程,包括后绑定都不可以
  • MainLooper仅仅是系统在App启动时在UI线程prepare的一个普通Looper,静态成员变量
  • 循环用的是for(;;),bytecode跟while(true)一样…
  • Looper持有线程的强引用,可能导致线程不被释放
  • 有一个神奇的阻塞looper的函数:postSyncBarrier。其后postSyncBarrier的Looper都会被阻塞,直到removeSyncBarrier被调用。时间使用的是SystemClock.uptimeMillis()

MessageQueue

  • 有一个IdleHandler接口
  • 在finalize里释放native资源
  • 使用的是一个单向链表,按时间排序存放Message
  • Message使用了对象池的方法,减少重复new对象。Message的生命周期是可控的,使用非常频繁,所以非常适合对象池
  • IdleHandler实际是在搜索后发现没有需要执行的Message时执行的,如果有Message,则会return退出函数,不执行到IdleHandler处

ActivityThread

Looper与UI事件,Crash中something的由来。from《深入理解Android内核设计思想》

  • 这里prepare了MainLooper
  • 所有UI事件都被加到了MainLooper里面,可能是postInvalidate这种send一个Message到MainLooper,更多的是借由调用执行的代码

你可能感兴趣的:(多线程,android,源代码)