Android Handler、Looper、HandlerThread及Message源码解析

读前须知

        本文是对Android Handler源码的解析,对Handler的基本使用还不了解的朋友可以先快速学习一下Handler再来阅读本文。文章篇幅较长,可以根据需要跳着阅读,或按顺序耐心阅读。

Handler

最直接的创建方法就是不带参数new一个Handler对象了。

Handler handler = new Handler();

//Handler.java
public Handler() {
     this(null, false);
}
...
private static final boolean FIND_POTENTIAL_LEAKS = false;
...
public Handler(Callback callback, boolean async) {
		\\1
        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());
            }
        }

		\\2
        mLooper = Looper.myLooper();
        \\3
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //4
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

直接跳进构造方法。看到代码块1,它是用来监测Handler创建方式有没有内存泄漏的风险的。一个类继承了Handler类。
        A:匿名类 或 内部类 或 局部类
        B:不是静态的类
如果A和B都达成,就认为它的对象实例有内存泄漏的风险。其实这很好理解,举个例子。

public class MainActivity extends AppCompatActivity {

    private Handler mDownloadHandler = new Handler(){
        ...
    };

	...
}

        MainAcivity已经destory掉了,mDownloadHandler由于是匿名内部类,它会持有MainAcivity的对象引用。GC时发现这个Activity还被其他可达对象引用,所以不能回收它,这就导致了内存泄漏。
        不过FIND_POTENTIAL_LEAKS是false的,且为常量,所以代码块1是不会执行的,这个代码块应该是编写Handler类的程序员拿来测试用的。接下来看到代码2,跳进myLooper看看。

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

        这里是一个关键的地方,从代码可以看出,每个线程都应该有只属于自己的Looper。
        继续看代码,到了代码3的会做一个判断,如果没能从这个线程获取到Looper,就抛出异常。

new Thread(){
    Handler handler=null;
    public void run(){
        handler =new Handler();
    };
}.start();

        在一个普通的线程上new一个Handler是会抛出异常的,因为普通的Thread并没有自己的Looper,不能让整个消息机制运作起来。需要特殊的Thread才能完成这样的任务,那就是HandlerThread,在后面会讲到。
        代码3能运行通过,默认Handler构造方法就基本执行完了,代码4,说明Handler和Looper要共同来维护一个消息队列。

...
final MessageQueue mQueue;
...

再看看另一种常用Handler构造方法,直接传入Looper,通常是用来处理异步任务的。

Looper looper = Looper.getMainLooper();
Handler handler = new Handler(looper);
...

//Handler.java
/**
...
@param looper The looper, must not be null.
...
*/
public Handler(Looper looper) {
      this(looper, null, false);
}
...
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}

        构造方法相当简单,就是赋初值,但注释里强调传入的Looper一定不能为空。Handler其他的构造方法也是差不多的。接下来就不得不说Looper和HandlerThread了,从Handler的构造方法就可以看出它们的重要性。

HandlerThread

HandlerThread类的代码其实不长,感兴趣的朋友可以全部读一遍,这里就挑重点来讲。

public class HandlerThread extends Thread {
    ...
    Looper mLooper;
    private @Nullable Handler mHandler;
	...
	@Override
    public void run() {
        mTid = Process.myTid();
        //1
        Looper.prepare();
        synchronized (this) {
        	//2
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
     	//3
        Looper.loop();
        mTid = -1;
    }
    ...
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
        	//4
            looper.quit();
            return true;
        }
        return false;
    }
    ...
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
        	//5
            looper.quitSafely();
            return true;
        }
        return false;
    }
    ...

快速地把上面的代码阅读完,将代码1234 或 1235串联起来那就是:
        1.生成一个Looper
        2.获取这个Looper
        3.进入Looper死循环,不断监听和处理消息。
        4.当不再需要监听时,结束死循环,这Thread结束,整个HandlerThread就是这么运行的。

Looper

从上面的代码可以看出来HandlerThread只是提供了一个运行的线程环境,真正在维护消息机制运作的是Looper。分别分析刚才代码1~5Looper所做的事情。先看代码1的prepare方法。

Looper.prepare();

...
public static void prepare() {
    //1.1
    //quitAllowed = true
    prepare(true);
}

...
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //1.2
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
...
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

        先看代码1.1,HandlerThread运行的消息处理器是可以被使用者退出的,也就是说Looper的控制权是部分对外开放的。那想想,什么Looper要把quitAllowed弄成false呢,那当然就是一些极为重要的Looper了,例如 Looper.getMainLooper()得到的主线程Looper。代码1.2,ThreadLocal在当前线程保存一个Looper的副本。
        再看到代码2,获得的一定是刚刚放进去的Looper对象,因为是在同一个线程里使用ThreadLocal的set和get的,一个HandlerThread始终只对应一个Looper。(对ThreadLocal不了解的朋友,可以先熟悉ThreadLocal的相关知识)。
        接下来就是整个消息机制的核心了:代码3的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;
...

Looper.loop()是一个静态方法,方法开始还是一样,先判断一下当前线程是否有Looper,继续看代码。

...
for (;;) {
	//1
    Message msg = queue.next(); // might block
    //2
    if (msg == null) {
    // No message indicates that the message queue is quitting.
        return;
    }
...            

接下来就会进入一个死循环,不断从队列里获取消息。代码1的注释说队列可能会阻塞。如果得到的为空,就退出Looper循环(在调用Looper的quit方法的时候才会得到空值)。这个阻塞队列MessageQueue是Google自己实现的,看看next方法看看是怎么获取消息的。

 Message next() {
 	//1
 	final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        Message prevMsg = null;
        //A
        Message msg = mMessages;
     	...
     	if (msg != null) {
     				//2
                    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);
                    }
                    //3 
                    else {
                        // Got a message.
                        //4
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {            
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //5
                        msg.markInUse();
                        return msg;
                    }
                }
                ...
                

        代码1处,当调用了Looper的quit方法后mPtr就会得到0,next方法返回null,进而使Looper的死循环结束。代码A,mMessages就是消息链表头。
        代码2的判断就是在问现在要不要处理此消息(也就是问这是不是一个延时消息,有的消息发送后不是立刻就要处理的,例如:Handler的sendMessageDelayed方法),延时时间还没到就先不处理,否则就进入代码块3立刻处理。
        由于有需要立刻处理的消息,所以在代码4处把阻塞标记设为false,由于这个消息要拿来处理,处理完要回收,为了能够更好的管理这个消息(后面会将其回收至Message池),代码5处标上使用标记,具体如下。

/** If set message is in use.
     * This flag is set when the message is enqueued and remains set while it
     * is delivered and afterwards when it is recycled.  The flag is only cleared
     * when a new message is created or obtained since that is the only time that
     * applications are allowed to modify the contents of the message.
     *
     * It is an error to attempt to enqueue or recycle a message that is already in use.
     */
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

代码5走完就可以返回获取到的消息了。如果没能从消息队列里获取到消息,那就继续往下走。

...
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
   dispose();
   return null;
}
...       

// Disposes of the underlying message queue.
    // Must only be called on the looper thread or the finalizer.
    private void dispose() {
        if (mPtr != 0) {
            nativeDestroy(mPtr);
            mPtr = 0;
        }
    }   
...        

        如果没有获取到消息,且调用了Looper的quit方法,mQuitting标记就会为true,进而把mPtr设为0,当下次在回到刚刚next方法的代码1处时,Looper死循环就要退出了。
        那要是Looper既没有消息(或消息延时还没结束)又不打算退出Looper循环呢?那就做点空闲时才做的事。

			...
            // 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 {
                	//1
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                    	//2
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            ...

        代码1从空闲任务列表里取一个空闲任务来处理,如果这个空闲任务返回false,在代码2就把它移出空闲任务列表。添加空闲任务代码如下:

handler.getLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
            	//自己的空闲任务
            	...
            	//返回false,这个空闲任务就只会运行一次
                return false;
            }
        });

我们来看看整个next方法的流程图。
在这里插入图片描述
        实际上橙色方框是在for(;;)这样的死循环中不断运行的,直到有消息才返回。所以next方法所谓 “没有消息就阻塞” 其实并没有把Looper所在的线程阻塞掉,只是让这个线程处理空闲任务或空转罢了。
        如果这是MainLooper那就要注意了,不要在空闲任务上做耗时的事,空闲任务太耗时就会导致Looper不能及时响应接收的消息。next方法就分析完了,继续看Looper死循环里接下来做的事。

...
			try {
				//1
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
...

看到代码1,target其实就是发送Message的Handler,跳进去dispatchMessage方法看看。

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

        代码2就是我们熟悉的handleMessage方法了,如果message有回调就运行代码1。这么一来,这个消息就到达目的地了。但是,这其实还没完,Message到达目的地、接收者处理完后,Looper还要对这个Message进行回收供下次使用,继续看Looper循环的代码。

...
msg.recycleUnchecked();

这个代码是最后一步。运行完它,Looper死循环就走完一轮了,跳进去看看。

/**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
        	//1
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

        首先做的事情很简单,把该初始化的值初始化、该设null的设null。再看到代码块1,这里就是回收Message的地方,sPool是Message池。看完代码块1其实就可以知道这个Message池本质上就是一个链表,sPool是链表头。

Message

提到Message回收,我们再看看Message的获取,如下是获取Message的方式。

//A
Message message = new Message();
//B
Message message1 = Message.obtain();

这两种获取Message的方式都可以,先跳进Message构造方法看看。

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

        没有做任何事情,上面的注释推荐我们用Message.obtain();回顾一下我们刚刚分析的next方法,在获取到一个消息后会标上使用标记。

//5
msg.markInUse();
return msg;

        我们通过A方式获取的Message在发送出去后会被标上使用标记。而用完后会被回收进Message池里。那问题就来了,如果每次我们都是通过A方式来获取Message,用完后被回收至Message池,那Message池就会越来越大,容易造成浪费。那B方式好在哪里呢?进去看看。

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

        代码很简单,先从Message池里找找有没有闲置的Message,没有再new一个,等发送完毕后再回收到Message池里。
        接下来再看看消息发送,常用的方式有如下三种

handler.sendEmptyMessage(MSG_UI)
handler.sendMessage(msg);
handler.sendMessageDelayed(msg,1000);

但其实最终都是调用了sendMessageDelayed方法,就去看看。

/**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

再深入sendMessageAtTime方法。

/**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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;
        }
        //1
        return enqueueMessage(queue, msg, uptimeMillis);
    }

代码1将消息入列,进去看看。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

再深入MessageQueue的enqueueMessage方法看看。

boolean enqueueMessage(Message msg, long when) {
        	...
        	//1
            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;
                //2
                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;
            }

            ...
        return true;
    }

        代码1将消息标上标记,通过new方式得到的Message在这里被标记,使用完毕后回收至Message池。
        代码块2的循环是通过时间来决定插入位置的,delay的时间越长越是排在后面。
在这里插入图片描述
找到插入位置插入消息就可以了。这么一来sendMessage就完成了。

ActivityThread及MainLooper

        更新界面的Handler需要使用主线程的Looper,那主线程的Looper又是怎么初始化的呢?这就要提到另一个拥有Looper的线程:ActivityThread

public final class ActivityThread extends ClientTransactionHandler {
	...
	final Looper mLooper = Looper.myLooper();
	...
	public static void main(String[] args) {
      	...
      	//1
        Looper.prepareMainLooper();
    	...
    	//2
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
        	//3
            sMainThreadHandler = thread.getHandler();
        }

        ...
        //4
        Looper.loop();
		
		//5
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
	...

这是我们最熟悉的main方法。代码1先准备好MainLooper,进去看看。

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

...
//MessageQueue.java
void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
...

        prepare方法传入false,也就是不允许退出,调用MainLooper的quit方法会直接抛出异常,它是非常重要的Looper当然不能随便就quit掉。再看看代码2,ActivityThread的构造方法。

ActivityThread() {
    mResourcesManager = ResourcesManager.getInstance();
}

做的事情很简单,就是new一个资源管理器,资源管理器是个单例。接下来是代码3,获取主线程的Handler

...
final H mH = new H();
...
final Handler getHandler() {
     return mH;
}
...

主线程的Handler类名就叫H,简单看看它的handleMessage方法做了什么。

public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
            	...
				case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case UNBIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
                    handleUnbindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case SERVICE_ARGS:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
                    handleServiceArgs((ServiceArgsData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case STOP_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
                    handleStopService((IBinder)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                    ...

        这里看到了Service的相关处理,这也就是为什么说耗时的任务不要直接在Service上运行,这些任务可以在Service中创建子线程来完成或用IntentService。
        接着就是代码4,MainLooper启动它的死循环,这样一来,整个APP就可以监听更新UI的Message了。

runOnUiThread方法

runOnUiThread(new Runnable() {
            @Override
            public void run() {
				textView.setText("hello world");
            }
});

在Activity里我们可以通过它来更新界面,看看它的源码。

public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

        代码很简单,如果是在主线程上调用runOnUiThread方法,就立即执行Runnable里的代码。否则就放入消息队列排队执行。

参考文章

Asynchronous的作用
IdleHandler的作用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值