Android Handler的原理
简介
在 Android 中,只有主线程才能操作 UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生 ANR 异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要更新 UI,一般是通过 Handler
发送消息,主线程接受消息并且进行相应的逻辑处理。除了直接使用 Handler
,还可以通过 View 的 post
方法以及 Activity 的 runOnUiThread
方法来更新 UI,它们内部也是利用了 Handler
。在上一篇文章 Android AsyncTask源码分析 中也讲到,其内部使用了 Handler
把任务的处理结果传回 UI 线程。
本文深入分析 Android 的消息处理机制,了解 Handler
的工作原理。
Handler
先通过一个例子看一下 Handler
的用法。
public class MainActivity extends AppCompatActivity {private static final int MESSAGE_TEXT_VIEW = 0;private TextView mTextView;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MESSAGE_TEXT_VIEW:mTextView.setText("UI成功更新");default:super.handleMessage(msg);}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);mTextView = (TextView) findViewById(R.id.text_view);new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}mHandler.obtainMessage(MESSAGE_TEXT_VIEW).sendToTarget();}}).start();}
}
上面的代码先是新建了一个 Handler
的实例,并且重写了 handleMessage
方法,在这个方法里,便是根据接受到的消息的类型进行相应的 UI 更新。那么看一下 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;
}
在构造方法中,通过调用 Looper.myLooper()
获得了 Looper
对象。如果 mLooper
为空,那么会抛出异常:"Can't create handler inside thread that has not called Looper.prepare()",意思是:不能在未调用 Looper.prepare()
的线程创建 handler
。上面的例子并没有调用这个方法,但是却没有抛出异常。其实是因为主线程在启动的时候已经帮我们调用过了,所以可以直接创建 Handler
。如果是在其它子线程,直接创建 Handler
是会导致应用崩溃的。
在得到 Handler
之后,又获取了它的内部变量 mQueue
, 这是 MessageQueue
对象,也就是消息队列,用于保存 Handler
发送的消息。
到此,Android 消息机制的三个重要角色全部出现了,分别是 Handler
、Looper
以及 MessageQueue
。 一般在代码我们接触比较多的是 Handler
,但 Looper
与 MessageQueue
却是 Handler
运行时不可或缺的。
Looper
上一节分析了 Handler
的构造,其中调用了 Looper.myLooper()
方法,下面是它的源码:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();public static @Nullable Looper myLooper() {return sThreadLocal.get();
}
这个方法的代码很简单,就是从 sThreadLocal
中获取 Looper
对象。sThreadLocal
是 ThreadLocal
对象,这说明 Looper
是线程独立的。
在 Handler
的构造中,从抛出的异常可知,每个线程想要获得 Looper
需要调用 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));
}
同样很简单,就是给 sThreadLocal
设置一个 Looper
。不过需要注意的是如果 sThreadLocal
已经设置过了,那么会抛出异常,也就是说一个线程只会有一个 Looper
。创建 Looper
的时候,内部会创建一个消息队列:
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}
现在的问题是, Looper
看上去很重要的样子,它到底是干嘛的?
回答: Looper
开启消息循环系统,不断从消息队列 MessageQueue
取出消息交由 Handler
处理。
为什么这样说呢,看一下 Looper
的 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 loggerPrinter logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}msg.target.dispatchMessage(msg);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();}
}
这个方法的代码有点长,不去追究细节,只看整体逻辑。可以看出,在这个方法内部有个死循环,里面通过 MessageQueue
的 next()
方法获取下一条消息,没有获取到会阻塞。如果成功获取新消息,便调用 msg.target.dispatchMessage(msg)
,msg.target
是 Handler
对象(下一节会看到),dispatchMessage
则是分发消息(此时已经运行在 UI 线程),下面分析消息的发送及处理流程。
消息发送与处理
在子线程发送消息时,是调用一系列的 sendMessage
、sendMessageDelayed
以及 sendMessageAtTime
等方法,最终会辗转调用 sendMessageAtTime(Message msg, long uptimeMillis)
,代码如下:
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);
}
这个方法就是调用 enqueueMessage
在消息队列中插入一条消息,在 enqueueMessage
总中,会把 msg.target
设置为当前的 Handler
对象。
消息插入消息队列后, Looper
负责从队列中取出,然后调用 Handler
的 dispatchMessage
方法。接下来看看这个方法是怎么处理消息的:
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}
首先,如果消息的 callback
不是空,便调用 handleCallback
处理。否则判断 Handler
的 mCallback
是否为空,不为空则调用它的 handleMessage
方法。如果仍然为空,才调用 Handler
自身的 handleMessage
,也就是我们创建 Handler
时重写的方法。
如果发送消息时调用 Handler
的 post(Runnable r)
方法,会把 Runnable
封装到消息对象的 callback
,然后调用 sendMessageDelayed
,相关代码如下:
public final boolean post(Runnable r)
{return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;
}
此时在 dispatchMessage
中便会调用 handleCallback
进行处理:
private static void handleCallback(Message message) {message.callback.run();
}
可以看到是直接调用了 run
方法处理消息。
如果在创建 Handler
时,直接提供一个 Callback
对象,消息就交给这个对象的 handleMessage
方法处理。Callback
是 Handler
内部的一个接口:
public interface Callback {public boolean handleMessage(Message msg);
}
以上便是消息发送与处理的流程,发送时是在子线程,但处理时 dispatchMessage
方法运行在主线程。
总结
至此,Android消息处理机制的原理就分析结束了。现在可以知道,消息处理是通过 Handler
、Looper
以及 MessageQueue
共同完成。 Handler
负责发送以及处理消息,Looper
创建消息队列并不断从队列中取出消息交给 Handler
, MessageQueue
则用于保存消息。
如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)
Android Handler的原理相关推荐
- android Handler机制原理解析(一篇就够,包你形象而深刻)
首先,我将Handler相关的原理机制形象的描述为以下情景: Handler:快递员(属于某个快递公司的职员) Message:包裹(可以放置很多东西的箱子) MessageQueue:快递分拣中心( ...
- Android Handler与Looper原理简析
一直感觉自己简直就是一个弱智,最近越来越感觉是这样了,真的希望自己有一天能够认同自己,认同自己. 本文转载于:https://juejin.im/post/59083d7fda2f60005d14ef ...
- Android Handler原理
前言 Handler消息处理机制在Android开发中起着举足轻重的作用,我们有必要好好理解下其原理,先前我写的一篇文章,感觉疏漏了好多东西,因此打算写这篇文章,下面我们先从一个简单的例子出发 一.日 ...
- android 实例源码解释,Android Handler 原理分析及实例代码
Android Handler 原理分析 Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题 今天就为大家详细剖析下Handler的原理 Handler使 ...
- Android Handler消息机制原理最全解读(持续补充中)
本文主要详细去解读Android开发中最常使用的Handler,以及使用过程中遇到的各种各样的疑问. Handler 在Android开发的过程中,我们常常会将耗时的一些操作放在子线程(work ...
- android handler的机制和原理_Android 插件化原理——Hook机制之AMSamp;PMS解析
在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook:插件框架通过AOP实现了插件使用和开发的透明性.在讲述DroidPlugin如何实现四大组件的插件 ...
- Android 插件化原理学习 —— Hook 机制之动态代理
前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...
- 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | ActivityThread 后续分析 | Application 替换位置 )
文章目录 一.ActivityThread 后续分析 二.ActivityThread 相关源码 三.Application 替换位置 dex 解密时 , 需要将 代理 Application 替换为 ...
- Android 插件化原理解析——Activity生命周期管理
之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...
最新文章
- linux symbolic link attack tutorial
- 2018-2019-1 20165201 《信息安全系统设计基础》第9周学习总结
- 发送文件到打印机,打印机收不到(无线打印机)
- Android系列之Fragment(二)----Fragment的生命周期和返回栈
- 爬取智联招聘(面向对象)
- .bash_profile和.bashrc说明
- 后缀的形容词_玩转英语词汇-词汇策略之形容词后缀
- logstash zip linux安装,centos7.4安装测试logstash6.5.0
- 数字信号处理《数字滤波器的MATLAB与FPGA实现》
- 详解position:sticky
- 010 极限的四则运算例题
- 【Linux系统】第10节 linux系统文件及目录权限详解
- iptables实现华为云服务器无公网IP上网
- Windows10重装设置(个人)
- linux自动刷新桌面,Ubuntu下实现用Python开机自动更新壁纸为bing壁纸
- 使用WT工具恢复MongoDB数据
- 比菜鸟更进一步(1):Style文件和toolbar的使用
- 智能家居论文文献_智能家居控制系统界面设计结论与参考文献
- 软件测试线上故障规范及模板
- Python中的decimal.Decimal类型和整型相乘后还是decimal.Decimal类型