Handler源码解读——handler使用时的注意事项

    工作中经常会遇到从子线程发送消息给主线程,让主线程更新UI的操作,常见的有handler.sendMessage(Message),和handler.post(runnable)和handler.postDelayed(runnable,milliseconds);一直在使用这些方法,却不知道他们的原理,今天就来解释一下他们的原理。

    先来一个常见的handler.sendMessage(message)的例子吧。代码如下:
    定义一个handler的子类,并实现它的handleMessage()方法:这里的msg.what的值是你自己定义的,就不多解释了。
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1000:
                int arg = msg.arg1;
                // TODO: 17/3/22
                break;
            case 1001:

                break;
            default:
                break;
        }
    }
};

接下来是在子线程中发送消息的部分:

//handler.sendMessage(Msg)
new Thread(new Runnable() {
    @Override
    public void run() {
        Message msg = Message.obtain();
        msg.what = 1000;
        msg.arg1 = 10;
        handler.sendMessage(msg);
    }
}).start();
    可以看到,我在一个新开的子线程中使用刚才定义的handler发送了一个Message对象,这个Message对象可以携带数据,这里简单起见,我只携带了一个int类型的值10.
    至于为什么在子线程中使用handler发送的Message,可以在UI线程中执行了呢?这是因为在Android的主线程中有一个Loop循环器,它一直在轮询这个Loop循环器所属的MessageQueue消息队列中的消息,如果MessageQueue里面有Message,就调用这个Message.target.dispatchMessage(Message)方法,这里其实是调用了handler对象的dispatchMessage()方法,这个方法里面最终(满足一定条件)会调用handler的handleMessage(Message)方法,也就是在主线程中调用了我们刚才定义的handler的handleMessage(Message)方法。
    好吧,说了这么多,先来个精简版的简图,接下来再用源码解释原理:

这里写图片描述

ps: 上图所涉及的源码,可在Android Studio中可直接查看

1、ActivityThread类的main方法中,最后调用了Loop.loop()方法,开启了主线程。

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

2、Loop.loop()方法中:

  源代码注解:在这个线程中一直调用MessageQuene队列,确保loop被quit之前一直调用。
/**
 * 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();
 }
}

①获取当前线程的loop对象。
②获取loop中的MessageQueue。
③使用for( ; ; )的开启一个死循环,开始轮询MessageQueue中的消息。
④如果消息不为空,则调用Message对象的target的dispatchMessage(msg)方法

3、msg.target.dispatchMessage(msg)

Message类中:
    /*package*/ Handler target;

①target对象为Handler类型,表示发送这个消息的handler对象
②可以通过obtain的一系列重载方法给message设置target,通过无参数的obtain方法获取到的message的handler为null;
③target对象有set和get方法

public void setTarget(Handler target) {
    this.target = target;
}

/**
 * Retrieve the a {@link android.os.Handler Handler} implementation that
 * will receive this message. The object must implement
 * {@link android.os.Handler#handleMessage(android.os.Message)
 * Handler.handleMessage()}. Each Handler has its own name-space for
 * message codes, so you do not need to
 * worry about yours conflicting with other handlers.
 */
public Handler getTarget() {
    return target;
}

④至于我们这个handler发送的message对象中的target是如何与handler勾搭上的,留待后边说明。

4、Handler.dispatchMessage(message)方法:

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

这段代码表明,dispatchMessage方法中可能会调用handleMessage(msg)方法,不过需要满足几个条件。好吧,这个问题留待后边解决。我们现在已经简单的看完了handler在子线程发送消息,最终在主线程进行处理的流程。接下来我们需要解决过程中遇到的几个困惑。

困惑一:第3步时,handler发送的message对象中的target是如何与这个handler勾搭上的。
困惑二:第4步时,在dispatchMessage方法中,执行handleMessage(msg)方法的条件如何解释。

解答一:handler发送的message的target对象是如何与这个handler勾搭上的。
①我们得从handler发送消息说起,先来看我们在子线程中发送消息的代码:来找找handler对象是何时设置给message.target的。

new Thread(new Runnable() {
@Override
 public void run() {
Message msg = Message.obtain();
 msg.what = 1000;
 msg.arg1 = 10;
 handler.sendMessage(msg);
 }
}).start();

②点进Message.obtain()方法:

/**
 * 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类型,是为了Message的复用,这里没有设置target的数据,再看最下边的new Message();

/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}

好吧,这里是个空实现,啥也没有。
③既然在Message里面没有找到,我们就在handler.sendMessage(msg)里面找找,点进去:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

没有,继续找:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
     }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

继续:

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;
 if (mAsynchronous) {
msg.setAsynchronous(true);
 }
return queue.enqueueMessage(msg, uptimeMillis);
}

好了,找到了。enqueueMessage()方法的第一步就给msg.target设置了值,就是当前的handler对象,也就是发送消息的那个handler。这个方法的最后一句将这个msg添加进当前的MessageQueue,这样,进入队列的message就携带上Handler类型的target对象了。
到现在为止,我们已经说明,在Loop.loop()方法里的那句msg.target.dispatchMessage(msg),调用的就是当前hander对象的dispatchMessage(msg)方法了。

解答二:在dispatchMessage方法中,执行handleMessage(msg)方法的条件是怎么回事
①先上dispatchMessage(msg)源码:

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

具体流程如图所示:
这里写图片描述

疑惑又来了,msg.callback是什么鬼?handleCallback(msg)方法是干嘛的?mCallback是什么鬼?它后边的mCallback.handleMessage(msg)方法又是干嘛的?
②msg.callback和handleCallback(msg)方法;
进入Message类中,发现callback成员变量为Runnable类型,Runnable接口自不必多说,它里面就一个无参无返回值的run方法;
在Message中是搜索callback,发现可以给它赋值的位置只有静态方法obtain()的两个重载方法:

public static Message obtain(Message orig) {
Message m = obtain();
 m.what = orig.what;
 m.arg1 = orig.arg1;
 m.arg2 = orig.arg2;
 m.obj = orig.obj;
 m.replyTo = orig.replyTo;
 m.sendingUid = orig.sendingUid;
 if (orig.data != null) {
m.data = new Bundle(orig.data);
 }
m.target = orig.target;
 m.callback = orig.callback;

 return m;
}
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;

    return m;
}

再看一眼刚才的handleCallback(msg)方法:它就是调用message.callback.run方法。

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

也就是说,只有当我们直接通过Message.obtain(Message)和Message.obtain(Handler,Runnable)方法来获取Message对象的时候(或者设置message.callback的值不为空),才有可能让msg.callback不为空,才会调用callback.run方法,此时就绝对不会调用我们的handleMessage(msg)方法。

③接下来看mCallback和mCallback.handleMessage(msg)是何方神圣;
mCallback是Handler里面定义的一个Callback接口类型,

/**
 * 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的时候,可以通过这个可以接口避免自己写一个Handler的实现类。
这句话是什么意思呢?且往下看。
在Handler类里面找给mCallback赋值的地方,找到三处,都是Handler的带参构造方法:

*public Handler(Callback callback) {
    this(callback, false);
}*
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(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

看到这儿,Handler里面这个接口的注释应该很好理解了。如果你使用上边这两个Handler的构造方法来实例化一个Handler对象,那么你需要传入一个Callback接口类型的参数,在callback的handleMessage(msg)方法里面,你就可以直接写主线程处理Message的逻辑了;此时我们就不需要再写Handler的实现类,重写handler的handleMessage(msg)方法了;另外,这个Callback接口里的handleMessage(msg)方法有返回值,如果你返回为true,就不会执行handler.handleMessage(msg)方法,如果返回为false,依旧会执行handler.handleMessage(msg)方法。这就给我们使用handler提供了多种选择。

这里插一句嘴,上方的两个Handler的构造方法中,都有一个boolean类型的async参数,同学们可能不知道怎么调用,没关系,看看Handler()这个空参构造是怎么写的:

public Handler() {
    this(null, false);
}

这个this(null,false)调用的就是Handler(Callback callback,boolean async)这个方法,也就是说,默认的我们传入的async这个参数为false就可以。至于这个参数何时为true,何时为false,这个问题留待后续研究,这次不做讲解。

好,原理和流程解释完了,来对关键流程做下总结。
这里写图片描述

handler.dispatchMessage(msg)里面的处理,前面已经解释过了,这里不再重复。

使用总结:
①在使用handler发送消息的时候,要记住,自己实现的handleMessage(msg)方法不一定会执行。
②如果我们发送的Message的callback参数(Runnable类型)不为空,那么就不会执行handler的handleMessage(msg)方法,只会执行message.callback的run方法。这种情况主要发生在我们使用handler.post(runnable)和handler.postDelayed(runnable,milliseconds)方法发送消息的时候。另外,在Message对象的callback的参数不为空的情况下,如果我们在创建Handler对象的时候既传入了Callback接口参数,也重写了里面的handleMessage(msg)方法,那么此时依旧只会执行message.callback的run方法。
③在我们发送的Message的callback参数(Runnable类型)为空的情况下(默认为空),在使用Handler的带参构造方法初始化Handler的时候,如果传入了Callback类型的参数,并且那么这个接口里面的方法肯定会被调用;如果同时我们也重写了该handler对象的handleMessage(msg)方法,那么handler对象的handleMessage(msg)方法不一定执行,取决于Callback接口中handleMessage(msg)方法的返回值,如果为true,那么handler对象的handleMessage(msg)方法不会执行,为false时,handler对象的handleMessage(msg)方法会在后边执行。

关于handler的总结,希望对你有用。

猜你喜欢

转载自blog.csdn.net/qq_26287435/article/details/64936888