handler原理解析

Handler簡單使用

1.使用靜態(tài)內(nèi)部類的方式繼承Handler并重寫接受的方法handleMessage。之所以使用靜態(tài)內(nèi)部類,是因?yàn)殪o態(tài)內(nèi)部類不會持有外部類的引用
static class MyHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 0x11:
                    String str = (String) msg.obj;
                    Log.d(TAG,"msg content == "+str);
                    break;
                default:break;
            }
        }
    }

2.獲取Handler實(shí)例
private  Handler mHander = new MyHandler();

3.獲取Message對象
1)Message msg = new Message();
2)Message msg = Message.obtain();/mHander.obtainMessage();等
obtain.what = 0x11;
obtain.obj = "Message Content";

獲取Message方式有2中,建議使用第二種方式,因?yàn)榈诙N方法采用了緩存池機(jī)制,避免重復(fù)創(chuàng)建新的對象。

4.發(fā)送消息
mHander.sendMessage(msg);立即發(fā)出
mHander.sendMessageDelayed(obtain,time);延時發(fā)送

5.最后在handleMessage方法中處理消息

子線程創(chuàng)建Handler

在子線程中直接創(chuàng)建Handler會報錯
錯誤日志為:

Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()"

代碼中可以看到 Handler在創(chuàng)建時會判斷當(dāng)前線程的Looper是否為空

mLooper = Looper.myLooper();
if (mLooper == null) {
    throw new RuntimeException(
         "Can't create handler inside thread " + Thread.currentThread()
               + " that has not called Looper.prepare()");
}

為空的話則會報錯。myLooper方法是獲取當(dāng)前線程的Looper,Looper存儲早sThreadLocal

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

sThreadLocal是ThreadLocal<Looper>類型。ThreadLocal中的對象set/get方法獲取到的數(shù)據(jù)都是當(dāng)前線程的變量。
當(dāng)myLooper為空時則說明當(dāng)前線程Looper未初始化,而初始化的方法則是:

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

所以子線程中如果創(chuàng)建Handler則需要調(diào)用Looper.prepare()方法。而Looper.loop方法則是核心,消息的傳遞全依賴于此。

Handler原理

Handler的運(yùn)行機(jī)制依賴于MessageQueue與Looper的支持。
我們從代碼角度來看一下這三者之間的關(guān)系:
從入口Handler.sendMessage方法看起:

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}

sendMessage方法鏈看下來,最終調(diào)用了mQueueenqueueMessage方法,mQueue是MessageQueue類型,是Looper的成員變量
在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;
    }

從這邊可以看到MessageQueue內(nèi)部存儲采用的是鏈表格式,本次方法的作用是將消息插入到鏈尾。整體邏輯是鏈表為空時,將msg使用mMessages保存下來,然后喚醒。當(dāng)數(shù)據(jù)不為空時,遍歷鏈表將數(shù)據(jù)插入到鏈尾,并喚醒。
綜上所述Handler的sendMessage方法本質(zhì)上就是一次插入方法,目的是將消息插入到MessageQueue鏈尾。
我們知道在子線程中發(fā)送消息之后則需要調(diào)用Looper.loop方法,否則消息不生效。下面我們來看一下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;

        ...

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

            ...
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
        }
    }

上面是省略了的代碼,可以看到loop是個死循環(huán),唯一的退出操作是MessageQueue的next方法返回為空。當(dāng)Looper調(diào)用quit或者quitSafely方法的時候,next會返回為空,此時Looper會跳出循環(huán)。next方法是阻塞操作,當(dāng)沒有消息時,next會一直阻塞在那里,導(dǎo)致loop方法也阻塞在那里。直到next方法返回了新消息,Looper就會處理新消息。當(dāng)獲取到信息時,會通過msg.target.dispatchMessage(msg)處理,target就是發(fā)送這條消息的Handler(Handler的enqueueMessage方法中將自身設(shè)置給msg(msg.target = this)),最終消息鏈回到了Handler的dispatchMessage方法中:

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

msg.callback是Runable類型,當(dāng)我們調(diào)用Handler.post(runable)的時候,最終是將Runable設(shè)置給Message的callback變量。
dispatchMessage判斷msg.callback是否為空,不為空調(diào)用handleCallback方法:

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

就是調(diào)用了Runable的run方法。而callback為空時則判斷mCallback是否為空,mCallback是Callback類型

    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

是Handler提供的一種非侵入式的回調(diào),當(dāng)你不想重寫handler時可以設(shè)置Callback,在handleMessage處理消息并返回為true。
否則則調(diào)用自身handleMessage方法,這個方法子類重寫后便可以處理消息。

到這里handler調(diào)用原理就走通了,那么有幾個問題?

1.View.post Handler.post 和普通的發(fā)送有什么區(qū)別
2.主線程直接創(chuàng)建Handler為什么不報錯
3.loop阻塞了為什么不會影響主線程。
View.post
public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
}

View.post方法判斷有兩條,一如果AttachInfo不為空則直接使用它的handler.post處理Runable。
如果為空的話,則使用HandlerActionQueue.post。HandlerActionQueue是一個隊(duì)列,內(nèi)部維護(hù)這一個數(shù)組。
post方法只是將Runable封裝成HandlerAction放入到數(shù)組中

    public void post(Runnable action) {
        postDelayed(action, 0);
    }

    public void postDelayed(Runnable action, long delayMillis) {
        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

        synchronized (this) {
            if (mActions == null) {
                mActions = new HandlerAction[4];
            }
            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
            mCount++;
        }
    }

在View的dispatchAttachedToWindow方法中會調(diào)用HandlerActionQueue的executeActions方法,遍歷數(shù)組通過Handler.postDelayed處理信息

    public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

我們之前分析過Handler的dispatchMessage方法。其中針對Message的callback是否為空做了條件判定。callback是Runable類型。
Handler.post方法

    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
    }

Handler.post方法其實(shí)也是調(diào)用了sendMessageDelayed來發(fā)送消息,區(qū)別在于在獲取Message的時候?qū)unable設(shè)置給Message的callback屬性,在最終分發(fā)的方法dispatchMessage中依據(jù)callback是否為空來判定是否Runable的run方法還是Handler自己的回調(diào)方法。
到這邊我們可以解釋了View.post和Handler.post本質(zhì)上并沒有不同,都是依賴于Handler發(fā)送消息機(jī)制,區(qū)別在于最終消息的回調(diào)方法不同。

在子線程中直接創(chuàng)建handler會報錯,主線程直接創(chuàng)建Handler為什么不報錯

Looper在主線程入口函數(shù)中調(diào)用了prepareMainLooper方法,該方法是是創(chuàng)建主線程的Looper,因此我們在主線程中創(chuàng)建handler時,Looper已經(jīng)存在,所以不會報錯。

Android中為什么主線程不會因?yàn)長ooper.loop()里的死循環(huán)卡死?

Android底層采用的是pipe/epoll機(jī)制,當(dāng)主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next方法的nativePullOnce里,此時主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到下一條消息發(fā)送或者有事務(wù)到達(dá)時,通過向pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。這邊采用的epoll機(jī)制是一種IO多路復(fù)用的機(jī)制,可以同時監(jiān)聽多個描述符,當(dāng)某個描述符就緒(讀/寫就緒)就立即通知相應(yīng)程序進(jìn)行讀或者寫操作,本質(zhì)同步I/O,即讀寫是阻塞的。因此主線程大多數(shù)情況下處于休眠狀態(tài),所以不會大量消耗CPU資源導(dǎo)致卡死

epoll提供了三種方法
epoll_create(): 創(chuàng)建一個epoll實(shí)例并返回相應(yīng)的文件描述符(epoll_create1() 擴(kuò)展了epoll_create() 的功能)。
epoll_ctl(): 注冊相關(guān)的文件描述符使用
epoll_wait(): 可以用于等待IO事件。如果當(dāng)前沒有可用的事件,這個函數(shù)會阻塞調(diào)用線程。
詳情參考https://my.oschina.net/u/3863980/blog/1933086

Activity是如何在死循環(huán)中執(zhí)行生命周期方法的?

通過Handler機(jī)制執(zhí)行生命周期方法的。ActivityThread中有內(nèi)部類H繼承Handler。
Activity生命周期依靠looper.loop,當(dāng)Handler接受到消息時,在其內(nèi)部handleMessage方法中處理對應(yīng)的生命周期方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。