知识的获取须要知其一,还要知其二,有机会还要知其三啦。java
在Android中常常会使用到Handler,或者在View的更新中常常能见到Handler的代码,只有主线程才能操做UI这是咱们的固定思惟。咱们一般在子线程作完耗时操做后,通知主线程去更新UI,通常都是经过Handler发送消息,主线程接收消息后执行对应的逻辑。android
那么不妨问问几个问题:markdown
若是能一下说明白,大佬请慢些走,手动奉杯茶【滑稽】。网络
handler是android给咱们提供用来更新UI的一套机制,也是一套消息处理机制(这是重点),咱们能够发送消息,也能够经过它处理消息。在系统的源码中,activity也会经过Handler机制发送消息回调生命周期函数。并发
咱们常常会在子线程去处理大量运算、网络请求、图片压缩等操做。当咱们操做完,要通知主线程操做结果,能够进行下一步行动了。这时候又有一个问题,若是几个线程同时去通知主线程呢,那么必然会有并发问题,咱们怎么避免这个问题又不会影响性能呢?(加同步锁会影响性能)函数
Google大哥仍是了解开发者意愿的,搬出来了Handler机制,内部有Looper、MessageQueue两个重要角色,光看名字咱们就知道这是一个消息队列,经过队列方式去对UI进行更新。很好解决了多个子线程的并发问题,还不影响性能。oop
第一步确定是从new Handler()开始了。Looper、MessageQueue、CallBack,能够看到咱们熟悉的3个对象都有了。post
在经过Looper.myLooper得到looper对象时,若是Looper为空,那么会抛出异常(提示:不能在未调用Looper.prepare()的线程里建立Handler)。在Android的主线程中,启动时便已调用过,能够直接建立Handler,可是在其它子线程里,直接建立Handler是会致使应用崩溃的。性能
接着从Looper中获取了MessageQueue对象了,存储消息都是保存在这里面。ui
进入myLooper方法,能够看到对象是经过ThreadLocal获取的,ThreadLocal是一个线程局部变量(map结构),和普通变量的不一样在于,每一个线程持有这个变量的一个副本,能够独立调用set和get方法,而且线程之间不会发生冲突(避免并发)。
/** * 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(); } 复制代码
既然知道了ThreadLocal是什么,那么就去看看在哪调用它的set方法。
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()建立了一个Looper对象,而后将对象存入到了sThreadLocal线程局部变量中。一样在一个线程中重复调用了prepare()方法,发现sThreadLocal已经不为空了,是会抛出异常的,提示“一个线程中只能有一个Looper对象”。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } 复制代码
这说明建立的Looper都是独立且惟一的,到这里Looper的建立过程也就完毕了。
handler.sendEmptyMessage()发送一个空消息,实际上几个发送消息方法最终调用的都是sendMessageAtTime()方法,而后执行queue.enqueueMessage()将消息存入队列中(注意看,target默认指向的是Handler本身)。
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; //target指向的是Handler本身 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } 复制代码
接下来再看看消息的发送,咱们知道发送是调用的Looper.loop()方法,直接进入源码。
看到先获取并判断Looper对象是否为空,而后拿到MessageQueue消息队列。在下面定义了一个死循化,不停的从队列中取值,若是为空则return掉,不为空则执行msg.target.dispatchMessage(msg)。敲黑板了!!!,这里的target以前说过默认状况是Handler,将信息又丢给Handler本身处理。
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; for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //....省略一大堆代码 try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } //...再省略一大堆代码 msg.recycleUnchecked(); } } 复制代码
在这里咱们就介绍完了Message的存储与发送,在Handler本身去处理消息时,会判断callback是否为空(去看看post(runnable)),为空则执行handleMessage(msg)方法,让咱们本身跟本身去完了。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } 复制代码
彩蛋来了,来一篇图解Handler吧。
我(Handler)对十元(Looper)说:“我喜欢你,作我女友吧(发送消息给Looper)”,十元接收到后确定不能立马就答应我啊,女孩子要矜持嘛,要想想:“我要不要接受他,他之后还会不会对我好,我不接受他他会不会很伤心,万一这帅哥跳河了怎么办(Looper.loop)”。而后过了一会想通了,以为嗯能够的,回答我:“好吧(Looper分发消息给Handler)”。这回到我了,我接收到消息后确定不是结束啊,还要想着两人的将来的,如今的年轻人又不是肯定关系就能结婚的,固然是选择继续关爱、呵护她呀(消息处理)。 (未完待续第二篇)