Android Handler消息机制不完全解析

1.Handler的作用

Android开发中,我们经常使用Handler进行页面的更新。例如我们需要在一个下载任务完成后,去更新我们的UI效果,因为AndroidUI操作不是线程安全的,也就意味着我们不能在非UI线程中去操作UI,否则会抛出CalledFromWrongThreadException异常。

Handler的基本用法:

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            //do something
        }
    }
};

Handler handler1 = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            //do something
        }
        return false;
    }
});
handler.sendEmptyMessage(1);
handler.post(new Runnable() {
    @Override
    public void run() {
        
    }
});
...

上面是我们熟悉的Handler的用法,通过几天的源码阅读,今天写下我对Handler的不完全解析吧。为什么说不完全呢?因为能力有限,所以并没有包括native代码的解析。手动滑稽。


2.源码不完全解析

提前声明下,这里的解析基本上都是建立在网上查阅资料以及自己源码的理解。如果有问题,请指出。
在这里我们先查看Handler的构造方法

//无参构造方法
public Handler() {
    this(null, false);
}

//参数为Looper的构造方法
public Handler(Looper looper) {
    this(looper, null, false);
}

//参数为Callback的构造方法
public Handler(Callback callback) {
    this(callback, false);
}

//参数为Looper和Callback的构造方法
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}

从构造方法看,他们竟然后面的都有一个值为false的参数,我们随便看一个构造方法。

/** 
* @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) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        //在子线程中创建Handler的时候会抛出的异常。
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//把Looper的MessageQueue赋值给Handler的mQuene属性
    mCallback = callback;//Callback赋值
    mAsynchronous = async;//是否为异步的标志
}

看这个方法的对最后一个参数的注释我们可以知道,最后一个参数是是否异步执行的标志,这个方法又是被隐藏的 @hide ,我们无法显示的调用这个构造方法,但是可以通过反射来调用这个构造方法(待测试)。OK,那么我们可以知道,我们所有的构造方法都是同步的,并没有异步执行。

上面的构造方法中包括了一个Looper对象,一个MessageQueue对象,我们来看看这两个到底是什么?什么时候创建的?

先来看看Looper

public final class Looper {
    private static final String TAG = "Looper";
    // sThreadLocal.get() will return null unless you've called prepare().
    //使用ThreadLocal来确保一个线程中只存在一个Looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;
    ...
}

我们接着看下 Looper.myLooper() 这个方法:

public static @Nullable Looper myLooper() {
    //直接把sThreadLocal中的Looper对象返回
    return sThreadLocal.get();
}

我们可以看到该方法直接返回当前线程的Looper,如果为null的话,那么抛出了一个异常Can't create handler inside thread that has not called Looper.prepare(),当然啦,我们的Handler在主线程中创建的,所以我们得到的Looper对象一定是不为null的。为什么这么肯定呢?这要从App的启动流程说起(有点扯远了,也是我下个阶段正在看的)。我们知道Android 系统基于Linux的,我们每个App都相当于一个是系统的子进行。举个栗子:我们的Eclipse(好久不用了)中有很多个工程,当我们执行某一个工程的时候就相当于Eclipse给我们的工程创建了一个进程,通过调用工程的入口方法——main()方法来启动。我们App启动也是这个流程,入口在ActivityThread中。我们来看下main()方法:

public static void main(String[] args) {
    ...
    //创建mainLooper。这里mark下,一会要看
    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");
}

我们要注意的是 Looper.prepareMainLooper() 这个方法:

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");
    }
    //创建一个Looper,并和当前线程“绑定”
    sThreadLocal.set(new Looper(quitAllowed));
}

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

//ThreadLocal
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对象,并与当前线程绑定,接下来看下Looper的构造方法:

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

构造方法只是创建了一个MessageQueue对象,这里简单看下MessageQueue的构造方法:

//quitAllowed是否允许停止
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();//native方法
}

我们这里看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
        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 {
            //调用Handler的dispatchMessage方法
            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();
    }
}

从构造方法可以看出,我们的mainLooper是不允许停止的。看到这里我们要转回去看看我们的Handler sendEmptyMessage 是如何处理的:

public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();//生成一个Message对象
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}   

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

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;//这里注意下,把当前Handler对象赋值给target,后面会用到
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

上面的代码也挺简单,执行过程中如果 Messagenull 的话,就生成一个Message,把当前Handler赋值给Message对象的target属性,然后调用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);//唤醒Looper.loop()中的queue.next(),继续执行
        }
    }
    return true;
}

从上面的代码看到,我们的Handler把这条消息放入到MessageQueue的尾部,即先进先出,如果这个队列中没有消息,即当前处于阻塞状态,则唤醒Looper.loop() 中的 queue.next(),继续处理这个消息。我们上面提到了Looper.loop() 这个方法,它是一个死循环,如果当前的消息不为null的时候就去执行msg.target.dispatchMessage(msg),去处理这个消息:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //mCallback的handleMessage方法如果返回true,则不会继续执行handleMessage方法
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    //这个callback是通过handler.post(new Runnable)方式传递过来的,执行其run()方法
    message.callback.run();
}
    
public interface Callback {
    public boolean handleMessage(Message msg);
}
    
public void handleMessage(Message msg) {
}

在处理消息的时候,先判断msg.callback是不是为null,如果不为null,则执行其run()方法,否则判断Callback是否为null,如果不为null则执行Callback 中的 handleMessage方法,并根据其返回值来判断是否执行HadlerhandleMessage方法。 所有的方法都是在当前线程去执行的。

By the way, ViewRootImpl中有一个checkThread()方法,用来判断是不是处于 当前 线程

由于能力有限这里我没有对native方法进行解析,有机会的话再去深入研究下。


3.我的理解

简单的说,我认为Looper、Handler就是让当前线程执行当前线程中所有的消息,Handler对象在哪个线程被创建,其消息的执行将会在被创建的那个线程中执行。

基于Handler和Looper的源码,我自己写了一个类似Handler消息机制,便于理解和记忆。源码在GitHub上,欢迎讨论。

posted @ 2017-03-18 23:37  风起云涌,勿忘本心  阅读(301)  评论(0编辑  收藏  举报