你真的懂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相关的类图:

更多相关文章

  1. 读写Android中assets目录下的文件的方法详解
  2. Android(安卓)动态权限最全解析
  3. Android(安卓)Activity 完全解析(上)
  4. 事件处理机制之Gestures(手势)
  5. Android中使用webservice验证用户登录的示例
  6. Android(安卓)—— 注解(Annotation)也被称为元数据(Metadata)
  7. Android(安卓)组件之一(Service)
  8. Android基础笔记(十一)- Service基础和注意事项以及Activity与Serv
  9. Android异步处理机制AsyncTask的理解

随机推荐

  1. httplive流媒体播放(m3u8)
  2. Configuring a New Product of Android 2
  3. android BLE从入门到精通开发
  4. Android(安卓)Jetpack 系列篇(二) WorkMa
  5. Android 学习笔记-2010年10月
  6. android沉浸式状态栏和虚拟按键
  7. 教程:实现Android的不同精度的定位(基于网
  8. Android Studio 几个非常有用的工具
  9. Android里面WebView加载HTML里面点击按钮
  10. Android 保存浏览记录 SharedPreTools