源码分析Android Handler是如何实现线程间通信的

Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的。开发者从一开始就被告知必须在主线程中进行UI操作。但Handler是如何实现线程间通信的呢?本文将从源码中分析Handler的消息通信机制。

0x00 Handler使用

首先看看我们平时是如何使用的Handler的。先看看以下代码

//定义Handler
Handler mHandler = new Handler(){public void handleMessage(Message msg){switch(msg.what){case UPDATE_UI:updateUI(msg);break;}}
};
class MyThread extends Thread{public void run(){//do same work!...//send messageMessage msg = mHandler.obtainMessage(UPDATE_UI);mHandler.sendMessage(msg);}
}private void updateUI(Message msg){//update UI
}

在子线程中sendMessage(Message)发送消息,然后在Handler的handleMessage(Message)接收消息,执行更新UI操作。那么Handler是如何把消息从MyThread传递到MainThread中来呢?我们从sendMessage()开始慢慢揭开它的面纱。

0x01 sendMessage(Message)

public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);
}
...
public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
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);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

我们发现调用sendMessage()方法最后都走到enqueueMessage()这个方法,一开始就把当前Handler实例赋给了Message.target的属性里面,后面可以知道这个target是用来执行处理函数回调的。

enqueueMessage方法是把Message信息放入到一个MessageQueue的队列中。顾名思义MessageQueue就是消息队列。从sendMessageAtTime()方法知道这个MessageQueueHandler中的一个成员。它是在Handler的构造函数中通过Loopger对象来初始化的。

0x02 Handler构造函数

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

这时候我们脑海知道创建Handler的时候,同时也创建了Looper实例和MessageQueue引用(MessageQueue对象其实是在Looper中构造的)。Looper是何物呢?简单地说就是消息循环,这个我们稍后会分析。

0x03 enqueueMessage(MessageQueue)

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

MessageQueue中可以看到这个入列方法中有一个for循环就是把当前的需要处理Message放到队列的合适位置。因为需要处理的Message对象都有一个开始处理的时间when,这个队列是按照when排序的。

至此,Handler调用sendMessage()方法后就把Message消息通过enqueueMessage()插入MessageQueue队列中。

而这个MessageQueue是在Looper中维护的。

0x04 prepare()创建Looper

0x02中我们知道创建Handler时就使用静态方法Looper.myLooper()得到当前线程的Looper对象。

/*** Return the Looper object associated with the current thread.  Returns* null if the calling thread is not associated with a Looper.*/
public static @Nullable Looper myLooper() {return sThreadLocal.get();
}

sThreadLocal是一个ThreadLocal类型的静态变量。什么时候会把Looper对象放在sThreadLocal中呢?通过prepare()方法。

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

继续翻阅源码知道Looper在构造函数中创建MessageQueue对象

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

调用prepare()方法将一个Looper对象放在了静态的ThreadLocal对象中。这个是一个与线程绑定的对象,且在内存中仅保存了一份引用。

使用ThreadLocal对象这一点非常巧妙,也非常重要,这是线程间通信的基础。即在线程中调用prepare()时就在该线程中绑定了Looper对象,而Looper对象中拥有MessageQueue引用。所以每个线程都有一个消息队列

这样HandlerLooperMessageQueue这几个类关系大概就可以画出来了。

0x05 启动循环loop()

/*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/
public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();//这里执行消息队列循环for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}//执行处理消息的回调try {msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}
}

loop()方法中有一个无限循环,不停地读取调用MessageQueuenext()方法。当next()没有返回时就阻塞在这里。当获取到MessageQueue中的消息时,就执行了处理消息的回调函数msg.target.dispatchMessage(msg)

前面0x01分析我们知道msg.target是在Handler中的enqueueMessage()进行赋值,即它指向当前的Handler实例。

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

执行msg.target.dispatchMessage(msg)后便走到了以下流程

/*** Handle system messages here.*/
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}

这里就是回调handleMessage(msg)函数处理消息的地方。Handler负责将Message入列,Looper则负责循环从MessageQueue中取出需要处理的Message并交由Handler来处理。

0x06 启动主线程的消息循环

我们知道通过静态方法Looper.prepare()创建了绑定当前线程的Looper对象,而通过loop()启动一个循环不停地读取队列中Message。但是Android系统是什么时候启动了主线程的消息循环呢?

要理解这一点就必须进入Android应用程序的入口ActivityThreadmain方法。

public static void main(String[] args) {...Looper.prepareMainLooper();...Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看出main方法中先后执行了Looper.prepareMainLooper()方法和Looper.loop()方法。正常情况下main方法不会退出,只有loop()方法发生异常后将会抛出RuntimeException

0x07 Looper.prepareMainLooper()

/*** Initialize the current thread as a looper, marking it as an* application's main looper. The main looper for your application* is created by the Android environment, so you should never need* to call this function yourself.  See also: {@link #prepare()}*/
public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}
}

prepareMainLooper()方法其实是调用了prepare()方法。

当我们启动应用时系统就调用了prepareMainLooper()并在主线程中绑定了一个Looper对象。

这时候我们回过来看看一开始的Handler使用方式。在主线程中我们创建了Handler对象,在Handler构造函数中初始化了Looper(即获取到了绑定在主线程中的Looper对象)。当在子线程MyThread中通过mHandler.sendMessage(msg)方法发送一个消息时就把Message放在与主线程绑定的MessageQueue中。这样在子线程中使用Handler就实现了消息的通信。

可以简单的使用以下类图表示,每个线程都由一个Handler,每个Handler都是与当前所在线程的Looper绑定

0x08 主线程是否会阻塞

0x06中知道在ActivityTheadmain方法中启动了一个死循环。那主线程是不是就一直阻塞在这里呢?其实不然。可以看到ActivityThread类里面有一个自定义的Handler对象mH,在这里对象中handleMessage()回调中定义了Activity的各种交互如管理Activity生命周期,启动service,显示window等,都是通过Handler进行处理的。同时可以看出只有当应用退出EXIT_APPLICATION之后才回调用Looper.quit()停止消息循环。

public void handleMessage(Message msg) {...switch (msg.what) {case LAUNCH_ACTIVITY: {...handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;...case PAUSE_ACTIVITY: {...handlePauseActivity((IBinder) args.arg1, false,(args.argi1 & USER_LEAVING) != 0, args.argi2,(args.argi1 & DONT_REPORT) != 0, args.argi3);...} break;...case SHOW_WINDOW:...handleWindowVisibility((IBinder)msg.obj, true);...break;...case EXIT_APPLICATION:if (mInitialApplication != null) {mInitialApplication.onTerminate();}Looper.myLooper().quit();break;...}...
}

0x09 总结

当创建Handler时将通过ThreadLocal在当前线程绑定一个Looper对象,而Looper持有MessageQueue对象。执行Handler.sendMessage(Message)方法将一个待处理的Message插入到MessageQueue中,这时候通过Looper.loop()方法获取到队列中Message,然后再交由Handler.handleMessage(Message)来处理。

微信关注我们,可以获取更多

转载于:https://www.cnblogs.com/angrycode/p/6576905.html

源码分析Android Handler是如何实现线程间通信的相关推荐

  1. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

  2. 从源码分析Android的Glide库的图片加载流程及特点

    转载:http://m.aspku.com/view-141093.html 这篇文章主要介绍了从源码分析Android的Glide库的图片加载流程及特点,Glide库是Android下一款人气很高的 ...

  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析-[Android取经之路]

    摘要:本节主要来讲解Android10.0 logd.logcat读写日志源码内容 阅读本文大约需要花费20分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  4. (连载)Android系统源码分析--Android系统启动流程之Linux内核

    > **这是一个连载的博文系列,我将持续为大家提供尽可能透彻的Android源码分析 [github连载地址](https://github.com/foxleezh/AOSP/issues/3 ...

  5. android view 源码分析,Android ViewPager源码详细分析

    1.问题 由于Android Framework源码很庞大,所以读源码必须带着问题来读!没有问题,创造问题再来读!否则很容易迷失在无数的方法与属性之中,最后无功而返. 那么,关于ViewPager有什 ...

  6. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 )

    文章目录 一.Activity 启动源码分析 ( AMS | ActivityManagerService ) 1.Instrumentation 调用 AMS 方法 2.ActivityStarte ...

  7. 【Android 电量优化】JobScheduler 相关源码分析 ( JobSchedulerService 源码分析 | Android 源码在线网址推荐 )

    文章目录 一.JobScheduler 提交任务 schedule 方法源码分析 二.schedule(JobInfo job, int uId) 方法 三.scheduleAsPackage 方法 ...

  8. android gps源码分析,Android编程之Android GPS ——AGPS源码分析及配置

    本文主要介绍了Android编程的Android GPS --AGPS源码分析及配置,通过具体的分析以及源码,向大家展示了这些,希望对大家学习Android编程有所帮助. 1:冷启动指令: locat ...

  9. HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程

    在<HDFS源码分析心跳汇报之数据结构初始化>一文中,我们了解到HDFS心跳相关的BlockPoolManager.BPOfferService.BPServiceActor三者之间的关系 ...

最新文章

  1. Selenium如何通过location和size定位元素坐标?
  2. python 面向对象_Python新手入门【面向对象】
  3. Deep learning chapter16
  4. 使用display:none和visibility:hidden隐藏的区别
  5. 华为OV小米鸿蒙,华为鸿蒙开源,小米OV们会采用吗?
  6. Codeforces Round #468 (Div. 2): F. Teodor is not a liar!(DP)
  7. java在线订单系统源码_春哥酒店在线预订微信小程序源码系统正式发布!
  8. MFC——SkinSharp For VS的使用说明
  9. 常用quartz表达式
  10. Linux平台下软件推荐及主题图标推荐
  11. mysql全库搜索关键字_数据库 全文检索
  12. 浅析ERP系统--人资
  13. ECS服务器10M带宽能让多少人同时访问?
  14. 偶然 --徐志摩
  15. 660 - 循环基础-利息计算
  16. Stenffensen加速迭代法
  17. 产品项目分析之竞品分析
  18. 计算机软考有哪些科目??
  19. Java泛型(11):潜在类型机制
  20. w10桌面不显示计算机了,电脑开机W10系统不显示桌面的解决方法

热门文章

  1. jquery触发点击事件
  2. OllyDbg笔记-对标志寄存器中ZF的理解(逆向方面)
  3. MySQL入门之视图
  4. 地图定点图表联动_拿下这套地图组件,快人一步做出炫酷报表!
  5. 多层数组如何遍历_带你从零学大数据系列之Java篇---第五章:数组
  6. linux 找不到swap分区,Linux下swap分区没有UUID解决办法
  7. java无ide编译_[转]无IDE时编译和运行Java
  8. 深度linux magento,linux下安装magento
  9. python设计自定义函数_我可以为内置Python类型添加自定义方法/属性吗?
  10. vim xxd命令查看二进制文件内容