单线程模型
我们知道,Android系统是单线程模型,即应用程序启动时,系统会创建一个主线程,又叫做UI线程,负责与UI组件(进行交互,比如控制UI界面界面显示、更新等,UI线程只能处理一些简单的、短暂的操作,如果要执行繁重的任务或者耗时很长的操作,比如访问网络、数据库、下载等,这种单线程模型会导致线程运行性能大大降低,甚至阻塞UI线程,如果被阻塞超过5秒,系统会提示应用程序无相应对话框,即ANR,导致退出整个应用程序或者短暂杀死应用程序,所以Android系统将大部分耗时、繁重任务交给子线程完成,不会在主线程中完成;同时,Android只允许主线程更新UI界面,子线程处理后的结果无法和主线程交互,即无法直接访问主线程,这就要用到Handler机制来解决此问题
Handler
在我们在子线程中执行完耗时操作后很多情况下我们需要更新UI,但我们都知道,不能在子线程中更新UI。此时最常用的手段就是通过Handler将一个Message消息send到UI线程中,然后再在Handler的handleMessage方法中进行处理,常见代码
|
|
每个Handler都会关联一个消息队列,消息队列被封装在Lopper中,而每个Looper又会关联一个线程(ThreadLocal),也就是每个消息队列会关联一个线程。Handler就是一个消息处理器,将消息投递给消息队列,然后再由对应的线程从消息队列中挨个取出消息,并且执行。默认情况下,消息队列只有一个,即主线程的消息队列,这个消息队列是在系统的启动过程中的ActivityThread.main方法中创建的,通过Lopper.prepareMainLooper()来创建,然后最后执行Looper.loop()来启动消息循环。那么我们为什么在平时的使用Handler的时候,并没有使用Looper呀,其实是因为系统为我们创建好了,我们可以查看ActivityThread.java来看看这个过程,只列了关键代码:
|
|
通过上面的原理介绍,我们可以画出下面这个流程图
那么它们具体是怎么协作的呢?下面我们通过源码一步步解析,首先看下Handler
|
|
从Handler默认的构造函数中我们可以看到,Handler会在内部通过Looper.getLooper()来获取Looper对象,并且与之关联,那么Looper.myLooper又是如何工作的呢?我们继续往下看.
|
|
我们看到myLooper()方法是通过sThreadLocal.get()来获取的,上面也提到,在ActivityThread.main方法中会调用prepareMainLooper()
方法,prepareMainLooper中执行了prepare
,prepare
中会创建一个Looper并赋值给 sThreadLocal
,这样Looper就和线程关联上了,而且有一点很重要,普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare()方法,而且一个线程只能有一个looper ,而在Handler中,消息队列通过Looper与线程关联,而Handler又与Looper关联,因此Handler最终就和线程、线程的消息队列关联上了,那么创建了Looper后,是如何执行消息循环呢?我们来看下Looper.loop()
方法
Looper
|
|
很明显,就是一个大的循环,不断从消息队列出取出消息。然后调用一个很关键的方法msg.target.dispatchMessage(msg)开始处理消息。msg.target就是message对应的handler,所以对于Looper,我们结论就是通过Looper.prepare()来创建Looper对象(消息队列封装在Looper对象中),并且保存在sThreadLoal中,然后通过Looper.loop()来执行消息循环,这两步通常是成对出现的!
Message
最后我们看看消息处理机制,先看看Message
前面说到loop循环中处理信息的msg.target.dispatchMessage(msg)方法。现在我们来看下这个方法的源码
|
|
这里为什么要先判断msg.callback!=null呢
,因为post(Runnable r)
方法也能用来处理事件
|
|
在post(Runnable r)时,会将Runnable包装成Message对象,并且将Runnable对象设置给Message对象的callback字段,最后会将该Message对象插入消息队列。
至此,Handler的工作原理就说完了。