Android中可以通过Handler来更新主线程中UI的变化,更新UI只能在主线程中进行更新,而为了让其他线程也能控制UI的变化,Android提供了一种机制HandlerLooperMessageQueue一同协作来达到其他线程更新UI的目的。

一般我们会在主线程中通过如下方法定义一个Handler

private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {tv.setText("mHandler change UI");super.handleMessage(msg);}};

一般都见不到LooperMessageQueue的,那么它们都是在哪里调用与如何协作的呢?在主线程不会显式的调用Looper而是会在ActivityThread.main方法中默认调用。

ActivityThread.main

public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");SamplingProfilerIntegration.start();// CloseGuard defaults to true and can be quite spammy.  We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Set the reporter for event logging in libcoreEventLogger.setReporter(new EventLoggingReporter());// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);Process.setArgV0("<pre-initialized>");Looper.prepareMainLooper();//创建LooperActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();//开启Looper循环throw new RuntimeException("Main thread loop unexpectedly exited");}

如上代码,调用了Looper.prepareMainLooper()方法,在主线程中创建了一个Looper,不信的话我们再查看该方法做了什么

Looper

prepare

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));//创建Looper并赋给sThreadLocal}/*** 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();}}public static @Nullable Looper myLooper() {return sThreadLocal.get();}

prepareMainLooper方法中调用了prepare而通过prepare会发现它其实就是创建了一个Looper,并把它赋给了sThreadLocal。同时可以通过myLooper方法获取当前线程中的Looper。再来看下new Looper(quitAllowed)初始化了什么

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

在这里我们终于看到了MessageQueue了,它创建了一个MessageQueue。该消息队列就是用来保存后续的Message。再回到ActivityThread.main方法中,发现它调用了Looper.loop()是用来开启Looper循环的,监听消息队列MessageQueue中的消息。

loop

我们来看下Looper.loop()的源码:

public static void loop() {final Looper me = myLooper();//获取Looperif (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.traceBegin(traceTag, msg.target.getTraceName(msg));}try {msg.target.dispatchMessage(msg);//通过Handler分发消息} 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中首先获取了当前所在线程的Looper,同时也获取到了Looper中的MessageQueue,说明Looper已经与当前的线程进行了绑定。在后面开启了一个for的死循环,发现它做的事件是不断的从消息队列中取出消息,最后都交给msg.target调用它的dispatchMessage方法,那么target又是什么呢?我们进入Message

Message

/*package*/ int flags;/*package*/ long when;/*package*/ Bundle data;/*package*/ Handler target;/*package*/ Runnable callback;// sometimes we store linked lists of these things/*package*/ Message next;

发现它就是我们熟悉的Handler,说明最后调用的就是Handler中的dispatchMessage方法,对消息的分发处理。这样一来Handler就通过Looper联系上了Looper所绑定的线程,即为主线程。

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,同时也获取了Looper中的消息队列。当然如果所处线程的Looper为空的话就会抛出异常,这就解释了为什么在非主线程中创建Handler要分别调用Looper.prepareLooper.loop而主线程则不需要,因为它默认已经调用了。

dispatchMessage

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

回到前面,对于dispatchMessage的处理,首先判断msg.callback是否为空,这里callback通过上面的Message应该能知道他就是一个Runnable,如果不为空则直接调用Runnablerun方法。否则调用HandlerhandleMessage方法.而这个方法相信大家已经很熟悉了,对事件的处理都是在这个方法中执行的。因为通过前面我们已经知道了Handler已经联系上了主线程,所以handleMessage中的处理自然相对于在主线程中进行,自然也能更新UI了。通过这里我们能把Looper比作是一个桥梁,来连接Looper所在的线程与Handler之间的通信,同时管理消息队列MessageQueue中的消息。那么前面的Runnable又是如何不为空的呢?我们使用Handler有两种方法,一种是直接创建一个Handler并且重写它的handleMessage方法,而另一种可以通过Handler.post(Runnable)来使用,这样事件的处理自然就在run方法中实现。

上面介绍了Handler是如何联系上了需要操作的线程与对消息是如何取出与处理的。下面来谈谈消息是如何放入到Looper中的MessageQueue中的。

sendMessageAtTime

通过Handler发送消息的方式很多,例如:sendMessagesendEmptyMessagesendMessageDelayed等,其实到最后他们调用的都是sendMessageAtTime方法。所以还是来看下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);}

sendMessageAtTime则就是调用了enqueueMessage操作,看这方法名就知道是入队列操作了。

enqueueMessage

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

果不其然直接调用了MessageQueue中的queue.enqueueMessage(msg, uptimeMillis)将消息加入消息队列,同时这段代码msg.target = this 将当前的Handler赋给了msg.target,这就是前面所说的Looper.loop方法中调用的Handler。这样就把消息放到了MessageQueue中,进而通过前面所讲的loop来取出消息进行相应的处理,这样就构成了整个对消息进行处理的系统。这也是使用Handler内部所发生的原理。好了HandlerLooperMessageQueue它们之间的联系基本就是这些了。我也简单画了张图希望有所帮助

总结

来总结下它们之间的流程。首先创建Handler而在Handler所处的线程中必须要有一个Looper,如果在主线程中默认帮我们实现了,其他线程必须调用Looper.prepare来创建Looper同时调用Looper.loop开启对消息的处理。每个Looper中都有一个MessageQueue它是用来存储Message的,Handler通过post或者send..等一系列操作通过Looper将消息放入到消息队列中,而Looper通过开启一个无限的循环来一直监听着消息的处理,不断从MessageQueue中取出消息,并交给与当前Looper所绑定的handlerdispatchMessage进行分发,最后根据情况调用Runnablerun或者HandlerHandlerMessage方法对消息进行最后的处理。

其它分享:https://idisfkj.github.io/arc...

关注

Handler、Looper与MessageQueue源码分析相关推荐

  1. Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析

    Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...

  2. Handler原理讲解及源码分析

    不用多说先上一张图 一,发送消息 首先我们每次会创建一个Handler会调用源码 如果looper对象为空的话会执行Looper.prepare()将一个新的Looper放入ThreadLocal中. ...

  3. Wangle源码分析:编解码Handler

    2019独角兽企业重金招聘Python工程师标准>>> 前言 编解码是协议相关的,如果没有编解码Handler,那么在处理网络的粘包.拆包时会变得很复杂.除了http之类的公有协议之 ...

  4. Wangle源码分析:Pipeline、Handler、Context

    2019独角兽企业重金招聘Python工程师标准>>> 基本概念 Wangle中的Pipeline和Netty中的Pipeline是很相似的,既可以将它看为一种职责链模式的实现也可以 ...

  5. Java线程池 源码分析

    1.个人总结及想法: (1)ThreadPoolExecutor的继承关系? ThreadPoolExecutor继承AbstractExectorService,AbstractExecutorSe ...

  6. Wangle源码分析:Service

    2019独角兽企业重金招聘Python工程师标准>>> 前言 Wangle中的Service代表一个远程服务(方法),熟悉RPC的朋友肯定知道这就是一个简单的RPC,当然,和一些常见 ...

  7. Wangle源码分析:ClientBootstrap

    2019独角兽企业重金招聘Python工程师标准>>> ClientBootstrap介绍 ClientBootstrap是wangle作为Client端的一个快速启动辅助类,在经过 ...

  8. Wangle源码分析:ServerBootstrap

    2019独角兽企业重金招聘Python工程师标准>>> ServerBootstrap介绍       ServerBootstrap,顾名思义,它是作为Wangle服务端的一个启动 ...

  9. Wangle源码分析:EventBaseHandler、AsyncSocketHandler

    2019独角兽企业重金招聘Python工程师标准>>> 前言 前面的Wangle源码分析系列文章详细的分析了Pipeline.Handler等实现原理,细心的读者可能发现,每次在构造 ...

最新文章

  1. jquery实现多行文字图片滚动效果
  2. git-commit编辑器nano改vim
  3. js中的几种跨域方法
  4. 无法访问此网站localhost 拒绝了我们的连接请求_官方教程丨如何在IPFS上创建托管个人网站?...
  5. ubuntu16.04配置量化投资tensorflow/pytorch深度学习环境
  6. nmon安装为什么重启mysql_Nmon的安装及使用
  7. apache ignite_Apache Ignite变得简单:第一个Java应用程序
  8. Oracle监听器启动出错:本地计算机上的OracleOraDb11g_home1TNSListener服务启动后又停止了解决方案
  9. 在Visual Studio Code中查找并​​替换为换行符
  10. 使用STM32与TB6612FNG驱动直流减速电机
  11. html reset 无效,HTML中的input type=reset标签失效(不起作用)的可能原因。
  12. SecureCRT 安装与破解教程
  13. 三维GIS视频融合监控平台
  14. java生成有理数_Java 有理数设计
  15. 配置Android的SDK,DNK,JDK,ANT打包APK环境
  16. Linux文件颜色所代表的含义
  17. 年薪30万IT精英 挥别都市回乡种田务农
  18. 坚持是一种态度,公众号粉丝突破 1300 啦
  19. Vue3使用Swiper
  20. 虚拟机可以做成存储服务器吗,利用win10自带虚拟机功能轻松打造家用nas

热门文章

  1. Spring+Quartz实现定时任务的配置方法
  2. 使用组策略配置Windows 7的高级防火墙
  3. 防止标题或者特定内容长度溢出wordcut
  4. 关于Remote Desktop Users组
  5. (DOM艺术) 实用的动画
  6. PHP整理笔记八正则表达式
  7. 《STM32库开发实战指南:基于STM32F103(第2版)》——2.1节仿真器简介
  8. Gradle -- buildScript块与allprojects块及根级别的repositories区别
  9. 记一次数据库事务的并发同步控制
  10. 【翻译】CEDEC2012 SQUARE ENIX GPGPU实现高速GI烘培工具的方法