你真的懂Android(安卓)Handler吗?(一)
你真的懂Android的Handler机制吗?在回答这个问题之前先问自己几个问题:
1、Handler是如何跟线程绑定的?
2、Handler中的消息是怎么传递的?是通过回调还是通过循环?
3、如果是通过循环传递的,那么为什么消息队列为空时没有引起ANR?是不是在非UI线程中进行的无限循环?
4、如果是在非UI线程中进行的无限循环,那么在UI线程发送消息并且在UI线程中处理消息时是否进行了线程切换?这样做是否浪费了资源?有没有更好的解决方案?
5、Handler是如何进行线程切换的?
我们从Handler对象的创建入手,深挖一下Handler消息机制。
模块一:Hander是如何跟线程绑定的?
首先看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,那么Handler时收不到消息的。所以我们猜测,Handler的消息传递是通过循环进行的。下面我们继续跟代码。
/** * Use the {@link Looper} for the current thread with the specified callback interface * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering * with respect to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param callback The callback interface in which to handle messages, or null. * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ 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 " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
这段代码依然是Handler的构造方法,前面的无参构造方法就是调用了这个方法。这个方法比较简单,就是创建了Looper对象,注意这里的Looper对象并不是new出来的,而是通过Looper.myLooper()获取的,至于为什么,我们一会儿再看。然后把Looper对象中的Queue赋值给了成员变量mQueue,然后通过传过来的参数为成员变量mCallback和mAsynchronous赋值。
了解了Handler的构造方法,再看下Looper.myLooper()方法:
/** * 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(); }
可以看到,myLooper()方法是一个静态方法,它会返回一个与当前线程绑定的Looper对象,如果当前线成没有与Looper绑定,就会返回null。那么我们需要找一下sThreadLocal是什么时候初始化的。
public final class Looper { /* * API Implementation Note: * * This class contains the code required to set up and manage an event loop * based on MessageQueue. APIs that affect the state of the queue should be * defined on MessageQueue or Handler rather than on Looper itself. For example, * idle handlers and sync barriers are defined on the queue whereas preparing the * thread, looping, and quitting are defined on the looper. */ private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal sThreadLocal = new ThreadLocal(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread;
通过上面代码可以看到,sThreadLocal是Looper类中的一个常量,它在Looper类加载的时候就会被初始化。那么ThreadLocal类是做什么的?它为什么可以跟当前线程绑定到一起?
带着这几个问题,我们需要追踪一下ThreadLocal的源码,前面Looper的myLooper()方法调用了ThreadLocal的get(),方法,所以我们先从get()方法入手。
/** * 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) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
从代码可以看到,调用ThreadLocal的get()方法时会从当前线程中获取数据,如果获取不到,就调用ThreadLocal的setInitialValue()方法,初始化一个变量t,然后把t存到当前线程中。
其实,Thread对象中有一个ThreadLocal.ThreadLocalMap类型的属性threadLocals,threadLocals会存放以ThreadLocal对象为key,任意对象为Value的数据,所以当我们从当前线程获取数据时就是查找当前线程中的threadLocals属性中有没有以ThreadLocal对象为key的数据,如果有就返回数据,如果没有就调用ThreadLocad的setInitalValue()方法去初始化一个值t,并将当前ThreadLocal对象为key,t为value存到当前线程中。
下面是setInitailValue()方法的源码:
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ 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; }/** * Returns the current thread's "initial value" for this * thread-local variable. This method will be invoked the first * time a thread accesses the variable with the {@link #get} * method, unless the thread previously invoked the {@link #set} * method, in which case the {@code initialValue} method will not * be invoked for the thread. Normally, this method is invoked at * most once per thread, but it may be invoked again in case of * subsequent invocations of {@link #remove} followed by {@link #get}. * * This implementation simply returns {@code null}; if the * programmer desires thread-local variables to have an initial * value other than {@code null}, {@code ThreadLocal} must be * subclassed, and this method overridden. Typically, an * anonymous inner class will be used. * * @return the initial value for this thread-local */ protected T initialValue() { return null; }
setInitialValue()方法中调用了initailValue(),这个方法会返回要被初始化的值,这个方法默认返回null,如果想要返回其他数据,就需要在子类中复写这个方法。Looper对象没有复习这个方法,所以默认就是返回null。也就是说如果我们直接在一个非UI线虫中调用Looper.myLooper()就会返回null,因此我们在非UI线程中没有调用Looper.prepare()方法,而直接new一个Handler对象,就会报异常。这是因为我们需要在调用Looper.myLooper()方法之前,先调用Looper.prepare()方法,这个方法会创建一个Looper对象lp,然后调用ThreadLocal的set()方法,把以ThreadLocal对象为key,以lp为value的数据存到当前线程中。
下面是ThreadLocal的set()方法的源码:
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
至此,我们明白了Handler是如何跟当前线程绑定的,以及ThreadLocal的作用。我们再做下总结:
Looper中持有ThreadLocal对象tl,当我们在一个线程中调用Looper.prepare()方法时,就会创建一个Looper对象lp,并把tl的弱引用作为key,lp作为value存到当前线程中,这样Looper就和当前线程绑定了。当我们创建Handler对象时,就会调用ThreadLocal的get()方法,从当前线程中获取Looper对象。ThreadLocal的作用就是作为key值,把looper对象和当前线程绑定到一起。
下面是ThreadLocal相关的类图:
更多相关文章
- 读写Android中assets目录下的文件的方法详解
- Android(安卓)动态权限最全解析
- Android(安卓)Activity 完全解析(上)
- 事件处理机制之Gestures(手势)
- Android中使用webservice验证用户登录的示例
- Android(安卓)—— 注解(Annotation)也被称为元数据(Metadata)
- Android(安卓)组件之一(Service)
- Android基础笔记(十一)- Service基础和注意事项以及Activity与Serv
- Android异步处理机制AsyncTask的理解