第10章 Android的消息机制


文章目录

        • 1. 前因
        • 2. 工作原理
        • 3. ThreadLocal
        • 3. 消息队列MessageQueue
        • 4. Looper
        • 5. Handler
        • 6. 主线程的消息循环
        • 7. 使用示例
        • 8. 其他

1. 前因 Handler的主要作用是讲一个任务切换到某个指定的线程中工作,主要用于更新UI,因为Android规定访问UI只能在主线程中进行(Android的UI控件不是线程安全的,上锁机制会让UI的访问逻辑复杂,且锁机制会降低UI访问效率,所以最简单高效的方法就是用单线程模型来处理UI操作)。
2. 工作原理 Handler创建时会采用当前线程的Looper来构建内部的消息循环系统。
通过Handler的post方法将一个Runnable或通过send方法发一个消息到Looper中,post方法最终也是调到send,send方法调用MessageQueue的enqueueMessage方法将消息放入消息队列,然后Looper发现会进行处理,最终消息中的Runnable或者Handler的handleMessage会被调用,由于Looper是运行在创建Handler所在的线程中的,这样Handler中的业务逻辑就被切换到创建Handler的线程中去执行了。
3. ThreadLocal 【第10章 Android的消息机制】ThreadLocal 是一个线程内部的数据存储类,通过它可以再指定的线程中存储数据,Looper、ActivityThread、AMS中都有用到。有 set 和 get 方法
使用场景分析:
1)对于Handler来说,它需要获取当前线程的looper,显然Looper的作用域就是线程并且不同的线程有不同的Looper,这时就可以使用ThreadLocal实现Looper在线程中的存取。
2)复杂逻辑下的对象传递,比如监听器的传递,采用ThreadLocal可以让监听器作为线程内的全局对象而存在。
3. 消息队列MessageQueue 是一个单链表
1)插入 enqueueMessage,单链表的插入
2)读取并删除 next,是一个无限循环的方法,如果消息队列中没有消息,next方法会一直阻塞在这里,当新消息到来时,next方法会返回这条消息并将其从单链表中移除
3)这里创建Message对象的时候,有三种方式:
Message msg1 = new Message(); // 就是直接初始化一个Message对象 Message msg2 = Message.obtain(); Message msg3 = handler.obtainMessage();

2、3种方式一样,从整个Messge池中返回一个新的Message实例,Messge池为空才创建新的对象,能避免重复Message创建对象,源码:
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }

4. Looper 不停的从MessageQueue中查看是否有新消息,如果有立刻处理,否则阻塞在那里。
1)构造方法:创建一个MessageQueue;将当前线程的对象保存起来
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

2)创建Looper:Looper.prepare()为当前线程创建一个Looper,Looper.loop() 开启消息循环
new Thread("123") { @Override public void run() { super.run(); Looper.prepare(); Handler handler = new Handler(); Looper.loop(); }; }.start();

3)Looper.getMainLooper() 获取主线程Looper
4)退出:quit 和 quitSafely
quit直接退出,quitSafely设定一个退出标记,等消息队列中已有的消息处理完毕后退出。
Looper退出后,Handler发送消息会失败,send返回false。
子线程如果手动创建Looper在所有事情完成后记得用quit终止循环,退出Looper后,这个线程也立即终止。
5)loop方法:死循环,唯一跳出循环的方式是MessageQueue的next方法返回null
Looper的quit方法调用,会调用MessageQueue的quit或quitSafely方法通知消息队列退出,此时next返回null
6)loop 方法调用MessageQueue的next方法获取新消息,而next是一个阻塞操作,当没有消息时,next会一直阻塞在那里,导致loop也一直阻塞在那里;如果MessageQueue的next返回了新消息,Looper就会处理这条消息:
msg.target.dispatchMessage(msg),这里的msg.target 是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage来处理了,注意,Handler的dispatchMessage方法是在创建 Handler时所使用的Looper中执行的,这样就成功将代码逻辑切换到指定的线程中去执行了。
5. Handler 发送消息和接收
1)发送通过post或者send,post最终也是通过send,发送消息的过程就是向消息队列中插入一条消息,MessageQueue的next方法会将这条消息给Looper
2)Looper收到消息后开始处理,最终交由Handler
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }

首先检查 Message的callback是否为null,不为bukk就通过handleCallback 来处理消息,Message的callback 是一个 Runnable 对象,实际上就是Handler 的 post方法所传递的Runnable参数
private static void handleCallback(Message message) { message.callback.run(); }

其次检查mCallback 是否为null,不为null就调用 mCallback 的 handleMessage 方法。
Callback 是一个接口,可以用来创建一个Handler的实例但不需要派生Handler的子类
Handler handler = new Handler(callback);

最后调用Handler 的 handleMessage 方法来处理消息。
3)Handler 还可以通过一个Looper来构造
public Handler(Looper looper) { this(looper, null, false); }

6. 主线程的消息循环 Activity 的 主线程就是 ActivityThread,入口为main,main方法中系统通过 Looper.prepareMainLooper() 来创建主线程的 Looper 以及 MessageQueue,并通过 Looper.loop() 来开启主线程的消息循环
ActivityThread.H 是其Handler,定义了一组消息类型,包含了四大组件的启动和停止等过程
ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后会回调 ApplicationThread 中的Binder方法,然后 ApplicationThread 会向 H发送消息,H收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型
7. 使用示例 1)在主线程创建
// 方法1 使用内部类方式创建Handler class mHandler extends Handler {// 复写handlerMessage() @Override public void handleMessage(Message msg) { ...// 需执行的UI操作} }// 方法2 使用匿名内部类创建Handler Handler handler = new Handler(){// 复写handlerMessage() @Override public void handleMessage(Message msg) { //msg就是子线程发送过来的消息。 } }; //开启一个子线程 发消息方式1 new Thread(new Runnable() { @Override public void run() { //在子线程发送一个消息。 Message msg = new Message(); handler.sendMessage(msg); } }).start(); //开启一个子线程 发消息方式2 new Thread(new Runnable() { @Override public void run() { //在子线程post一个Runnable对象 handler.post(new Runnable() { @Override public void run() { //这里是消息处理的方法 //这里运行在主线程。 } }); } }).start();

注意:Handler的内存泄漏
as不建议我们用上述方式生成Handler,这是应为非静态内部类会持有外部内的引用。那么Handler将会持有Activity的引用,我们知道handler是会被msg.target持有的,而msg又在MessageQueue队列中,那么当消息队列中拥有未消费的Message时,会导致Activity即使finish了也无法被GC回收,最终导致内存泄漏。为了避免这个问题我们可以将Handler写成外部类或者静态的内部类,并且传递的Activity引用可以用WeakReference弱引用来持有,同时可以在Activity的onDestory中使用Handler.removeCallbacksAndMessages(null); 来清空消息队列
2)在子线程创建方式1 使用子线程的Looper 消息处理在子线程
// 声明Handler Handler handler; new Thread(new Runnable() { @Override public void run() { // 创建当前线程的Looper Looper.prepare(); // 在子线程创建handler对象 handler = new Handler() { @Override public void handleMessage(Message msg) { // 这里是消息处理,它是运行在子线程的 } }; // 开启Looper的消息轮询 Looper.loop(); } }).start(); mBanner.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 在主线程发送一个消息到子线程 Message msg = new Message(); handler.sendMessage(msg); } });

3)在子线程创建方式2 使用主线程的Looper 消息处理在主线程
new Thread(new Runnable() { @Override public void run() { // 获取主线程的Looper Looper looper = Looper.getMainLooper(); // 用主线程的Looper创建Handler handler = new Handler(looper) { @Override public void handleMessage(Message msg) { // 这里是运行在主线程的 } }; } }).start();

4)使用 Callback接口
public class MyClass implements Handler.Callback { ... private Handler handler; // 主线程的Looper handler = new Handler(Looper.getMainLooper(), this); // 子线程的Looper HandlerThread handlerThread = new HandlerThread("subthread"); handlerThread.start(); handler = new Handler(handlerThread.getLooper(), this); @Override public boolean handleMessage(Message msg) { ... } }

8. 其他 **1)Android已经提供了很多实现了Handler的类和方法,方便我们使用。**如Activity类的runOnUiThread()方法,View的post()方法,HandlerThread类等,关于这些知识,有待补充
  • runOnUiThread
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }

  • postInvalidate底层是使用了Handler,向主线程发消息更新UI
  • HandlerThread 是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例
2)Android中为什么主线程不会因为Looper.loop()里的死循环卡死
3)一个线程只能有一个Looper
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"); } sThreadLocal.set(new Looper(quitAllowed)); }

本文参考:
《Android开发艺术探索》第十章
https://juejin.im/post/5910533dac502e006cfe01cd

    推荐阅读