Android 中的Handler原理
来源:互联网 发布:大数据量 数据库 编辑:程序博客网 时间:2024/05/22 13:40
概述
Android的正常运行离不开消息机制的使用,而消息机制对我们最直观的体现就是Handler。
Handler是Android消息机制的最上层的接口,在我们的开发过程中我们只要和Handler进行交互即可,在开发中我们运用的最多的场景是利用Handler将一个耗时操作的运行结果从一个线程中切换到主线程,以辅助更新UI线程。
Android的消息机制主要是由Handler、Looper以及MessageQueue来进行的,其中MessageQue中存储着待处理的消息,以一种队列的方式提供给Handler。而Looper则是负责无限循环的从MessageQueue队列中提出待处理的消息进行处理。
我们调用Handler的send方法的时候,MessageQueue的enqueueMessage方法会被调用,接着Looper发现新来的消息的时候就会将它从消息队列中提出来处理,最后Handler的handleMessage方法被调用。由于Looper运行在handler所在的线程中,故最后信息就被转移到了handler的线程中。
Part.1 ThreadLocal
ThreadLocal是一个数据存储类,从名字上我们就能明白他是属于线程自己的一个数据存储工具,它大概的工作原理是在每个线程内部创造一份变量的拷贝,这份拷贝只有当前线程才能访问调用。
笔者一开始觉得很奇怪,不明白为什么需要这个东西,后来想到这实际上这是解决线程安全性的一个方法。假设我们有一个变量要设置成静态,但是又只能被当前线程修改这个变量,利用ThreadLocal类就能很容易的办到。
public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); new Thread(thread).start(); new Thread(thread).start(); new Thread(thread).start(); }}class MyThread implements Runnable{ private static ThreadLocal<Integer> mIntThreadLocal = new ThreadLocal<>(); private int myInt = 0; @Override public void run() { mIntThreadLocal.set(0); for (int i = 0; i < 3; i++) { mIntThreadLocal.set(mIntThreadLocal.get() + 1); myInt++; } System.out.println("ThreadLocal: " + mIntThreadLocal.get() + " int: " + myInt); }}
输出结果:
ThreadLocal: 3 int: 6ThreadLocal: 3 int: 9ThreadLocal: 3 int: 6
可以看到普通的int由于线程安全问题而被修改了,而被ThreadLocal装着的变量确一直维持着正确的结果
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);}ThreadLocalMap getMap(Thread t) { return t.threadLocals; }void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
可以看到,在ThreadLocal内部有个map类,这个类就是用来存储我们放进去的信息的,注意t.threadLocals这个变量,这个变量是Thread中的内部成员,当新建一个新的线程的时候,该变量就会重新新建一次,而map被赋值到了该变量上,这样子就可以确保每个线程都有一个自己的map,从而也就使得不同线程中的同一变量得到了隔离。
该Map是一个hashmap,利用ThreadLocal的哈希值进行运算得到值所在的位置
Part.2 MessageQueue
MesaageQueue是用单链表实现的,每个Node是Message类。该类主要包含两个操作:插入和读取。读取操作会伴随着删除操作,其中读取的方法是enqueueMessage,而遍历以及删除的方法是next方法
next()方法存在着一个for循环,它的作用是不断遍历消息队列,直到找到能处理的message节点将它从队列中摘下来,然后返回,如果不存在的话就会一直阻塞在该for循环里面。
Part.3 Looper
Looper在Android的消息机制中扮演着一个消息循环的角色,它会不断的从MessageQueue中查看是否存在新的信息,如果有就会提出来处理,否则将一直阻塞。
当我们使用Handler的时候,就必须要在Handler所在的线程新建一个Looper以配套,否则系统会提示我们报错。
public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); }
上面为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)); }private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
在prepare方法中我们看到了ThreadLocal.set(),这说明Looper是一个线程独享的对象,每个线程持有着自己的Looper,而Looper的构造方法中又将MessageQueue消息队列构造了出来。
实际上除了Looper.prepare()还提供了Looper.prepareMain()方法,这个方法是特地为主线程(UI)线程提供的,在ActivityThread类中存在着一个Main方法,该方法为整个程序的入口,其中就调用了Looper.prepareMain()来初始化主线程的Looper,这就是为什么在主线程设置Handler的时候不用我们特地去prepare一个Looper的原因。
在prepare完毕之后,就可以调用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 block if (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 logger final 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); } 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(); } }
主要关注for循环里面,首先loop调用了MessageQueue的next方法,而next方法在上文说了,它也是一个死循环,除非有message否则会一直阻塞,当得到了message后,looper会调用msg.target.dispatchMessage()方法,向目标handler发送该信息,目标handler得到信息后便会调用Handler的handleMessage方法处理对应的事件,最后,loop又开始了新一次的循环…
这里有一点值得我们去思考,这个loop方法是一个死循环,里面的next也是一个死循环,当我们调用了这个方法的时候,整个线程不就进入了一个无休止的循环里面,我们的程序不就ANR么。尽管如此,我们的程序还是成功的运行了下去,这是因为loop方法是启动于整个main方法的最后,在loop之前ActivityThread已经完成了所有程序的准备操作,而程序的各种生命周期是通过AMS进行IPC通知ActivityThread中的handler——绑定在我们looper上的handler,该hanlder内部对所有的生命周期都有对应的处理,因而我们的APP仍旧可以正常的运行,这也是为什么我们总是将要调用的方法放在对应的生命周期中处理。
Part.4 Handler
整个消息机制开始的地方与终结的地方都在Handler里面,它是Message的发送者,也是Message的接受者。
4.1 Handler.sendMessage
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); }
经过多步调用,最终来到了sendMessageAtTime方法,在方法的最后调用了enqueueMessage,该方法又调用了queue的enqueueMessage。由此得知,Handler的发送信息只是向消息队列中插入了一条信息。
当消息插入完毕后就会进入到上文提到的next环节,最终又交由到handler中,此时handler首先会检查自己的callback是否为null,若是交由callback的handleMessage进行处理。
由于
Looper运行在消息需要被处理的线程中,而我们在其他线程中使用到了目标线程的handler,因此此时消息就从其他线程回到了目标线程中。
Handler的构造方法规定了我们必须先构造一个Looper再去构造Handler,否则系统会报异常
感谢《Android 开发艺术探索》
- Android 中的Handler原理
- Android中的Looper、Handler和Message工作原理
- Android中的Handler、Looper和MessageQueue的使用以及原理
- Android中的Handler,Message,MessageQueue,Looper 原理关系图
- android中的消息机制--浅谈Handler的原理及使用
- Android中的基础----Handler、Looper、MessageQueue的工作原理
- Android Handler运行原理
- Android handler机制原理
- Android中Handler原理
- Android Handler实现原理
- android线程 handler原理
- Android Handler机制原理
- android handler机制原理
- android Handler原理
- android handler 调用原理
- Android Handler 原理初探
- Android Handler原理理解
- Android Handler原理分析
- 一个想法(续二):换个角度思考如何解决IT企业招聘难的问题!
- js与android原生的互调
- 快速排序
- 数据库新技术3
- BeanFactory not initialized or already closed
- Android 中的Handler原理
- Java开发中的23种设计模式详解及代码和图解
- BZOJ 2957 分块
- MySQL用户名和密码都正确却无法登陆的问题
- android 媒体播放器之播放音乐
- android 媒体播放器之播放音乐
- Android Service基础
- IL2Cpp深坑之WeakReference
- Hadoop之MapReduce运行原理(一)