日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

從源碼角度分析Android的消息機制

瀏覽:18日期:2022-09-20 10:00:52
前言

說到Android的消息機制,那么主要的就是指的Handler的運行機制。其中包括MessageQueue以及Looper的工作過程。

在開始正文之前,先拋出兩個問題:

為什么更新UI的操作要在主線程中進行? Android中為什么主線程不會因為Looper.loop()里的死循環(huán)卡死?

UI線程的判斷是在ViewRootImpl中的checkThread方法中完成的。

對于第一個問題,這里給一個簡單的回答:

如果可以在子線程中修改UI,多線程的并發(fā)訪問可能會導致UI控件的不可預期性,采用加鎖的方式,就會降低UI的訪問效率以及會阻塞其他線程的執(zhí)行,所以最簡單有效的方法就是采用單線程模型來處理UI操作。

Handler的運行離不來底層的MessageQueue和Looper的支撐。MessageQueue翻譯過來是一個消息隊列,里面存儲了Handler需要的Message,MessageQueue并不是一個隊列,其實上是用單鏈表的數(shù)據(jù)結(jié)構來存儲Message。

那么Handler如何拿到Message呢?這時候就需要Looper了,Looper通過Looper.loop()來開啟一個死循環(huán),不斷從MessageQueue中取消息然后傳遞給Handler。

這里還有另一個知識點就是Looper的獲取,這里就要提高一個存儲類:ThreadLocal

ThreadLocal的工作原理

ThreadLocal是線程內(nèi)部的一個數(shù)據(jù)存儲類,可以存儲某個線程中的數(shù)據(jù),對于其他線程無法獲取該線程的數(shù)據(jù)。我們通過原理來看一下,這個觀點是否正確。

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings('unchecked') T result = (T)e.value; return result; } } return setInitialValue(); }

可以看出它的set和get方法就是在當前線程中所做的操作,ThreadLocalMap內(nèi)部是一個數(shù)組table。 這樣就保證了在不同線程中的數(shù)據(jù)互不干擾。

ThreadLocal除了使用在Handler中獲取Looper,還用于一些復雜的場景,比如:監(jiān)聽器的傳遞。

我們簡單了解了ThreadLocal,那么我們從New Handler()來一步步梳理下消息機制。

Looper的工作原理

// Handler.java public Handler() { this(null, false); } // callback 消息回調(diào);async 是否同步 public Handler(Callback callback, boolean async) { ... // 1. 首先獲取looper mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( 'Can’t create handler inside thread ' + Thread.currentThread() + ' that has not called Looper.prepare()'); } // 2. 獲取MessggeQueue mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }

我們平常用的是無參數(shù)的方法,它傳入的是空的回調(diào)以及false。

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

這里就出現(xiàn)了我們之前說的ThreadLoacal類,那么looper值是什么時候設置進去的呢?

它的設置方法其實是在prepare方法以及prepareMainLooper方法中,我們來分別來看下:

public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { // 在創(chuàng)建looper之前,判斷l(xiāng)ooper是否與threadloacal綁定過,這也是prepare只能設置一遍的原因。 if (sThreadLocal.get() != null) { throw new RuntimeException('Only one Looper may be created per thread'); } sThreadLocal.set(new Looper(quitAllowed)); } public static void prepareMainLooper() { // 這里其實還是調(diào)用的prepare方法 prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException('The main Looper has already been prepared.'); } sMainLooper = myLooper(); } }

通過上面可以prepare方法只能設置一遍,那么我們在主線程中為什么能直接使用呢? app程序的入口是在ActivityThread中的main方法中:

public static void main(String[] args) { ... //1. 初始化Looper對象 Looper.prepareMainLooper(); // 2. 開啟無限循環(huán) Looper.loop(); throw new RuntimeException('Main thread loop unexpectedly exited'); }

看到了吧,初始化在這里,那么我們再來看下looper的初始化方法:

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

Looper的初始化做了兩件事:創(chuàng)建消息隊列MessageQueue以及獲取當前的線程。 到這里,我們可以得到一個結(jié)論:

prepare方法在一個線程中只能調(diào)用一次。 Looper的初始化在一個線程中只能調(diào)用一次。 最后可以得知:一個線程對應一個Looper,一個Looper對應一個MessageQueue。

Looper可以理解為一個工廠線,不斷從MessageQueue中取Message,工廠線開啟的方式就是Looper.loop()

public static void loop() { final Looper me = myLooper(); // 1. 判斷l(xiāng)ooper是否存在 if (me == null) { throw new RuntimeException('No Looper; Looper.prepare() wasn’t called on this thread.'); } final MessageQueue queue = me.mQueue; ... //2. 開啟一個死循環(huán) 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); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... } }

looper方法通過開啟一個死循環(huán),不斷從MessageQueue中取Message消息,當message為空時,退出該循環(huán),否則調(diào)用msg.target.dispatchMessage(msg)方法,target就是msg綁定的Handler對象。

Handler的工作原理

好了到這里又回到了Handler類中。

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

這個handleMessage就是我們需要實現(xiàn)的方法。 那么Handler是如何設置到Message中的呢?我們來看下我們熟知的sendMessage方法:

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

可以看到,通過一系列的方法,在enqueueMessage中將handler賦值到msg的target中。最后調(diào)用的是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 { 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; }

enqueueMessage方法主要做了兩件事:

首先判斷handler是否存在以及是否在使用中。然后根據(jù)時間順序插入MessageQueue中。

到這里基本的流程已經(jīng)梳理完了,回到起初我們的問題:Looper.loop()是一個死循環(huán),為什么不會堵塞主線程呢?

我們來看下MessageQueue的next方法:

Message next() { final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); ... } }

nativePollOnce方法是一個 native 方法,當調(diào)用此 native 方法時,主線程會釋放 CPU 資源進入休眠狀態(tài),直到下條消息到達或者有事務發(fā)生,通過往 pipe 管道寫端寫入數(shù)據(jù)來喚醒主線程工作,這里采用的 epoll 機制。關于 nativePollOnce 的詳細分析可以參考:nativePollOnce函數(shù)分析

總結(jié) app程序啟動從ActivityThread中的main方法中開始,通過Looper.prepare()進行Looper以及MessageQueue的創(chuàng)建以及ThreadLocal與線程之間的綁定。 我們在創(chuàng)建Handler時,通過ThreadLocal來獲取該線程中的Looper以及在Looper上綁定的MessageQueue。 通過Handler.sendMessage()方法來將msg與Handler之間進行綁定,然后將msg通過時間順序插入MessageQueue中。 主線程創(chuàng)建后,Looper.loop()來啟動一個(不占用資源)死循環(huán),從Looper已經(jīng)存在的MessageQueue中不斷取出Message,然后調(diào)用不為空的Message綁定的Handler的dispatchMessage(msg)方法,最后會調(diào)用我們復寫的handlerMessage方法中。參考資料

Androi開發(fā)藝術探索

以上就是從源碼角度分析Android的消息機制的詳細內(nèi)容,更多關于Android 消息機制的資料請關注好吧啦網(wǎng)其它相關文章!

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
精品一区二区三区免费看| 日本 国产 欧美色综合| 青草综合视频| 日韩av一区二区三区| 最新亚洲国产| 欧美日一区二区三区在线观看国产免| 鲁大师成人一区二区三区 | 国产精品xxx在线观看| 国产欧美日韩综合一区在线播放| 久久精品99国产精品| 欧美精品91| 成人三级高清视频在线看| 成人片免费看| 桃色一区二区| 国产二区精品| 蜜桃视频一区二区三区在线观看| 日韩极品在线观看| 欧美精品aa| 性欧美videohd高精| 亚洲精品成人| 日本不卡的三区四区五区| 国产欧美一区二区三区米奇| 欧美激情网址| 日本久久二区| 美日韩一区二区三区| 91欧美极品| 国产精品视频一区二区三区综合 | 99在线观看免费视频精品观看| 精品久久在线| 综合激情在线| 欧美日一区二区在线观看| 国产精品a级| 日韩大片免费观看| 亚洲激情国产| 日韩一区二区三区精品| 欧美激情一区| 欧美日韩中文字幕一区二区三区 | 精品高清久久| 欧美日韩激情在线一区二区三区| 蜜桃一区二区三区在线| 日韩国产在线一| 九九99久久精品在免费线bt| 天堂日韩电影| 亚洲日韩中文字幕一区| 精品国产日韩欧美精品国产欧美日韩一区二区三区 | 蜜桃视频在线观看一区| 国产精品一站二站| 99精品视频精品精品视频| 午夜在线观看免费一区| 日本午夜精品久久久久| 日韩欧美三级| 日本一区免费网站| 色一区二区三区| 亚洲aa在线| 欧美13videosex性极品| 亚洲精品在线a| 欧美天堂视频| 日韩av资源网| 999国产精品永久免费视频app| 欧美亚洲三级| 久久一区二区三区电影| 欧美日本一区| 国产精品88久久久久久| 久久国内精品自在自线400部| 日韩欧美精品| 欧美永久精品| 欧美.日韩.国产.一区.二区 | 国产精品tv| 欧美.日韩.国产.一区.二区 | 久久精品高清| 国产精品欧美在线观看| 91久久中文| 国产精品蜜芽在线观看| 欧美日韩18| 狠狠爱www人成狠狠爱综合网| 国产精品1区在线| 亚洲欧美网站| 91精品亚洲| 国产一区二区三区四区二区| 一区二区精品| 久久狠狠婷婷| 国产精品自在| 在线免费观看亚洲| 日韩欧美午夜| 美女精品久久| 日韩一区二区三免费高清在线观看| 久久中文字幕av| 红杏一区二区三区| 日韩 欧美一区二区三区| 一区二区三区视频免费观看| 美女免费视频一区| 午夜亚洲福利| 国产视频一区欧美| 啪啪国产精品| 国产精品不卡| 国产精品chinese| 欧美精品国产一区| 午夜一级久久| 日韩视频二区| 亚洲天堂久久| 精品国产91| 久久99精品久久久野外观看| 国产亚洲电影| 日韩精品一区二区三区中文字幕| 黄色国产精品| 精品在线91| 亚洲国产综合在线看不卡| 欧美好骚综合网| 成人亚洲精品| 另类欧美日韩国产在线| 国产精品日本一区二区三区在线 | 在线视频观看日韩| 播放一区二区| 国产麻豆久久| 91精品国产自产在线观看永久∴ | 日韩欧乱色一区二区三区在线| 午夜宅男久久久| 最新日韩av| 日韩亚洲在线| 亚洲天堂久久| 狠狠久久婷婷| 免费日韩av片| 久久国产高清| 国产午夜久久| 久久aⅴ国产紧身牛仔裤| 视频一区在线视频| 久久亚洲国产精品一区二区| 午夜在线播放视频欧美| 视频一区欧美精品| 亚洲久草在线| 日韩精品视频在线看| 日韩久久一区| 国产精品白浆| 91视频一区| se01亚洲视频 | www.com.cn成人| 快播电影网址老女人久久| 精品日韩视频| 91精品91| 噜噜噜躁狠狠躁狠狠精品视频 | 婷婷成人av| 亚洲精品四区| 91精品国产自产在线丝袜啪| 国产精品三p一区二区| 久久不卡国产精品一区二区| 精品三级av| 亚洲成人国产| 免费看黄色91| 欧美日韩亚洲一区在线观看| 美女免费视频一区| 久久精品免费一区二区三区| 在线一区欧美| 97久久中文字幕| 精品三级久久久| caoporn视频在线| 久久久久一区| 午夜一区在线| 国产亚洲一区二区三区啪| 美腿丝袜亚洲三区| 久久精品国产99久久| 久久国产精品亚洲77777| 欧美日本久久| 美女视频黄 久久| 日韩欧美国产精品综合嫩v| 91久久久精品国产| 亚洲精品影视| 国产精品jk白丝蜜臀av小说| 午夜精品久久久久久久久久蜜桃| 亚洲精品一区二区妖精| 五月天综合网站| 91亚洲精品视频在线观看 | 日韩欧美视频专区| 欧美69视频| 青草久久视频| 超碰99在线| 欧美专区在线| 免费在线成人| 国产精品视频3p| 999久久久精品国产| 日韩精品视频网站| 樱桃视频成人在线观看| 亚洲资源av| 国产亚洲高清一区| 久久九九精品| 国产欧美一区二区三区精品观看| 一本大道色婷婷在线| 亚洲免费观看高清完整版在线观| caoporn视频在线| 亚洲美女91| 日韩免费在线| 91精品福利观看| 亚洲一级黄色| 国产精品3区| 亚洲一区二区免费看| 国产精品v日韩精品v欧美精品网站| 国产主播一区| 国产精品2区| 亚洲福利免费| 国产精品sm| 蜜臀av一区二区在线免费观看|