Android 提供了Handler和Looper来来知足线程间的通讯,而前面咱们所说的IPC指的是进程间的通讯。这是两个彻底不一样的概念。java
Handler先进先出原则,Looper类用来管理特定线程内消息的交换(MessageExchange);android
咱们刚说Handler机制的主要做用是将某一任务切换到特定的线程来执行,咱们作项目可能都遇到过ANR(Application Not Response)
,这就是由于执行某项任务的时间太长而致使程序没法响应。这种状况咱们就须要将这项耗时较长的任务移到子线程来执行,从而消除ANR。而咱们都知道Android规定访问UI只能在主线程中进行,若是在子线程中访问UI,那么程序就会抛出异常。而Android提供Handler就是为了解决在子线程中没法访问UI的矛盾。数组
首先,咱们先看一个例子,咱们在子线程中建立一个Handler。app
new Thread(new Runnable() { @Override public void run() { new Handler(){ @Override public void handleMessage (Message message){ super.handleMessage(message); } }; } },"MyThread").start();
咱们运行时会发现,会抛出异常:Can't create handler inside thread that has not called Looper.prepare()
async
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.<init>(Handler.java:204) at android.os.Handler.<init>(Handler.java:118) at com.example.bthvi.myconstrainlayoutapplication.MainActivity$1$1.<init>(MainActivity.java:21) at com.example.bthvi.myconstrainlayoutapplication.MainActivity$1.run(MainActivity.java:21) at java.lang.Thread.run(Thread.java:764)
到底是为何会抛出异常呢?下面咱们经过Handler源码来看看。ide
当咱们建立Handler对象的时候调用的是下面的方法:函数
/** * Default constructor associates this handler with the {@link Looper} for the * current thread. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. */ public Handler() { this(null, false); }
咱们看到注释中说:++默认的构造函数将这个Handler与当前的线程的Looper关联,若是当前线程没有Looper,那么这个程序将没法接收消息,所以会抛出异常。++ 到底是怎么抛出异常的呢?咱们继续往下看:oop
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();//注释1 if (mLooper == null) { throw new RuntimeException(//注释2 "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
咱们看到在这里开始有个if语句,因为FIND_POTENTIAL_LEAKS
默认值是false因此咱们不须要去管它,注释1处,这里调用了Looper.myLooper()
,咱们看看它的源码:ui
/** * 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(); }
这个方法的做用就是返回与当前线程相关连的Looper对象。这里又调用了ThreadLocal.get()
,this
ThreadLocal
是一个线程内部的数据存储类,经过它能够在指定的线程中存储数据,数据存储之后只有在特定的线程中能够获取到存储的数据,对于其余线程来讲则没法获取到。ThreadLocal
用一句大白话来说解,++就是看上去只new了一份,但在每一个不一样的线程中却能够拥有不一样数据副本的神奇类。++ 其本质是ThreadLocal中的Values类维护了一个Object[],而每一个Thread类中有一个ThreadLocal.Values成员,当调用ThreadLocal的set方法时,实际上是根据必定规则把这个线程中对应的ThreadLocal值塞进了Values的Object[]数组中的某个index里。这个index老是为ThreadLocal的reference字段所标识的对象的下一个位置。
下面咱们来看它的get方法。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
在该方法的第二行调用了getMap方法。就是去获取当前线程的ThreadLocalMap对象。可是这个对象是在何时建立的呢?
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
由于抛出异常了因此咱们猜想这个值多是null。说到这里可能有些迷茫,咱们回头看看注释1处那里紧接着,咱们看到若是获取到的myLooper为空(至于为何会返回为空咱们看完后面回过头来看就很明白了)的的话,就会抛出咱们前面看到的异常。
...... mLooper = Looper.myLooper();//注释1 if (mLooper == null) { throw new RuntimeException(//注释2 "Can't create handler inside thread that has not called Looper.prepare()"); } ......
异常中说若是在子线程中建立Handler必需要调用Looper.prepare()
方法,那么咱们想确定是在调用该方法的时候作了一些操做,可能跟后面消息的接收和处理相关,经过后面的源码咱们会发现其实这个方法是对当前线程建立一个Looper对象,咱们来看源码:
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) {//注释3 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
咱们调用是没有传参的prepare
,他会调用内部的传参的prepare
方法。咱们看到注释3处又调用了ThreadLocal.get()
,假设咱们第一次调用Looper.prepare(),那么这个值确定是空的。若是不是空的话前面Looper.myLooper()就不会为空,也就不会抛出异常了。(那么当在一个线程中第二次调用该方法的时候,他的返回值就不会是空,系统会抛出异常一个线程只能建立一个Looper。也就是说一个子线程中Looper.prepare()只能调用一次。)因此这里确定走ThreadLocal.set()
方法,而且新建了一个Looper对象做为入参:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
咱们看到第二行仍是调用了getMap因为第一次调用,因此这个返回值仍是空的。因此应该走了createMap()
,下面咱们看看它的源码:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
咱们看到这里才对当前线的ThreadLocal进行了赋值。这个方法中咱们看到ThreadLocal已Map的结构存储了当前线程对应的Looper。以线程为Entry
也就是Key,以Looper为Value。咱们回过来再看,Looper.myLooper()。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); } public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//注释4 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
其实发现他就是去获取当前线程的Looper。当没有调用Looper.prepare()来建立Looper时,当前线程的ThreadLocalMap对象为空,因此前面的ThreadLocal.get()方法会调用setInitialValue
这个方法,
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
咱们看到这个方法返回了value,value是经过一个方法返回的,下面咱们看看这个方法:
protected T initialValue() { return null; }
这个方法单纯的就是返回null,因此也就是至关于ThreadLocal.get()返回null===>Looper.myLooper()返回null。因此Handler会在注释2处抛出异常。
回过头来,咱们仔细想一想为何会抛出异常来?就是由于:
Handler对象是基于Looper的,每一个Handler必须有一个Looper,这个Looper是在调用Looper.prepare()的时候建立的,这个Looper会以Map的形式存储在当前线程的ThreadLocal中。当当掉用Looper.myLooper()方法就是去在当前线程的ThreadLocal中拿到当前线程的Looper对象。