学习内容

  • Android 的消息机制
  • Handler 即其底层支撑

原文开篇部分:

  1. 从开发角度来说,Handler 是 Android 消息机制的上层接口,通过它可以将任务切换到 Handler 所在的线程中执行。
  2. 更新 UI 仅仅是 Handler 的一个特殊的使用场景。本质不是专门用来更新 UI,只是常被用来这么做而已。
  3. Andorid 的消息机制主要指 Handler 的运行机制,Handler 的运行底层的 MessageQueueLooper 的支撑。
    1. MessageQueue(消息队列):采用单链表的数据结构存储消息列表,以队列的形式对外提供插入和删除的工作。只负责存储,不管处理。
    2. Looper 负责处理消息:以无限循环的形式查找是否有新消息,有则处理,无则等待。
    3. ThreadLocal 可以在把不同的线程中互不干扰的存储提供数据,通过它来获取每个线程中的 Looper
  4. 线程默认没有 Looper,因此如果需要使用 Handler 必须先为线程创建 Looper。UI 线程(ActivityThread)创建时会初始化 Looper,因此默认可以在主线程中使用 Handler。

Android 的消息机制概述

1.Handler 的作用:

  • 将一个任务切换到某个制定的线程中执行。

2.为什么提供上述 Handler 的功能?

  • Android 规定访问 UI 只能在主线程中执行,如果在子线程中访问 UI,那么程序会抛出异常。
  • 以上限制,源码中 ViewRootImpl 对 UI 操作进行验证,通过 chearThread 方法实现。
  • 同时由于 Android 的限制,导致必须在主线程中访问 UI,但是如果在主线程中进行耗时操作,可能会导致 ANR,因此。系统提供 Handler,主要原因就是为了解决在子线程中无法访问 UI 的矛盾。(子线程进行耗时操作,通过 Handler 访问 UI)

3. Android 为什么不允许子线程访问 UI?

  • 核心点:Andorid 的 UI 控件并非线程安全。
  • 通常方法及其问题:
    • 对 UI 控件的访问加入*上锁机制
    • 问题:使 UI 访问的逻辑变得复杂;锁机制会降低 UI 访问的效率,因为锁机制会阻塞某些线程的执行。
  • 解决方案:
    • 采用单线程模型来处理 UI 操作
    • 通过 Handler 切换 UI 访问的执行线程即可

4.Handler 的工作原理:

  • Handler 创建时会采用当前线程的 Looper 来构建内部的消息循环系统,当前线程没有 Looper 会报错
  • 创建完毕后,通过 Handler 的 post 方法将 Runnable 投递到 Handler 内部的 Looper 中去处理;或者通过 Handler 的 send 方法发送消息,该消息也会在 Looper 中处理。实际 post 最后也会调用 send 方法。
  • send 方法的工作过程:调用 send 方法后,它会调用 MessageQueue 的 enqueueMessage 方法将该消息放入消息队列中,然后 Looper 发现新消息后,会处理这个消息,最终消息中的 Runnable 或者 Handler 的 handleMessage 方法就会被调用。
    • 注意:Looper 运行在创建 Handler 所在的线程中,这样一来 Handler 中的业务逻辑就被切换到创建 Handler 所在的线程中去了。

Android 的消息机制分析

ThreadLocal 的工作原理

1.基本定义

  • ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储后,只有在制定线程中可以获取到存储的数据,而其他线程则无法获取到数据。
  • why ?(为什么通过 ThreadLocal 可以在不同的线程中维护一套数据的副本并且彼此互不打扰?)
    • (简单说明) 不同线程访问同一个 ThreadLocal 的 get 方法,ThreadLocal 内部会从各自的线程中取出一个数组,然后再从数组中根据当前 ThreadLocal 的索引去查找对应的 value 值。显然,不同线程中的数组是不同的,这也就是原因所在。

2.使用场景

  1. 存储以线程为作用于并且不同县城具有不同的数据副本的数据
    1. 如:通过 ThreadLocal 存储不同线程中的 Looper,供 Handler 获取当前线程的 Looper
  2. 复杂逻辑下的对象传递,比如监听器的传递。
    1. 复杂逻辑:比如 函数调用栈比较深以及代码入口的多样性。
    2. 采用 ThreadLoacl 让监听器作为线程内的全局对象存在,而线程内部只要通过 get 方法就可以获取到监听器。

3.原理

  1. ThreadLocal 是一个泛型类,定义为 public class ThreadLocal,核心在于 ThreadLocal 的 get 和 set 方法。

  2. set 方法:

    ​ (lz这里查看了一下现版本的源码,发现和书中有些许差异)

    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

    ​ 虽说和书中源码有出入,但是大体思想是一致的:通过一个结构 ThreadLocalMap 存储线程的 ThreadLocal 数据。

    ​ ThreadLocalMap 部分代码如下:

    static class ThreadLocalMap {        static class Entry extends WeakReference> {            /** The value associated with this ThreadLocal. */            Object value;            Entry(ThreadLocal<?> k, Object v) {                super(k);                value = v;            }        }        private Entry[] table;       //...

    ​ 内部的这个 private Entry[] table 数组即用来存储 ThreadLocal 的值。

    ​ 回到上面 ThreadLocal 的 set 方法,如果ThreadLocalMap 为空,则创建该线程的 ThreadLocalMap 对象,并将 ThreadLocal 的值存储。如果非空,则直接通过 ThreadLocalMap.set 方法存储ThreadLocal 的值,ThreadLocalMap.set 的源码如下:

    private void set(ThreadLocal<?> key, Object value) {            Entry[] tab = table;            int len = tab.length;            int i = key.threadLocalHashCode & (len-1);            for (Entry e = tab[i];                 e != null;                 e = tab[i = nextIndex(i, len)]) {                ThreadLocal<?> k = e.get();                if (k == key) {                    e.value = value;                    return;                }                if (k == null) {                    replaceStaleEntry(key, value, i);                    return;                }            }            tab[i] = new Entry(key, value);            int sz = ++size;            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash();        }

    ​ ThreadLocal 的值直接存储在 Entry.key 匹配的 Entry.value 中。(和书中有所差异,并非凭借索引位置)

  3. set 方法介绍完,下面介绍 get 方法:

    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }

    ​ get 方法实际就获取 ThreadLocalMap 对象,如果非空,则调用 ThreadLocalMap.getEntry 方法,得到目标 Entry 对象,该 Entry 对象的 value 字段即为 ThreadLocal 值。如果 ThreadLocalMap 为空,那么返回初始值,该初始值可通过重写 initialValue 方法更改。

    ​ ThreadLocalMap.getEntry 方法如下:

    private Entry getEntry(ThreadLocal<?> key) {            int i = key.threadLocalHashCode & (table.length - 1);            Entry e = table[i];            if (e != null && e.get() == key)                return e;            else                return getEntryAfterMiss(key, i, e);        }

    ​ 可以看到,逻辑很清晰,取出 table 数组,并找出 ThreadLocal 的引用对象所在的 Entry ,返回之。

  4. 小结

    1. 操作对象是当前线程的 ThreadLocalMap 内的 private Entry[] table 数组,读写仅限于各自线程的内部,
    2. 因此多个线程可以互不干扰的存储和修改数据。

消息队列的工作原理

1.基本介绍

  • 消息队列在 Android 中指的是 MessageQueue,内部通过一个单链表的数据结构来维护消息列表(方便插入和删除)

2. 原理

  1. enqueueMessage 方法:向消息队列中插入一条消息。
  2. next 方法:无限循环的方法。从消息队列中取出一条消息并将其从消息队列中移除。如果消息队列中没有消息,那么 next 方法会一直阻塞。

Loop 的工作原理

1.基本介绍

  • Looper 在 Android 的消息机制中扮演着消息循环的角色,具体来说就是它会不断的从 MessageQueue 中查看是否有新消息,如果有新消息就会立刻处理,否则就会一直阻塞在那里。
  • Handler 的工作需要 Looper,没有 Looper 的线程会报错

2.原理

  1. 构造方法:创建一个 MessageQueue,然后保存当前线程的对象。
  2. 通过 Looper.prepare() 为当前线程创建一个 Looper,然后就可以创建 Handler 的对象了,接着通过 Looper.loop() 方法来开启消息循环。
  3. 除 prepare 外,还有一个 prepareMainLooper 方法,主要是给主线程 ActivityThread 创建 Looper 使用的,本质也是通过 prepare 方法实现的。对应的,系统提供 getMainLooper 方法来在任意位置获取主线程的 Looper。
  4. Looper 终止:
    1. quit 方法:直接退出 Looper
    2. quitSafely 方法:设定退出标记,当消息队列中的已有消息处理完毕后安全地退出。
    3. Looper 退出后,通过 Handler 发送的消息会失败,此时 Handler 的 send 方法会返回 false。子线程中,如果手动创建了 Looper,那么所有事情做完后,应当调用 quit 方法来终止消息循环,否则这个子线程会一直处于等待的状态,而如果退出 Looper 以后,这个县城就会立刻终止,因此 建议不需要的时候终止 Looper
  5. Looper.loop 方法
    1. 调用了 loop 方法后,消息循环系统才会 真正的起作用。
    2. 工作过程:
      1. loop 方法是一个死循环,当且仅当 MessageQueue.next 方法返回了 null 时,跳出循环。当 Looper 的 quit 方法被调用时,Looper 就会调用 MessageQueue 的 quit 或者 quitSafely 方法来通知消息队列退出,此时,next 的方法就会返回 null。
      2. loop 方法会调用 MessageQueue 的 next 方法来获取新消息,当没有信息时,next 会阻塞,这也导致 loop 方法一直阻塞。
      3. 当 next 方法返回新消息时,Looper 就会处理这条新消息:msg.target.dispatchMessage(msg),这里的 msg.target 是发送这条消息的 Handler 对象,这样 Handler 发送的消息最终又交给它的 dispatchMessage 方法来处理了。
      4. Handler 的 dispatchMessage 方法是在创建 Handler 时所使用的 Looper 中执行的,这样就将代码逻辑切换到制定的线程中了。

Handler 的工作原理

1.原理

Handler 的工作包含消息的发送和接收过程。

  1. 消息的发送:

    1. 通过 post 的一系列方法和 send 的一系列方法来实现,post 相关方法最终是通过 send 相关方法来实现的。
    2. Handler 发送消息只是向消息队列中插入一条消息,MessageQueue 的 next 方法就会返回这条信息给 Looper,Looper 收到消息后就会开始处理这条消息,最终消息由 Looper 交由 Looper 处理,即 Handler 的 dispatchMessage 方法会被调用,此时进入处理消息的阶段。
  2. 消息的处理

    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

    流程如下:

    1. 首先,检查 Message 的 callback 是否为 null,不为null 就通过 handlerCallback 来处理消息。Message 的 callback 是一个 Runnable 对象,实际上就是 Handler 的 post 方法所传递的 Runnable 参数。

      private static void handleCallback(Message message) {        message.callback.run();    }
    2. 其次,检查 mCallback 是否为 null,不为 null 就调用 mCallback 的 handleMessage 方法来处理消息。

      public interface Callback {        /**         * @param msg A {@link android.os.Message Message} object         * @return True if no further handling is desired         */        public boolean handleMessage(Message msg);    }

      通过 Callback 可以如下方式创建 Handler 对象:Handler handler = new Handler(callback)。这样做的意义在于提供另外一种无需派生 Handler 子类并重写其 handlerMessage 方法处理具体消息的 Handler 的使用方式。

    3. 最后,调用 Handler 的 handlerMessage 方法来处理消息。只是一个空方法,需要我们派生 Handler 子类时,重写该方法。

      public void handleMessage(Message msg) {    }
  3. 补充:

    1. Handler 还有一个特殊的构造方法,通过一个特定的 Looper 来构造 Handler

      public Handler(Looper looper) {        this(looper, null, false);    }public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

      原文中说“通过这个构造方法可以实现一些特殊的功能”,然而因为我接触事件尚短,不清楚具体能做些什么。。。

    2. 默认构造方法中对 Looper 对象进行判断,如果为空的话,那么就会报这个常见的异常"Can't create handler inside thread that has not called Looper.prepare()"。

      public Handler() {        this(null, false);    }public Handler(Callback callback, boolean async) {                mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

主线程的消息循环

消息循环模型

  1. Android 的主线程 ActivityThread 的入口方法 main 中,会通过 Looper.prepareMainLooper 来创建主线程的 Looper 以及 MessageQueue,并通过 Looper.loop 方法来开启主线程的消息循环。
  2. 主线程的消息循环开始之后,ActivityThread 需要一个 Handler 来和消息队列进行交互,这个 Handler 就是 ActivityThread.H,内部定义了一组消息模型,主要包含四大组件的启动和停止等过程。
  3. ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后回调 ApplicationThread 中的 Binder 方法,然后 ApplicationThread 会向 H 发送消息,H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行,即切换到主线程中执行。
  4. 以上即为主线程的消息循环模型。

更多相关文章

  1. 如何提高Android的性能
  2. android 线程优先级设置
  3. 两分钟彻底让你明白Android(安卓)Activity生命周期(图文)!
  4. 【移动安全高级篇】————7、APK 的自我保护
  5. Android推送通知指南
  6. Handler Looper源码解析(Android消息传递机制)
  7. Android任务切换方法
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. 编译自己的windows版本android sdk
  2. Android(安卓)Activity应用窗口的创建过
  3. Android(安卓)API Guides---Administrati
  4. AppWidget开发实例讲解(一)
  5. Android中结合OrmLite for android组件对
  6. 【讲座】It's Android(安卓)Time:程序员创
  7. Android(安卓)开发之android架构
  8. Android仿人人客户端(v5.7.1)——新鲜事之
  9. android单元测试AndroidTestCase
  10. Android(安卓)播放Gif 动画