Handler机制原理解析(一)

我们都知道,在Android中,主线程也叫UI线程是负责界面更新的,子线程或者工作线程适合做网络请求,数据库等耗时操作。如果在主线程中执行耗时操作可能引发ANR异常。那么,按照要求,各线程各司其职,工作完了结果如何让其他线程知道呢?为了解决线程间通信问题,Android为我们提供了一种方案:Handler。接下来我们从使用入手,慢慢分析Handler到底是如何工作的。

1、使用

一般的,我们会在UI线程中new一个Handler对象,并实现handleMessage,这个方法是运行在主线程的,可以进行更新UI操作;而在需要工作线程的时候,可以去创建一个,并在工作线程有了工作结果之后通过Handler对象sendMeassage(这时候是在工作线程),可以将结果封装在Meassage中。发送完之后,就会在handleMeassage方法中执行处理(这里就到了UI线程)。使用起来非常的方便,那么,究竟Android是如何做到在线程间通信的呢?

2、原理解析

2.1初始化

按照使用的思路,我们先看看,new Handler到底做了什么

    public Handler() {this(null, false);}

一个无参构造,调用了一个2参的构造

    public Handler(Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}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;}

乍一看上去,似乎做了很多事,没关系,我们一点一点看,先看第一个if,没必要仔细研究,看log就可以知道,这是对可能发生的内存泄漏的处理,关于内存泄漏,本篇不做详述。接下来,Looper调用了一个静态方法,得到了一个mLooper对象,接下来对该成员判空,exception已经告诉我们了,必须先执行Looper.prepare,否则就要抛异常。这里跳到我们平常的使用中,好像没执行过prepare方法啊,怎么也没见这个异常呢?这里先给出结论,因为我们一般是在UI线程new的Handler,所以不需要我们手动调用,因为系统已经帮我们调用过了(如果在工作线程创建Handler,就需要手动调用了)。回到代码分析,我们还是先看看myLooper方法吧

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

这里只有简单的一句,get,那么就看看吧,sThreadLocal是个啥

    // sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

注意我把注释也贴出来了,注释中说道,直到你调用了prepare,否则get到的为null。前面我们说了,系统帮我们调用prepare了,所以这里肯定是可以get到的。同时我们也看到,ThreadLocal似乎是个容器,类型为Looper类型,我们get到的应该是个looper对象。怎么验证呢,好办,去Handler类中看看mLooper这个成员好了。

final Looper mLooper;

不出所料,还是个final的。这个Looper就是Handler核心类中的一个,目前为止,我们还不知道它是干啥的,不过从命名来看,好像个环。那么就先往下分析吧,mQueue = mLooper.mQueue。我们看看这句,是把Looper类的成员赋给了Handler的成员。那么我们去Looper中看看吧

final MessageQueue mQueue;

MeassageQueue,消息队列,应该就是放message的吧,就是我们通过handler,sendMeassage的message。虽然不能确定,看名字像。再看一下mCallback和mAsynchronus,就是无参构造时穿过来的参数。

2.2send

初始化到这里就差不多分析完了,除了我们熟知的Handler和Meassage外,只有知道还有2个核心类Looper和MessageQueue就好了。接下来我们看看Handler去sendMeassage到底是在干嘛(send方法有很多,最终都会调用到

    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);}

这里可以看到,首先,我们确保MessageQueue对象一定要存在,接下来,执行了enqueueMessage方法

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

首先,第一句,将this(Handler)赋给了Meassage的target,这个target是啥啊,Handler?

    /*package*/ Handler target;

Message类的成员给了我们答案,确实是这样。到这,我们回顾下,Looper是Handler的成员,MessageQueue是Looper同时也是Handler的成员,而Handler有是Message的成员,他们几个似乎有着不可告人的密码。不八卦了,还是回到代码分析,mAsynchronous,这个成员默认是false,不用管。下面又调用了个方法,我们去看看

    boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

这个方法实在挺大,不过,抓住问题的关键就好了,看方法名,就是给Message入队,入什么队,别忘了,queue.enqueueMessage,啥意思,MessageQueue拿着一个一个的Meassage,把它们放在合适的队列位置上,所谓合适,就是上面这个挺大的算法。

2.3handleMessage

到这里,初始化也分析了,send也分析了,怎么就走到handleMessage了呢?别忘了,我们还有一员大将没有登场呢,没错,就是Looper,它有个非常重要的方法,loop,方法也比较大,这里只用关键的部分。需要注意的是,这个方法也是Android帮我们调用的。如果想在工作线程中使用Handler,除了要prepare外还要loop。

    /*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {....for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;....}....}

这个方法的意思也不难,就是用个死循环来循环MessageQueue。如果队列中有Message就处理,那么处理是怎么处理的呢

msg.target.dispatchMessage(msg);

还记得Meassage的target吗,对了,就是前面说的关系不一般的Handler。那就看看吧

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

这里最后一行,我们终于看到了,调用到了我们自己要实现的方法

    /*** Subclasses must implement this to receive messages.*/public void handleMessage(Message msg) {}

注释也可以清楚的告诉我们,就是这里,到此,Handler的工作原理主体就分析完了。高兴之余,我们再看看,dispatch方法中的其他分支吧,callback是个啥,成员mCallback又是个啥

/*package*/ Runnable callback;

Message中,callback就是个Runnable。

final Callback mCallback;
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);
}

而在Handler中,Callback是个接口,也是处理message的,这个跟直接调用handlerMessage有啥区别呢,网上资料说是可以避免内存泄漏,本人是没有搞懂,忘大神指教。

3、结语

Handler的基本工作原理就分析完了。可是还有几个点我们都是一带而过的,比如内存泄漏,这个足够单独开个篇幅了;比如prepare,loop是什么时候调用的,如何调用的;比如,send(Runnable)是怎么处理的呢?那么就在下一篇简单说说后面2个部分吧。

Handler机制原理解析(一)相关推荐

  1. Handler机制原理解析(二)prepare,loop,post

    Handler机制原理解析(二)prepare,loop,post 上一篇已经介绍了Handler机制的原理,如果不熟悉可以看Handler机制原理解析(一).这一篇,介绍下Handler周边的知识点 ...

  2. android Handler机制原理解析(一篇就够,包你形象而深刻)

    首先,我将Handler相关的原理机制形象的描述为以下情景: Handler:快递员(属于某个快递公司的职员) Message:包裹(可以放置很多东西的箱子) MessageQueue:快递分拣中心( ...

  3. AsyncTask机制原理解析

    AsyncTask机制原理解析 Android为我们提供了2种方便的异步处理方案,Handler和AsyncTask,两种方式适合的场景网上一搜就知道了,但是为什么呢?这篇分析将为你揭晓答案.前面分析 ...

  4. python解析原理_Python语法垃圾回收机制原理解析

    一 引入 解释器在执行到定义变量的语法时,会申请内存空间来存放变量的值,而内存的容量是有限的,这就涉及到变量值所占用内存空间的回收问题,当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉,那 ...

  5. Binder通信机制原理解析

    Binder是什么?Binder有啥用?作为一个应用开发者,如果我们开发的应用不涉及跨进程通信(IPC),我想我们也不会去接触Binder.但不知你有没有发现,近来的Andorid面试,都会问及And ...

  6. Android实战开发Handler机制深度解析

    本文为自己多年来在Android实战开发过程中总结归纳的一些常见问题,现在分享出来希望对初学者有所帮助. 本文出自门心叼龙的博客,转载请注明出处: https://blog.csdn.net/gedu ...

  7. RCU锁机制原理解析

    背景 为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种很有效的同步机制,在UNIX系统和Linux系统中得到了广泛的使用.但是 ...

  8. Hadoop的HA机制原理解析,与HA高可用集群环境搭建

    2019独角兽企业重金招聘Python工程师标准>>> Hadoop的HA工作机制示意图 下面我们开始搭建这一套高可用集群环境 hadoop2.0已经发布了稳定版本了,增加了很多特性 ...

  9. Android 签名机制原理解析和V1 、V2签名区别

    一.什么是签名? 是确保消息来源的真实性 是确保消息不会被第三方篡改 1.基本信息基础必备 1.1 消息摘要 消息摘要,又称数字摘要 或 数字指纹.  简单来说,消息摘要就是在消息数据上,执行一个单向 ...

最新文章

  1. DFT实训教程笔记2(bibili版本)- Scan synthesis practice
  2. 二十七、Node.js搭建第一个Express应用框架
  3. Android之Tab分页标签的实现方法--------采用ActivityGroup和GridView的结合
  4. HTTP协议长短连接以及无状态
  5. python string模块template_Python标准库笔记(1) — string模块
  6. 谷歌技术三宝之BigTable(转)
  7. 对给定数组升或降排序
  8. 西工大机考《会计电算化》大作业网考
  9. 友链导航源码php,2020优化版导航源码自动收录秘趣导航批量检查友链有效性导航源码...
  10. 决战大数据(升级版):大数据的关键思考 - 电子书下载(高清版PDF格式+EPUB格式)...
  11. “Vidalia tor privoxy”配置自动启动
  12. 如何创建WooCommerce弹出窗口来增加销售额(6种经过验证的方法)
  13. 修改Excel时出现“被保护单元格不支持此功能“的解决办法
  14. 【图像融合】基于matlab高分辨率全色图IHS图像融合(含评价指标)【含Matlab源码 2406期】
  15. 【修炼七】团队建设-尊重
  16. matlab中的a‘与a.‘的不同之处
  17. SIM7600怎么打电话,怎么电话通信
  18. Unity3D 游戏引擎之FBX模型的载入与人物行走动画的播放【转】
  19. 烟台大学计算机学院宋宜斌教授,清华教授、中科院张钹院士烟台大学做学术报告...
  20. 1、两人做游戏,轮流报数,报出的数只能是1、2、3、4、5、6、7、8其中之一,把两个人报出的数连加起来,谁报数后加起来的和是123,谁就获胜,让你先报,为了确保胜利,你第一个数报多少?

热门文章

  1. Kylin 10 安装达梦数据库 图形界面乱码 最小修正解决(一个字体文件)
  2. 电影《看不见的客人》是如何找到真相的?
  3. R无法安装RMySQL程序包解决方案
  4. Hibernate学习笔记Session.evict(user)方法
  5. 从技术转型项目经理到需求专家的长者分享
  6. api 与 implementation 的区别
  7. Mybatis-Plus中getOne方法获取最新一条数据
  8. 音视频协议-RTP协议
  9. 在Excel 2007中创建“迷你图”
  10. 分布式系统开发实战:分布式计算,分布式计算常用技术