前记

Android 中无法在子线程中直接更新 UI,通过 Handler 提供的消息处理函数可以实现子线程 UI 更新。那么 Handler 究竟是啥。Handler 这货其实和 Looper 有一腿,即使平常在使用 Handler 的时候不用直接引用 Looper,但是,但是,但是,相信他们没办法切断这姻缘。

Android 的消息驱动模型

Android 深入了解 Handler 和 Looper_第1张图片

Android 同大都数系统相同,使用基于消息驱动的模型。即由事件源(用户点击按钮、滑动列表 etc.)产生消息并投递到消息队列,消息处理器取出消息并进行分发、处理。

窥窃 Looper

好了,有了消息驱动模型的概念,现在开始主题。即使你有多不情愿了解 Looper,但还是从这里开始吧。

简单来讲,Looper 中维护一个消息队列,消息被投递到消息队列当中,并由 Looper 实现消息分发。

先来看一个 Looper 使用的例子:

class Fuck extends Thread {    @Override    public void run() {        // 投递消息前需要调用该函数        Looper.prepare();        // 投递消息到 Looper...        // 分发、处理消息        Looper.loop();    }}

翻开 android.os.Looper.java,我们对四个变量感兴趣:

public final class Looper {...static final ThreadLocal sThreadLocal = new ThreadLocal();private static Looper sMainLooper;final MessageQueue mQueue;final Thread mThread;...}
  1. ThreadLocal ThreadLocal 是 Java 提供的一个类,可以关联(保存和读取)一个当前线程的变量,其他线程无法跨越线程对该变量进行访问,具体参考 Oracle 文档。
  2. Looper 应用程序主线程(即 UI 进程)的 Looper 实例(找到真面目www)。
  3. MessageQueue Looper 中维护的消息队列
  4. Thread 与 Looper 关联的线程实例

Looper - 消息准备阶段

在使用 Looper 前需要调用 prepare 函数,看看其实现:

public static void prepare() {    prepare(true);}private static void prepare(boolean quitAllowed) {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    // 初始化 Looper 实例,并保存到本地线程变量中    sThreadLocal.set(new Looper(quitAllowed));}}

该函数生成一个新的 Looper 对象,并将其绑定到本地线程变量(Thread Local Variable, TLV)中。

扒开构造函数:

private Looper(boolean quitAllowed) {    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}

构造函数内 生成新的消息队列并和当前线程关联起来

Looper - 消息处理阶段

当消息投递到 Looper 的消息队列中后,使用 loop 函数对消息进行分发处理:

public static void loop() {    // 获取当前线程 Looper 实例    final Looper me = myLooper();    ...    // 取出所有消息    for (;;) {        Message msg = queue.next(); // might block        if (msg == null) {            // 消息为空,直接返回            return;        }        ...        // 交由该消息绑定的 Handler 进行分发,即提交到对应的函数进行处理        msg.target.dispatchMessage(msg);        ...    }}

第一句获取当前 Looper 对象,其实就是在 prepare 函数中被保存到 ThreadLocal 中的对象(这里要细心记下来,后面会由此提出问题,从而更加深入了解):

public static @Nullable Looper myLooper() {    return sThreadLocal.get();}

Handler 的实现

接下来进入我们钟爱的 Handler,先回忆一下我们平常如何在子线程中更新 UI:

class FuckActivity extends Activity {    private static final int MSG_DO_WTF_YOU_WANT = 0x1;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch(msg.what) {                case MSG_DO_WTF_YOU_WANT:                    // 更新 UI                    break;            }        }    };    public void shit() {        new Thread(new Runnable() {            @Override            public void run() {                // 创建或获取一个与 mHandler 关联的消息对象                Message msg = mHandler.obtainMessage();                // 投递到消息队列进行 UI 更新                msg.what = MSG_DO_WTF_YOU_WANT;                msg.sendToTarget();            }        }).start();    }}

翻开 android.os.Handler.java,看看 Handler 的构造函数:

public Handler() {    this(null, false);}public Handler(Callback callback, boolean async) {    ...    // 获取当前线程的 Looper 对象    mLooper = Looper.myLooper();    if (mLooper == null) {        throw new RuntimeException(        "Can't create handler inside thread that has not called Looper.prepare()");    }    // 获取消息队列    mQueue = mLooper.mQueue;    ...}

回忆一下 Looper 的处理流程:调用 prepare 函数初始化一个 Looper 对象,关联当前线程,之后调用 loop 函数处理消息

乍看之下,我们在初始化 Handler 的时候并没有引用 prepare 函数,那么在 loop 处理消息的时候 Looper 对象从哪里来?

其实这里是一个大坑

你可以简单的了解到:Looper 对象是在 Acitivty 中维护的,即主线程关联一个默认的 Looper 对象。

如果你意犹未尽,想扒开 Activity 中实现 Looper 的那层皮,好,这边走:

Android 深入了解 Handler 和 Looper_第2张图片

图片引用自http://multi-core-dump.blogspot.com/2010/04/android-application-launch.html

这里涉及到 Android 进程的启动过程(当然简单过一下)。Android 底层基于 Linux,当系统启动时,初始化 init 进程,并由 init 进程启动 Zygote 进程。之后 Zygote 初始化工作:启动、初始化虚拟机,并启动 system_server 对象。

有了 system_server,等待一个事件触发(启动 Activity、接收 Broadcast etc.),system_server 通过 Zygote fork 一个新应用进程,并调用新进程中 android.app.ActivityThread.java 的 main 函数:

public static void main(String[] args) {    ...    // 到了!!!    Looper.prepareMainLooper();    ...}

终于到了实现的地方,接下来回到 Looper 的 prepareMainLooper 函数:

public static void prepareMainLooper() {    // 万物始于 prepare ~~    prepare(false);    synchronized (Looper.class) {        if (sMainLooper != null) {            throw new IllegalStateException("The main Looper has already been prepared.");        }        // 现在 sMainLooper 这第四张王牌也用上了        sMainLooper = myLooper();    }}

至此,Activity 主线程终于有一个 Looper 实例。在 Activity 线程中初始化 Handler 时获取到该实例,之后的消息被投递到该 Looper 的消息队列,即主 UI 线程的消息队列,最后由 Handler 分发处理

关于此处的大坑,别打我。提供参考资料:

  1. Colt’s Blog: Android OS - Processes and the Zygote!
  2. ./multi_core_dump: Android Application Launch
  3. 《深入了解 Android 卷I》

最后

其实 Handler 中还有其他关于消息操作的函数,具体可以参考 Android 源码。到此总结:Looper 维护一个线程的消息队列,Handler 从该线程实例化后,通过重写相关函数实现消息处理

并且:Activity 线程中维护一个默认的 Looper 对象

然而这并没有什么卵用。这里写图片描述

更多相关文章

  1. C语言函数以及函数的使用
  2. Android——进程与线程
  3. [Android] Android进程和线程模型
  4. android 自定义线程池ThreadPoolUtils工具类
  5. Android应用程序键盘(Keyboard)消息处理机制分析(28)
  6. Android进程与线程基本知识四
  7. android 里使用Socket进行发送消息案例
  8. Android实现自己的回调函数

随机推荐

  1. Android从网上下载文件
  2. android图片缩放(指定大小)
  3. android实现Parcelable序列化对象
  4. Android(安卓)修改host文件的3种方法
  5. 关于如何使用Mumu模拟器连接电脑adb
  6. Android 自定义布局对话框避免全屏的设置
  7. Android设备内存和SD卡操作工具类
  8. 【Android-Activity】EditText的基本属性
  9. Android各个版本对应的源代码
  10. Android(安卓)Widget开发系列(二)