Handler从使用到崩溃

目的

这个目的是我今天写这篇文章的目的,希望通过通俗易懂的语言将Handler这个东西给大家讲清楚,当然还包括和它关系很密切的Looper、Message和MessageQueue。

从用法开始

public class HandlerTestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_test);
        Handler mainHandler = new Handler();
    }
}

这段代码并没有什么意义,大家可以运行一下,并没有什么问题,它的主要目的是为了引出下面这段代码:

public class HandlerTestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_test);
        Handler mainHandler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Handler threadHandler = new Handler();
            }
        }).start();
    }
}

什么!?崩溃了!点题了,就这么从使用到崩溃了!!!通过崩溃的堆栈信息:

FATAL EXCEPTION: Thread-103
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    at android.os.Handler.<init>(Handler.java:197)
    at android.os.Handler.<init>(Handler.java:111)
    at com.jucongyuan.handlertest.HandlerTestActivity$1.run(HandlerTestActivity.java:16)
    at java.lang.Thread.run(Thread.java:856)

(这里开始需要借助Android Framework的源码了),我们看看Handler中这次执行过的代码:

public class Handler {
    public Handler() {
        this(null, false);
    }
    public Handler(android.os.Handler.Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends android.os.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()");
        }
        /** 这里可以查崩溃的原因 **/
        /** 这里持有了mLooper的mQueue,现在不用管,待会你会回来看的 **/
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
}

Looper隆重登场,原来是在构造函数中,mLooper为空时抛出了一个异常,就是我们崩溃日志中的异常,查看Looper源码,这里就不贴源码了,大家可以自己查看,通过源码,知道Looper是从Handler所在的线程中取到的,也就是说,threadHandler所在的我们自己创建的那个线程中没有Looper,回到最初,我们在主线程也就是UI线程中也实例化了Handler,没有报错,所以我们猜想UI线程实例化时,一定同时实例化了一个Looper,求证一下,看看Framework源码中创建主线程的代码了,在AcitivityThread中,有一个main方法:

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

    AndroidKeyStoreProvider.install();

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

我们的猜想是正确的,通过调用prepareMainLooper方法,最终调用了Looper的私有构造方法:

Looper.Java

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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

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

这个Looper也就归属了主线程。同时注意,我们在线程中调用prepare()方法时,如果这个线程已经存在Looper了,会抛出一个Only one Looper may be created per thread,就是说一个线程有只能有一个Looper,所以如果我们在主线程中再调用Looper的prepare方法为主线程实例化一个Looper,就会报错。我们上面的那个崩溃大家应该知道怎么解决了:要为线程创建一个Looper:

public class HandlerTestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_test);
        Handler mainHandler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler threadHandler = new Handler();
            }
        }).start();
    }
}

在Looper的构造函数中,MessageQueue隆重登场了!在看MessageQueue之前,我们先小小总结一下,毕竟到现在信息量还是比较大了:

  1. 要在线程中使用Handler,该线程必须有一个Looper
  2. Looper的构造方法是私有的,我们在线程中通过调用Looper的prepare()方法为该线程生成一个Looper。主线程调用prepareMainLooper方法生成是为了我们方便通过Looper.getMainLooper()方法获取主线程的Looper
  3. 每个线程只能有一个Looper,可以没有(没有不能使用Handler?作为一个提高题大家自己寻找答案)
  4. Looper会持有实例化它的线程和一个消息队列MessageQueue实例
  5. Handler会通过Looper持有它的MessageQueue实例(上面Handler的源码说你会回去看的地方)

随着MessageQueue的出场,现在我们要说的四个类就只剩下Message了,我们直接看Message类:

public final class Message implements Parcelable {

    public int what;

    public int arg1; 

    public int arg2;

    public Object obj;

    public Messenger replyTo;

    public int sendingUid = -1;

    static final int FLAG_IN_USE = 1 << 0;

    static final int FLAG_ASYNCHRONOUS = 1 << 1;

    static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    int flags;

    long when;

    Bundle data;

    Handler target;

    Runnable callback;

    Message next;
}

最后三个属性我们记住,待会会使用,同时Message next也说明Message类是一个单向链表的数据结构。

发送一条Message

我们使用Handler最常用的方法就是sendMessage(Message msg),来看看整个过程吧:

public class Handler {
    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);
    }
}

发送消息时,其实是调用了MessageQueue的enqueueMessage方法,这个MessageQueue是哪里来的呢,上面的结论5中已经总结,在看MessageQueue之前,我们注意msg.target = this;(Message最后三个属性的中的一个),每一条Message都持有了发送它的Handler的。接下来是MessageQueue的enqueueMessage:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

这段代码其实也很简单,我们注意最下面else的内容,他其实就是在通过Message的when属性,把这条Message插入到这个MessageQueue中Message单向链表的合适位置。但是,消息到这里就结束了啊,结束了?看这里的代码的确是结束了,当时我们知道消息最后是会到Handler的handleMessage(Message msg)方法中,这个方法在哪里调用的呢?上面的源码注释中,还有一个地方我提到:注意这里,我们待会要再来看它,那就是Looper.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
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

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

一目了然,一个for (;;)死循环一直不断的从MessageQueue中取消息Message,并且通过msg.target.dispatchMessage(msg);调用了Handler的dispatchMessage方法(上面的结论,Message持有了发送它的Handler对象,就是target),到这里,我们应该可以猜到,Handler的dispatchMessage方法中一定调用了Handler的handleMessage(Message msg)方法:

public void handleMessage(Message msg) {
}

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

猜想同样正确。整个过程无缝的串联起来了,这里Message最后三个属性的中的callback也是出现了,如果msg.callback不为空就是调用的handleCallback方法,而handleCallback方法如下,这个callback是哪里来的呢?callback其实就是Runnable,它就是我们调用Handler的post方法传入的Runnable,大家可以通过源码从post方法调用开始分析整个流程。

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

再来一次总结:

  1. Handler发送消息时,调用Looper中MessageQueue的enqueueMessage方法来组织消息
  2. Looper通过loop方法不断的从MessageQueue中去消息,发送Message到Message持有的Handler中由handleMessage方法执行

最后一个结论有点奇妙,我们通过Handler发消息就是为了让Handler中的另一而方法执行,这不是劳民伤财吗?不过大家想一下Handler的用法:线程间通讯。为什么线程间需要这样通讯,因为在Android中,非UI线程(非主线程)中我们不能更新UI,所以我们可以借助Handler来发送一些消息更新UI,常见的功能如:下载进度的更新……

大总结

这篇文章只是通过一步一步看源码来分析Handler、Looper、Message、MessageQueue和Thread之间的关系,其实他们的用法还很多很多,从他们不止一个的构造方法就可以看出来,但是我们要更好的使用它必须先了解它最简单的一面,然后再继续深入,看源码是非常重要的一种方式。

坚持原创分享,您的支持将鼓励我不断前行!