Android消息处理机制
概览
Android应用程序的每一个线程在启动时,都可以在内部创建一个消息队列,当然也有不存在消息队列的纯任务型线程
一般是根据是否有界面操作,把线程分为三种:
- 应用程序的主线程,Android把UI界面操作都放在这里 ActivityThread
- 与界面无关的纯任务线程 HandlerThread
- 与界面相关的子线程,AsyncTask
Android的消息机制主要是通过三个类来实现:MessageQueue、Looper、Handler
MessageQueue是消息队列实体,
Looper用来创建消息队列、进入消息循环
Handler用来发送消息和处理消息
不带消息循环的线程,即纯任务型线程的生命周期就是从任务开始执行到任务执行结束
带消息循环的线程,其生命周期划分为创建消息队列和进入消息循环两个阶段
下面就先分析创建和进入消息循环的两个阶段
创建消息队列
先看一下Looper的几个关键数据
1 | public final class Looper { |
静态成员sThreadLocal是线程局部存储,每个线程在这个变量中存储的Looper是独立的,即每个线程的sThreadLocal里存的都是自己的Looper
但是静态成员sMainLooper只保存主线程的Looper,设置这个变量就是为了让其他子线程都能够很轻易的获得主线程的Looper
Java层Looper的静态函数prepare函数似乎所有线程都可以访问的接口,就是创建属于当前线程的Looper并存放到threadlocal里面,myLooper是相反的接口,是提供给所有的子线程,从threadLocal里取出属于自己子线程的looper。
1 | /** |
prepareMainLooper只能由主线程调用,其内部还是调用的prepare接口,只不过将其保存到了sMainLooper中
再继续分析Looper的创建过程
1 | //构造函数 |
可以看到构造函数创建了MessageQueue对象,同时将当前线程保存到了mThread中
再来跟踪MessageQueue创建的具体过程,内部还是调用nativeInit来完成初始化
1 | private long mPtr; // used by native code |
nativeInit创建了一个nativeMessageQueue对象,同时返回了指针,所以mPtr里保存的就是nativeMessageQueue对象的地址,
1 | NativeMessageQueue::NativeMessageQueue() : |
NativeMessageQueue的构造函数首先获得当前线程的Looper,这个Looper是C++层的Looper,所以正常流程到这里是还没创建的,保存到mLooper中,
1 | Looper::Looper(bool allowNonCallbacks) : |
这里创建了eventfd,后续感知消息队列的变化就通过这个fd,rebuildEpollLocked就是将fd加到epoll中监控
这几个类的关系就是
进入消息循环
调用Looper的loop函数进入消息循环,依次获取当前线程的looper对象和消息队列
1 | //消息循环 |
queue的next方法就是取出新消息
1 | Message next() { |
这里nativePollOnce是来判断是否有新消息需要处理,如果有消息就会继续往下执行,否则就会卡在这里睡眠等待。
从nativePollOnce出来之后,mMessages存放的是当前需要处理的消息
Message是以链表的形式,按处理实现从小到大的顺序排列在消息队列中的,msg.next -> msg.next
首先判断当前消息是否到了要被处理的时间,如果到了,就返回message,(now>msg.when),如果还没到就计算一下还需要等待的时间,下一次循环nativePollOnce就会睡眠等待。
前面是有消息的情况,如果没有消息要处理,就给nextPollTimeoutMillis赋值-1,表示等待时间为无限
1 | //处理销毁的消息 |
我们可以注册空闲消息处理器到消息队列中,像这后半段,就是依次调用每个空闲消息处理器的queueIdle,如果queueIdle返回false的话,空闲消息队列就会被移除
判断新消息
回头看一下nativePollOnce是怎么判断是否有新消息存在的,
就是从Messagequeue -> nativeMessageQueue -> Looper
然后通过epoll判断是否有eventfd是否被写了,没有的话就会在这里等待
1 | private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ |
消息发送过程
Android系统通过Handler类来向线程的消息队列发送消息
handler的构造函数如下,需要给一个Callback参数保存到mcallback,这里可以看到Handler是关联到looper的,也即handler发送到的目标messageQueue
1 | public Handler(Callback callback, boolean async) { |
发送消息用的是sendMessage
1 | public final boolean sendMessage(Message msg) |
Delay 和AtTime都是封装的时间,最终是调用目标MessageQueue的enqueueMessage,注意,这里设置了target为this
1 | boolean enqueueMessage(Message msg, long when) { |
因为消息队列中的消息是按照处理时间从小到大排序的,所以需要判断插入的位置,当消息被插入到头部时,说明需要处理的消息的时间被提前了,那就得需要唤醒目标线程
这里可以看到message的插入就没经过C++层,是直接Java层面上就已经插入了消息队列中,C++的epoll只负责线程的睡眠和唤醒
看一下nativeWake
1 | private native static void nativeWake(long ptr); |
唤醒looper实际就是往eventfd随便写入一个uint64,然后那边epoll就知道有写入事件发生了
消息处理过程
回到Looper.loop
1 | Message msg = queue.next(); // might block |
这里派发消息是通过handler调用dispatchMessage,相当于通过handler发送消息,折返回来又通过handler处理消息。
1 | /** |
派发消息,如果消息自身在发送时已经指定了一个回调接口,那么就调用handleCallback来处理这个消息
如果Handler自身在创建时指定了一个Callback,就调用这个回调函数
都不满足的话,就调用handlerMessage来处理
handleCallback
1 | private static void handleCallback(Message message) { |
直接调用msg的callback的run函数,因为callback是一个Runnable对象
这种消息是通过post发送的,post首先会用getPostMessage把传进来的Runnable封装成Message对象,然后正常的调用sendMessage发送消息
1 | //调用getPostMessage将Runnable对象封装成Message对象 |
mCallback.handleMessage
mCallback是实现了Callback接口的对象
1 | /** |
其就是一个接口,实现了handleMessage
调用mCallback.handleMessage实际就是派发给实现了callback接口的一个对象处理
handleMessage
1 | //空实现,一般我们都是用Handler的一个子类来发送消息,然后使用其handleMessage来处理消息 |
handlerMessage是空实现,一般情况下我们都不直接用Handler来发送消息,而是使用它的一个子类来发送消息,这个子类重写了Handler的成员函数handlerMessage
空闲消息处理器
前面分析消息循环时已经分析了空闲消息,这里来空闲消息的一些结构
1 | /** |
空闲消息处理器是一个接口,消息循环每次到空闲时间时,就会调用每个注册的空闲消息处理器的queueIdle,相当于派发了空闲消息
添加空闲消息处理器是MessageQueue的两个成员函数
1 | public void addIdleHandler(@NonNull IdleHandler handler) { |
看完了Android的消息机制的基础设施之后,看一下Android是怎么在线程中使用的
应用程序主线程
ActivityThread启动之后会进入main函数,去掉其他的代码,就这两句就创建了主线程的Looper并在最后进入的消息循环
1 | public static void main(String[] args) { |
纯任务线程
一般的子线程跟Java一样,使用Thread描述,不过我们一般是实现Thread的一个子类,然后用这个Thread子类来创建一个Android应用程序子线程,
1 | public class SubThread extends Thread{ |
带消息循环的与界面无关的子线程
Android中提供的是HandlerThread类
1 | public class HandlerThread extends Thread { |
HandlerThread是继承Thread的,当我们新建一个HandlerThread线程并start时,run函数就会被调用,这里可以看到HandlerThread新建了一个looper,然后在最后进入了消息循环
我们可以定义一个实现了Runnable接口的类,例如ThreadTask类,用来描述想要子线程执行的任务
1 | public class ThreadTask implements Runnable{ |
还记得前面说过post会把callback封装到msg里面
带消息循环的与界面相关的子线程
Android提供了一个异步任务类AsyncTask,来将一个涉及界面操作的任务放在子线程中执行
以一个CounterService来作为案例
1 | public class CounterService extends Service implements ICounterService { |
在AsyncTask中doInBackground是真正干的任务,当异步任务执行完成之后,会将返回值分发给成员函数onPostExecute来处理,
接下来详细看一下AsynTask,AsyncTask有三个类型的参数Params、Progress和Result
1 | public abstract class AsyncTask<Params, Progress, Result> { |
sPoolworkQueue,里面存的都是实现了Runnable接口的对象,就知道其是公国队列
只不过LinkedBlockingQueue有一些特性,一个线程试图从空的LinkedBlockingQueue取出工作任务就会被阻塞,一个线程如果试图往一个满的LinkedBlockingQueue中写入工作任务也会被阻塞
sThreadFactory是用来创建执行工作任务的线程的
1 | public static final Executor THREAD_POOL_EXECUTOR; |
threadPoolExecutor是一个线程池,其创建的时候传入到最后两个参数sPoolWorkQUeue和sTHreadFactory就是给线程池指定了工作队列和线程工厂,即用sThreadFactory创建工作线程,执行sPoolWorkQueue中的任务
CORE_POOL_SIZE和MAXIMUM_POOL_SIZE指定的是核心线程数量和线程池中最大的线程数量
由于上面关键的成员变量都是静态成员变量,所以其实在同一个应用程序进程中,所以异步任务类AsyncTask都是在一个线程池中执行的,这有助于减少创建线程池时的消耗
1 | //指向的一个Handler对象是在应用程序的主线程中创建的 |
这里sHandler是static成员,所以会在AsyncTask第一次被应用程序使用时被创建,如果应用程序第一次使用AsyncTask是运行在主线程,那么这里创建的Handler就可以被用来向主线程的消息队列发送消息
这里虽然InternalHandler没有默认构造函数,但是其继承的Handler,Handelr是有默认构造函数的
1 | public Handler() { |
handleMessage可以处理两类消息MESSAGE_POST_PROGRESS和MESSAGE_POST_RESULT
这里msg.obj指向的是一个AsyncTaskResult对象,其是在AsyncTask的运行过程中传递数据的
1 | //mTask描述一个宿主异步任务 |
了解了AsyncTask的基本组件之后,看一下AsyncTask的构造函数
1 | //创建一个异步任务 |
这里也就得知AsyncTask创建完成后,会在内部获得一个WorkerRunnable和FutureTask对象
前面知道Service里面startCounter使用execute来启动异步任务的
异步任务的执行,传入一个默认的Executor和一个参数
1 | private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; |
最后是调用的executor的execute,这里默认的executor是一个SerialExecutor,主要用来线性处理工作任务
经过SerialExector中转一下还是用的Thread_Poll_Execute去执行任务
mActive是当前活动的任务,poll是取出任务,然后 还是调用Thread_Pool_Execute去执行execute
这个线程池就是前面介绍的,线程池的execute
如果当前线程数量小于核心线程数量,就新建一个线程,并把任务添加到新线程里面
即便成功插入到了工作队列,也得检查一下是否需要新建线程
如果我们不能插入task,应该尝试添加一个新线程
PS:这里我分析的不是很细,只是想粗略的先找到调用路径,这里start应该就是线程开始执行任务
1 | private boolean addWorker(Runnable firstTask, boolean core) { |
结合前面,调用的就是传入的Runnable的run函数
而传入的Runnable是mFuture,mFuture在构造的时候传入的mWorker,所以这里就调用到mWorker的call函数
1 | 133 public FutureTask(Callable<V> callable) { |
再回头来看mworker,这里就找到了调用doInBackground的路径
1 | //WorkRunnable继承自Callback类,即将要执行的工作任务 |
publishProgress
上面分析的AsyncTask的任务的执行流程,下面看一下其是怎么执行与界面相关的操作的
执行界面操作需要调用publishProgress函数
1 |
|
getHandler得到的是mHandler,而mHandler是构造的时候赋值的,指向MainLooper
1 | /** |
obtain创建一个Message,并设置其target为传进来的Handler参数,然后sendToTarget其实还是调用Handler去发送的Message,这里封装的还是一个MESSAGE_POST_PROGRESS
而Handler是指向主线程的,就完成了向主线程发送消息,等到主线程派发消息时,就会来到Handler的handleMessage,就会分发给onProgressUpdata处理
1 |
|
1 |
|
也就是说虽然都是AsyncTask中的代码,子线程调用publishProgress,onProgressUpdate是在主线程中做的处理,counterCallback.count的工作就是更新界面上的计数器