博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从源码角度理解Handler、Looper、MessageQueue之间关系
阅读量:6984 次
发布时间:2019-06-27

本文共 5106 字,大约阅读时间需要 17 分钟。

为什么?

多问为什么是解决并理解问题的最好的办法,Handler是Android SDK中的类,Android为什么要引入Handler、Looper、MessageQueue呢?

线程间通讯,消息队列处理

经常使用的场景有通过Handler更新UI,貌似我们理解中更新UI只能在主线程 这句话好像是对的,其实更准确的说法是更新UI只能在UI线程,即要更新的UI所在ViewRoot创建时候的线程,如果当前ViewRoot是在子线程创建的,更新ViewRoot里面的UI必须在这个子线程中更新,而我们常见的ViewRoot创建都是在主线程,所以把常见的结果当成标准结果是万万不可的。

为什么采用消息队列的形式呢?消息队列的方式可以解决线程堵塞数据混乱等问题。

言归正传

Handler

Handler是用来发送和处理消息的

Handler对象的创建需要绑定一个指定线程的Looper, 即Handler是和线程相关的,如一个Handler绑定的是一个子线程中的Looper,那么这个Handler就不能用来更新UI线程(主线程)。

Looper

Looper是通过for (;;)循环来不停的把MessageQueue中的消息交给Handler处理

MessageQueue

用来存储Message的消息队列

源码分析(类分析)

Handler的构造函数中只有2个基本构造函数

1.未指定Looper

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;    }复制代码

2.指定Looper

public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }复制代码

其中未指定Looper通过mLooper = Looper.myLooper(); 来获取当前线程中的Looper

让我们来看看Looper.myLooper();究竟做了哪些操作 ,打开Looper源码

public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }复制代码

额。。。sThreadLocal是什么鬼,怎么通过他返回一个Looper 继续查看

static final ThreadLocal
sThreadLocal = new ThreadLocal
();复制代码

原来是一个ThreadLocalThreadLocal用于保存某个线程共享变量,当前即为保存线程中Looper变量,通过sThreadLocal来获取当前线程的Looper,在过sThreadLocal没有进行set()的时候sThreadLocal.get() 为空,为空的时候

if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }复制代码

会提示RuntimeException 提示我们在创建对象之前必须先执行Looper.prepare()

那为什么我们经常在使用Handler更新UI的时候没有执行过呢,其实并不是没有执行,只不过并不需要我们来执行,在ActivityThreadmain方法中已经执行过Looper.prepareMainLooper();所以不需要我们执行,系统已经帮我们执行过了
而在子线程中创建Handler 或指定的子线程中的Looper,必须我们手动执行Looper.prepare()不然就会报RuntimeException异常信息
继续查看Looper.prepare()执行了哪些操作

public static void prepare() {        prepare(true);    }private static void prepare(boolean quitAllowed) {        //如果已经存在了Looper也会报异常 所以Looper.prepare()只能必须执行1次        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        //保存在sThreadLocal中        sThreadLocal.set(new Looper(quitAllowed));    }复制代码

继续看new Looper(quitAllowed)的处理

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

创建MessageQueue对象 绑定当前线程

小结

自此三者的关系明朗了起来

Handler绑定了Looper,Looper创建了一个MessageQueue对象
Handler拥有Looper、MessageQueue成员变量

源码分析(如何发处理消息)

通过层层跳转,发送消息都会执行到sendMessageAtTime方法中

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        return enqueueMessage(queue, msg, uptimeMillis);    }复制代码

也就是enqueueMessage(queue, msg, uptimeMillis)方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        //设置msg的target为当前Handler        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }复制代码

这里主要就是设置msg的target为当前Handler然后调用MessageQueue的enqueueMessage(msg, uptimeMillis)方法把此msg存储到消息队列中

到了这里好像结束了,源码分析完了,等等还没消息处理呢,怎么只有发送,没有接收处理 自此Looper.loop()方法登场了

public static void loop() {        //省略部分代码        for (;;) {        Message msg = queue.next(); // might block           //省略部分代码            try {                //执行msg的target(Handler)的dispatchMessage方法                msg.target.dispatchMessage(msg);                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }           //省略部分代码        }    }复制代码

原来for循环不停的取出msg执行msg对于的Handler中的dispatchMessage(msg)方法

public void dispatchMessage(Message msg) {        //如果msg创建的时候设置了callback则会执行callback的run方法        if (msg.callback != null) {            handleCallback(msg);        } else {            //如果在创建Handler的时候设置了callback则会执行callback的handleMessage方法            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            //都没有设置则会执行Handler的handleMessage方法            handleMessage(msg);        }    }复制代码

至此 Handler的发送与消息处理整个流程就解析完毕

总结

线程中使用Handler步骤

1.Looper.prepare() 准备Looper(主线程已经在ActivityThread中执行);
2.new Handler() 创建Handler(注意指定的Looper线程或创建对象所在的线程) 3.Looper.loop()启动Looper 循环取出MessageQueue中的消息交给Handler处理(主线程已经在ActivityThread中执行)

发送消息流程

1.Handler.sendMessageAtTime() 定时发送消息
2.MessageQueue.enqueueMessage()存储消息至消息队列
3.Handler.dispatchMessage(msg);消息的分发处理

转载地址:http://zttpl.baihongyu.com/

你可能感兴趣的文章
maven 工程依赖war包
查看>>
C# 常用文件操作
查看>>
MySQL绿色版5.7以上安装教程
查看>>
PIC中档单片机汇编指令详解(6)
查看>>
JVM是怎么判断不可用对象的
查看>>
Tornado使用mako 模板总结
查看>>
用python 登录 ssh 与 sftp 通过证书登录系统
查看>>
tpcc的测试
查看>>
批处理延时启动的几个方法
查看>>
Struts 体系结构与工作原理(图) .
查看>>
vim + cscope + kscope
查看>>
[Android] android的消息队列机制
查看>>
Xampp中的apache,tomcat无法启动的问题
查看>>
Oracle中表被删除或数据被错误修改后的恢复方法
查看>>
常见TCP端口号
查看>>
请不要轻易使用 is_numberic 加入存在E字母
查看>>
linux下svn迁移
查看>>
android studio下NDK开发
查看>>
SpringBoot基础篇配置信息之配置刷新
查看>>
第十一天:find
查看>>