Android Handler机制原理及源码解析
今天打算写一下Handler机制的原理及源码解析,Handler在我们的开发中用到的频率还是非常高的,同时这也是一个非常基础的知识点,但是即使是基础知识,有很多工作两三年的安卓开发依然是一知半解,搞不清楚原理,包括View、ViewGroup的事件分发及绘制流程。
在深入学习一下知识点之前,希望能够带着疑问去思考:
1.为什么在子线程实例化Handler会报错闪退,而主线程不会
2.为什么每个线程只能存在一个Looper和MessageQueue
3.多个Handler发送消息是怎么保证Looper轮询消息队列发送最新消息不错乱发给其他Handler的
4.子线程真的不能更新UI吗?
5.ThreadLocal的作用
......
Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.e("接收消息", (String) msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Message msg = handler.obtainMessage(); msg.obj= "hello world"; handler.sendMessage(msg); }
我们在实际的开发中基本上都是这样类似的写法,但是为什么当我们在子线程实例化Handler就不行了呢
public Handler() { this(null, false); } 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( // 在实例化Handler时, mLooper 为空的时候就会抛出这个异常 "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
那么问题找到,Looper为空,接下来我们再看Looper里的代码
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
好的,问题找到,也回到了我们第4个问题ThreadLocal是什么
public class ThreadLocal<T> { /** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取当前线程的私有数据变量 ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); } /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } } /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ // Thread 源码里的私有变量,为 Map 键值对类型 ThreadLocal.ThreadLocalMap threadLocals = null;
Java基础不算太差的朋友相信已经懂了,sThreadLocal.get()获取的Looper其实就保存在当前线程的私有变量threadLocals,那我们怎样给子线程加一个Looper呢,为什么在主线程可以直接使用呢?
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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.prepare() 就可以给当前线程添加一个轮询器,主线程之所以不需要开发者添加,是因为在程序启动的时候,famework层就已经给我们实例化了一个,在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 libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); // 实例化轮询器 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // 开启消息轮询 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
当你在子线程加上轮询器时,却发现并不行,因为轮询器还没有开启轮询消息
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死循环 for (;;) { Message msg = queue.next(); // might block // 如果消息为空就return,进入下一次轮询 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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long time = end - start; if (time > slowDispatchThresholdMs) { Slog.w(TAG, "Dispatch took " + time + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what); } } 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(); } }
那么接下来我们从头到尾过一遍Handler机制,首先Looper在程序启动的时候系统就已经帮我们创建好了,那我们去看一下方法里面的实现。
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); /** * 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()} */ // 系统实例化Handler public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } // 往当前线程的私有变量里添加 Looper sThreadLocal.set(new Looper(quitAllowed)); } // Looper 在实例化的时候也实例化了一个消息队列同时还持有了当前线程的引用 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
然后我们从发送消息查看源码
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } ------经过几个方法的调用进入下面的方法 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { // mQueue 在Handler 实例化的时候就从当前线程中取出消息队列并赋值了 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) { // 重点在这里,把当前Handler的引用赋值给 msg 的 target msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
进入消息队列的源码
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; // p == null 代表前面没有消息, when 是延迟消息的时间值 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. // 当前消息的 next 是 p 引用,形成一个单链表结构,如果是第一个消息的话,p为空 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; // 发送了第一个消息后 mMessages 就不为空了 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.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
Looper.loop()会进入轮询,然后从Looper获取MessageQueue ,再调用MessageQueue .next(),这个方法不再解析,简单的说就是把当前的消息返回,注意理解Message的单链表结构,next是null,当msg获取的是null,进入下一次轮询,消息不为空调用msg.target.dispatchMessage(msg),target就是Handler,所以消息不会发送错乱。
最后再啰嗦一句,子线程可以更新UI,View不是只能在主线程更新UI,而是只能在创建它的线程更新UI,详情见源码的ViewRootImpl。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
iOS消息转发小记
消息转发流程图 15277558865032.jpg 如果类接收到无法处理的消息,会触发消息转发机制,一共有三个步骤,接受者在每一步中均有机会处理消息。步骤越往后,处理消息的代价就越大,所以最好再第一步就处理完。 第一道防线 在类里面实现两个方法来处理未知消息。执行动态方法解析之前,先会判断是否曾经有动态解析。 resolveInstanceMethod:处理实例方法 resolveClassMethod:处理类方法 我们来看个Demo,先看调用方代码 TestA *testA = [[TestA alloc] init]; [testA instanceMethod]; [TestA classMethod]; 再来看看TestA的定义。 // TestA.h @interface TestA : NSObject - (void)instanceMethod; + (void)classMethod; @end // TestA.m @implementation TestA - (void)newInstanceMethod { NSLog(@"newInstanceMethod...
- 下一篇
RN Exception: Before building your project, you need to accept the lic...
异常 * What went wrong: A problem occurred configuring project ':app'. > You have not accepted the license agreements of the following SDK components: [Android SDK Platform 23, Android SDK Build-Tools 23.0.1]. Before building your project, you need to accept the license agreements and comp lete the installation of the missing components using the Android Studio SDK Man ager. Alternatively, to learn how to transfer the license agreements from one workstat ion to another, go to http://d.android.c...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装Docker,最新的服务器搭配容器使用
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS6,CentOS7官方镜像安装Oracle11G