Android中的消息处理机制概述
大家对于Android中的消息处理机制的用法一定都比较熟悉,至于工作原理估计不少人有研究。就像我们自己写的类我们用起来比较熟悉一样,如果我们熟悉了消息处理机制的具体实现,那么我们用起来肯定也会事半功倍。
博主之前只是稍有涉猎,对其中一些地方也还心存疑虑,比如既然Looper.loop()里是一个死循环,那它会不会很消耗CPU呢?死循环阻塞了线程,那我们其他的事务是如何被处理的呢?Android的UI线程是在哪里被初始化的呢?等等。索性今天就把他们放到一起,说道说道。
Android中线程的分类
- 带有消息队列,用来执行循环性任务(例如主线程、android.os.HandlerThread)
- 有消息时就处理
- 没有消息时就睡眠
- 没有消息队列,用来执行一次性任务(例如java.lang.Thread)
- 任务一旦执行完成便退出
带有消息队列线程概述
四要素
- Message(消息)
- MessageQueue(消息队列)
- Looper(消息循环)
- Handler(消息发送和处理)
四要素的交互过程
具体工作过程
- 消息队列的创建
- 消息循环
- 消息的发送最基本的两个API
- Handler.sendMessage
- 带一个Message参数,用来描述消息的内容
- Handler.post
- 带一个Runnable参数,会被转换为一个Message参数
- Handler.sendMessage
- 消息的处理
基于消息的异步任务接口
- android.os.HandlerThread
- 适合用来处于不需要更新UI的后台任务
- android.os.AyncTask
- 适合用来处于需要更新UI的后台任务
带有消息队列线程的具体实现
ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
Looper
用于在指定线程中运行一个消息循环,一旦有新任务则执行,执行完继续等待下一个任务,即变成Looper线程。Looper类的注释里有这样一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class LooperThread extends Thread { public Handler mHandler; public void run() { //将当前线程初始化为Looper线程 Looper.prepare(); // ...其他处理,如实例化handler mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; // 开始循环处理消息队列 Looper.loop(); } } |
其实核心代码就两行,我们先来看下Looper.prepare()方法的具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public final class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class //Looper内的消息队列 final MessageQueue mQueue; // 当前线程 final Thread mThread; private Printer mLogging; private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //试图在有Looper的线程中再次创建Looper将抛出异常 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw/span>sMainLooper != null) { throw究。就像我们自己写的类我们用起来比较熟悉一样,如果我们熟悉了消息处理机制的具体实现,那么我们用起来肯定也会事半功倍。
博主之前只是稍有涉猎,对其中一些地方也还心存疑虑,比如既然Looper.loop()里是一个死循环,那它会不会很消耗CPU呢?死循环阻塞了线程,那我们其他的事务是如何被处理的呢?Android的UI线程是在哪里被初始化的呢?等等。索性今天就把他们放到一起,说道说道。 Android中线程的分类
带有消息队列线程概述四要素
四要素的交互过程具体工作过程
基于消息的异步任务接口
带有消息队列线程的具体实现ThreadLocalThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。 Looper用于在指定线程中运行一个消息循环,一旦有新任务则执行,执行完继续等待下一个任务,即变成Looper线程。Looper类的注释里有这样一个例子:
其实核心代码就两行,我们先来看下Looper.prepare()方法的具体实现
|