Handler - 简单的一些原理

2018-04-17  本文已影响0人  SHERLOCKvv

看本篇文章的小伙伴们,先声明一点哈,原理为主,较少涉及用法

最近在面试,发现很多面试官都会让说一下Handler的原理。Handler你百度的话会发现有特别多的文章来讲这个东西,比我讲的详细的有太多太多。我也想写一篇文章研究下里面较为浅显的道道,加深下自己的印象,防止以后忘记了的话有熟悉的东西可看。


Handler在项目中经常会用到,关于它相信不会很陌生,不过依照惯例还是来介绍下它吧:

//Handler.post(Runnable)用法
private Handler mHandler = new Handler();
···
new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(1000);
      mHandler.post(new Runnable(){
        public void run(){
          //UI操作TODO 
        }
      });
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
 }).start();
//Handler.postDelayed(Runnable, time)用法
private Handler mHandler = new Handler();

private MyRunnable runnable = new MyRunnable();

class MyRunnable implements Runnable {
    public void run(){
        //各种操作 TODO
        mHandler.postDelayed(runnable, 1000);
    }
}
//Handler.sendMessage(Message)用法
private Handler mHandler = new Handler(){
    public void handleMessage(Message msg){
       //UI操作TODO
    }
};
····
new Thread(){
  public void run(){
    //两种方法创建Message都可以,推荐第二种
    //Message msg = new Message();
    Message msg = mHandler.obtainMessage();
    msg.arg1 = 88;
    msg.arg1 = 100;
    msg.obj = mBeanOne;
    //两种方法发送msg都可以
    //mHandler.sendMessage(msg);
    msg.sendToTarget();
    }
}.start();

好,到这里,我们开始研究下它的源码。

obtainMessage()源码分析

最后一种方法中,我们看到Message的创建方法有两种,一种是最常规的(我也是经常这么用):

Message msg = new Message();

但是可以看到还有另一种方法去创建它:

Message msg = mHandler.obtainMessage();

我们点开Handler的obtainMessage()方法去追源码:

Handler类
/**
 * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
 * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
 *  If you don't want that facility, just call Message.obtain() instead.
 */
public final Message obtainMessage()
{
    return Message.obtain(this);
}

ok!点开obtain(this)方法继续追!
注意一下这个方法的参数this,意思是把Handler对象传进去了。

Handler类
/**
 * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
 * @param h  Handler to assign to the returned Message object's <em>target</em> member.
 * @return A Message object from the global pool.
 */
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

好,到这里为止,我们终于看到Message的赋值操作了,通过obtain()方法返回了一个Message,并把上面传进来的Handler参数赋值给了message的target。
这里插一句嘴,target其实就是handler自己,标识一个发送地址。因为发送message你必须得有一个地址吧,说明要发送到哪里,这个target你就可以把它理解为一个地址,即,把消息发送给handler自己。
OK,继续往下追obtain():

Message类
/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

sPool就是Message类里面的私有静态变量:

public final class Message implements Parcelable {
    ···
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    ···
}

好了,现在就能看出来,obtain()的作用就是new出一个Message对象,不过它在new之前会检查Message里面有没有Message对象sPool,有的话就不用创建了。

sendToTarget()源码分析

最后一种方法中,我们看到Message的发送方法也有两种,一种是常规的:

mHandler.sendMessage(msg);

还有一种:

msg.sendToTarget();

往下追sendToTarget()吧:

/**
 * Sends this Message to the Handler specified by {@link #getTarget}.
 * Throws a null pointer exception if this field has not been set.
 */
public void sendToTarget() {
    target.sendMessage(this);
}

不用往下追特别多,一层你就知道是啥意思了,还记得上面我们说的
“target其实就是handler自己,标识一个发送地址”吧,那不就是在调用
mHandler.sendMessage(msg);嘛,我去!
上面的方法出现的需要注意下的地方差不多就这俩了。
现在看看Handler真正核心的东西:

Handler Looper MessageQueue 三者的关系

Looper类
/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
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 (;;) {
        Message msg = queue.next(); // might block
        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 traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        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();
    }
}

我把有用的代码抽取一部分,变成易懂的伪代码如下:

public static void loop() {
    final Looper me = myLooper();

    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next(); 

        if (msg == null) {
            return;
        }

        try {
            msg.target.dispatchMessage(msg);
        } finally {
            
        }
    }
}

这下子就简洁多了,咱们来细看下它的源码:
首先是通过myLooper()方法得到Looper对象,这里需要知道的是:这个Looper对象是UI线程也就是主线程里面的Looper,通过追myLooper()方法就能知道:

Looper类
/**
 * 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();
}

这里留个谜,这个方法后面讲Handler关联Looper时会详细讲到。
继续看上面的Looper.loop(),拿到了Looper对象,也就拿到了Looper里面的MessageQueue消息队列:MessageQueue queue = me.mQueue;
接下来就是一个未指定条件终止语句的for循环了,就是无限循环。
它通过MessageQueue的next方法遍历消息队列,有的话就取出来:
msg.target.dispatchMessage(msg);
上面我们已经说了,Message的target就是Handler自己,所以,这个方法其实就是调用:
mHandler.dispatchMessage(msg);
OK,点进去看看吧:

Handler类
/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

果不其然,就是Handler里面的方法。
在讲这里的源码逻辑之前,先说下三个东西
①Message的callback

Message类
/*package*/ Runnable callback;

public
interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

可以看到这个callback其实就是个Runable接口,它里面就是个待实现的run()方法
②Handler的mCallBack

Handler类
final Callback mCallback;

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}

一样!Handler也是个接口,它里面也有一个handleMessage待实现接口。
③handleMessage(msg);

Handler类
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

我去,这三兄弟一样的呀,都是实现方法去处理这个Message参数

OK,看到这里,dispatchMessage(msg)的源码逻辑就好懂了:
首先会判断Message自己携带的CallBack回调方法是不是null,如果不是null的话直接调用handleCallback(msg);

private static void handleCallback(Message message) {
    message.callback.run();
}

如果是null的话就去判断Handler里面的CallBack对象mCallback,
不为null直接调mCallback.handleMessage(msg);
如果是null就调handleMessage(msg);

看到这里,是不是想到了点什么,当时我看到这里的时候,我想到了sendMessage的处理方式了,我在这里举个例子:

Message msg = mHandler.obtainMessage();
msg.arg1 = 88;
msg.arg1 = 100;
msg.obj = mPerson;
mHandler.sendMessage(msg);

//接受Message并处理:
private Handler mHandler = new Handler(){
    public void handleMessage(Message msg){
       //拿到了mPserson对象,随你处置
    }
};

上面的代码不就是走的dispatchMessage(msg);的第三种逻辑,重写handleMessage方法嘛!


其实,从上面的代码中我们或多或少的能多出来点东西(Handler发消息,处理的过程却是在Looper里面):在Handler中发送消息,其实就是向Looper发送消息,具体点说就是向Looper里面的MessageQueue队列中发送消息


接下来,我们看看Handler怎么找到Looper给它发送消息的,也就是说,也Handler的内部怎么和Looper进行关联的?

我在这里简单的叙述下过程哈,在叙述的过程中贴上一下源码。
首先,我们先来看看咱们最最最常用的UI线程,每天都和它打交道:

Main线程(所有应用程序更新UI的线程:ActivityThread),在创建这个Main线程的过程中,Main线程中就会创建一个Looper,而创建Looper的过程中,会创建一个MessageQueue。所有的Hanlder进行关联取出的消息队列都是通过默认的UI线程的Looper当中进行关联的:
我们找到这个ActivityThread类,它里面有个main()方法

ActivityThread类
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 main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ....
    Looper.loop();
    ....
}

我去,就两个东西呀!
其实这两个东西对我们理解Handler关联主线程的Looper已经足够了!
追一下prepareMainLooper()方法:

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()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

一句句看:
①首先是prepare(false);

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));
}

这个sThreadLocal.get() 就是拿到主线程里面的Looper对象的方法,咱们先略过这个方法,也留个谜(记录一下,留了两个谜了),后面再看它的源码,这里只需要知道在主线程里面创建Looper,你不得先判断了一下人家之前有没有Looper对象吗,有就复用,没有才给人家创建呗,就这逻辑。
OK,sThreadLocal.set(new Looper(quitAllowed));这里就是创建Looper对象了,看到了没,set参数里面直接调用new Looper(quitAllowed),奥,原来在这里创建的!

Looper类
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

是不是,和我们上面说的一模一样,创建Looper的时候就会创建一个MessageQueue,赋值给Looper类的mQueue变量,MessageQueue确实在Looper里面,嘿嘿。至于参数quitAllowed,它默认为true,从名字可以看出来就是消息循环是否可以退出,默认是可退出的。
然后接着看:
sThreadLocal.set(new Looper(quitAllowed));
主线程把刚创建的Looper对象给sThreadLocal.set()了:

ThreadLocal类
/**
 * 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);
}

可以看到,这是个存储操作,把主线程创建的这个Looper存储到ThreadLocalMap里面了,key是当前的主线程,value就是Looper。
好了,这时候我们可以看sThreadLocal.get()这个方法了:
几乎可以不用看它就知道是从ThreadLocalMap里面通过主线程这个key来拿主线程的Looper对象:

ThreadLocal类
/**
 * 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();
}

哈哈,看到没,还真是!

②其次sMainLooper = myLooper();:

sMainLooper = myLooper();

哈哈,这个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();
}

没啥东西,接着就是sThreadLocal.get();
不用追了,就是上面的那个,从ThreadLocalMap里面通过主线程这个key来拿主线程的Looper对象,所以,sMainLooper = myLooper();就是把主线程的Looper对象赋值给sMainLooper变量。

OK,主线程里面的Looper我们看完了,我们再来看看Handler里面的一些东西,这个就很简单了。直接说一下吧:
首先看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 that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;     
    mAsynchronous = async;
}

嘿嘿,简化一下:

public Handler(Callback callback, boolean async) {
    ....
    mLooper = Looper.myLooper();
    ....
    mQueue = mLooper.mQueue;
    ....
}

看到了吗,又是上面的myLooper方法,拿到了主线程的Looper对象,赋给了Handler的mLooper,然后调用mLooper.mQueue,又把主线程Looper里面的MessageQueue赋值给了Handler的mQueue。至此,Handler就已经和主线程的Looper关联起来了,通过send等方法给主线程Looper里面MessageQueue发消息,然后主线程Looper通过loop()方法取出MessageQueue里面的消息,通过你重写的各种回调方法callback来执行结果操作。

补充一下知识点

走到这里,Handler的原理基本差不多了
但是这里还要补充的两点是:


以上

参考

Android中为什么主线程不会因为Looper.loop()里的死循环阻塞?

上一篇 下一篇

猜你喜欢

热点阅读