天天看点

Android Handler总结3-源码分析

Handler机制

Android的消息处理有三个核心类:Looper,Handler和Message(MessageQueue封装到了Looper类里面)

Message类

android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,Message类定义的变量和常用方法如下:

(1)public int what:变量,用于定义此Message属于何种操作

(2)public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息

(3)public int arg1:变量,传递一些整型数据时使用

(4)public int arg2:变量,传递一些整型数据时使用

(5)public Handler getTarget():普通方法,取得操作此消息的Handler对象。

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,但是有这么几点需要注意:

(1)尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。

(2)如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存

(3)擅用message.what来标识信息,以便用不同方式处理message。

(4)使用setData()存放Bundle对象。

Looper

在使用Handler处理Message时,需要Looper(通道)来完成。

在一个Activity中,系统会自动帮用户启动Looper对象,而在一个用户自定义的类中,则需要用户手工调用Looper类中的方法,然后才可以正常启动Looper对象。

Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
         
        // ...其他处理,如实例化handler
         
        // 开始循环处理消息队列
        Looper.loop();
    }
}      

1、Looper.prepare()

为线程创建Looper对象,一个线程只能有一个Looper对象。我们调用该方法会在调用线程的TLS中创建Looper对象,Looper.prepare()源码如下:

Android Handler总结3-源码分析

每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象,源码如下:

Android Handler总结3-源码分析

2、Looper.loop()

循环获取MQ中的消息,并发送给相应Handler对象。

调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。其核心源码如下:

public static void loop() {
        final Looper me = myLooper();     //得到当前线程的Looper对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;     //得到当前Looper的MQ
        
        ....
        
        for (;;) {
            Message msg = queue.next();      // 可能阻塞
            if (msg == null) {
                return;
            }

           ......
           
            try {
                msg.target.dispatchMessage(msg);    //!!!!!!!将真正的处理工作交给message的target
               .....
            } finally {
              .....
            msg.recycleUnchecked();     //回收Message资源
        }
    }      

Handler

Message对象封装了所有的消息,而这些消息的操作需要android.os.Handler类来处理。

什么是Handler?Handler起到了处理MQ上的消息的作用(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。

Handler创建时会关联一个Looper,默认的构造方法将关联当前线程的Looper,不过也可以手动设置。

一个线程可以有多个Handler,但是只能有一个Looper!

关键源码如下:

public Handler(Callback callback, boolean async) {
        .......
        //默认关联当前线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }      

有了handler之后,我们就可以使用

post(Runnable)

postAtTime(Runnable, long)

postDelayed(Runnable, long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message, long)

sendMessageDelayed(Message, long)

这些方法向MQ上发送消息了。post发出的Runnable对象最后都被封装成message对象,源码如下:

Android Handler总结3-源码分析
Android Handler总结3-源码分析

post发出的message,其callback为Runnable对象。其他方法最终都会调用sendMessageAtTime(),sendMessageAtTime()源码如下:

Android Handler总结3-源码分析
Android Handler总结3-源码分析

最终会调用MQ的enqueueMessage方法,完成向MQ插入一个message的工作。

Handler处理消息

分析了message的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg),源码如下:

Android Handler总结3-源码分析

Handler先执行msg的callback方法(post Runnable的方式),如果不是这种方式,则执行自己的callback方法(不是message的callback,该callback可以在创建Handler的时候设置,带有返回值,返回true则表示消息已处理,不再继续执行handlerMessage),如果callback返回false表示message未被处理掉,继续执行handlerMessage方法。

注:

1、Handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上。

2、消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,handler是在它关联的looper线程中处理消息的。

MessageQueue

MessageQueue,主要包含2个操作:插入和读取。

读取操作会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除。

虽然MessageQueue叫消息队列,但是它的内部实现并不是用的队列,实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

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;
   
    int flags;

    long when;

    Bundle data;

    Handler target;

    Runnable callback;

    Message next;   //通过next形成链表      

MQ的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;
    }      

在Message的源码中定义了一个成员属性target,其类型为Handler。由上面enqueuMessage的源码,我们可以看到,当Message没有处理其的Handler或该Message正在被处理的时候,都不能正常进入MessageQueue,这一点也是很容易理解的。当线程处于死亡状态的时候,Message会被回收掉,而不再进入该线程对应的MessageQueue中。否则,一切正常,enqueMessage就执行单链表的插入操作,将Message插入到MessageQueue中。

MQ的next()源码如下:

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }      

通过源码我们可以知道,next方法会不停地去循环读取MessageQueue中的Message。若MessageQueue中没有消息了,则next方法会暂时阻塞( nextPollTimeoutMillis = -1)。有消息到来时,next会继续读取消息,返回该消息,并将其从单链表中移除。

UI线程的Looper

当我们在Activity中创建一个Handler对象时,我们不需要写Looper.prepare和Looper.loop就可以默认绑定UI线程的Looper,那么UI线程的Looper对象是何时创建的呢?

public static void main(String[] args) {
        ....
 
        //创建Looper和MessageQueue对象,用于处理主线程的消息
        Looper.prepareMainLooper();
 
        //创建ActivityThread对象
        ActivityThread thread = new ActivityThread(); 
 
        //建立Binder通道 (创建新线程)
        thread.attach(false);
 
        Looper.loop(); //消息循环运行
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }