Android Handler機(jī)制詳解原理
Looper是整個(gè)跨線程通信的管理者
// 內(nèi)部持有的變量如下: ThreadLocal<Looper> MainLooper Observer MessageQueue Thread
1.首先先回憶一下Handler怎么用
Android線程通信分為以下兩種情況
- 1.子線程發(fā)消息給UI線程
- 2.UI線程發(fā)消息給子線程
- 3.子線程發(fā)消息給另個(gè)子線程
1.子線程發(fā)消息給UI線程
class FragmentContentActivity : AppCompatActivity() { val FLAG = 1 lateinit var handler: Handler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) handler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { FLAG -> { findViewById<TextView>(R.id.text).text = msg.data["Text"].toString() } } } } thread { Thread.sleep(2000L) handler.sendMessage(Message.obtain().apply { what = FLAG data = Bundle().apply { this.putString("Text", "ThreadMessage") } }) } } }
2.UI線程/子線程發(fā)消息給子線程
class FragmentContentActivity : AppCompatActivity() { val THREAD_FLAG =2 lateinit var threadHandler: Handler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) thread { Looper.prepare() threadHandler = object :Handler(Looper.myLooper()!!){ override fun handleMessage(msg: Message) { when(msg.what){ THREAD_FLAG -> { Toast.makeText( this@FragmentContentActivity, "${msg.data["Text"]}", Toast.LENGTH_SHORT ).show() } } } } Looper.loop() } } override fun onResume() { super.onResume() findViewById<TextView>(R.id.text).postDelayed({ threadHandler.sendMessage(Message.obtain().apply { what = THREAD_FLAG data = Bundle().apply { putString("Text","UI Message") } }) },2000L) } }
**在子線程的使用中,我們發(fā)現(xiàn)必須要進(jìn)行Looper.prepare()和Looper.loop()前后這兩個(gè)操作,因此,帶著這個(gè)疑問(wèn)來(lái)看一下Looper的邏輯
**
// 在調(diào)用prepare()之后一定要調(diào)用loop(),最后結(jié)束消息循環(huán)的時(shí)候調(diào)用quit() 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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
prepare()就是將初始化一個(gè)Looper對(duì)象放入到ThreadLocal中,初始化Looper,同時(shí)mQueue
public static void loop(){ Binder.clearCallingIdentity() for (;;) { Message msg = queue.next(); // might block long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { // 其實(shí) loop()只做了這一個(gè)調(diào)用,其他的都是監(jiān)控當(dāng)前消息循環(huán)時(shí)間是否超時(shí),應(yīng)該和ANR有關(guān) 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); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } //消息實(shí)體回收 msg.recycleUnchecked();
可以看到Looper.loop其實(shí)只是在for循環(huán)中,獲取mQueue的下一個(gè)msg節(jié)點(diǎn),然后調(diào)用msg.target.dispatchMessage(msg)
。乍看只是msg對(duì)象內(nèi)部的操作。
因?yàn)閘oop()其實(shí)邏輯上算死循環(huán),這意味著,當(dāng)前線程的自發(fā)的順序執(zhí)行命令到此結(jié)束了,只能通過(guò)其他線程觸發(fā)handler機(jī)制,來(lái)被動(dòng)的在當(dāng)前線程執(zhí)行命令,當(dāng)前線程完全變成了一個(gè)響應(yīng)線程
Looper類(lèi)只是初始化并開(kāi)啟線程死循環(huán)的一個(gè)開(kāi)關(guān),具體工作在MessageQueue中進(jìn)行
MessageQueue 消息隊(duì)列
隊(duì)列內(nèi)消息的添加不是直接調(diào)用MessageQueue,而是由與Looper相關(guān)聯(lián)的Handler調(diào)用
MessageQueue的內(nèi)部持有的變量如下: ArrayList mMessages SparseArray IdleHandler[] mBlocked
MessageQueue類(lèi)的功能主要有:元素插入隊(duì)列,獲取隊(duì)列的頭部元素,查找隊(duì)列中元素,綜述就是對(duì)隊(duì)列的增刪改查,其中 mMessage就是這個(gè)隊(duì)列的入口也是這個(gè)隊(duì)列的頭結(jié)點(diǎn)
boolean enqueueMessage(Message msg,long when) //msg 元素插入隊(duì)列 boolean hasMessages(Handler h,int what,Object object) //查找handler下的msg.what/object相同的Msg boolean hasEqualMessages(Handler h,int what,Object obj)//查找 msg.object.equal(obj)的msg removeMessages(Handler h,int what,Object obj)/(Handler h,Runnable r,Object obj) removeEqualMessages(...) //刪除與參數(shù)msg.object相同或equal的msg Message next() //獲取隊(duì)列中的頭部元素
可以看出,這些方法內(nèi)部都調(diào)用了 synchronized(this),隊(duì)列的操作都是線程同步的
Message next() -> ... // linux機(jī)制下的總線進(jìn)入輪詢(xún),線程相當(dāng)于掛起狀態(tài),nextPollTimeOut是掛起多長(zhǎng)時(shí)間 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; //先判斷msg.target是否為null,表示當(dāng)前消息是不是異步消息 if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. //同步屏障:取出當(dāng)前隊(duì)列中的異步消息 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. //重新計(jì)算線程進(jìn)入掛起狀態(tài)的時(shí)間 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; } ...
可以看出next()內(nèi)部主要有兩種獲取msg的邏輯
1.當(dāng)前消息都是普通消息,按照msg.when的大小排序,每一次循環(huán)執(zhí)行,通過(guò)檢測(cè)when是否大于now來(lái)決定是否獲取msg,或是掛起當(dāng)前線程。
2.當(dāng)前消息中有異步消息,優(yōu)先獲取msg.isAsynchronous()==true
的,或者按照此異步消息的等待時(shí)間,來(lái)重新設(shè)置掛起線程的時(shí)間,從而達(dá)到精準(zhǔn)的獲取異步消息。
通俗的來(lái)講就是說(shuō),當(dāng)前所有普通消息按照預(yù)約的執(zhí)行時(shí)間的先后來(lái)排隊(duì),這樣可基本上既可以達(dá)到按照預(yù)約時(shí)間執(zhí)行消息,也可以最大效率的在一定時(shí)間段內(nèi)執(zhí)行最多的消息,但是這忽略了每個(gè)消息的執(zhí)行消耗的時(shí)間,比如A消息是隊(duì)列內(nèi)的No.1,A消息預(yù)約執(zhí)行時(shí)間是1s之后,整個(gè)隊(duì)列是等待狀態(tài)的,這個(gè)時(shí)候來(lái)了B消息,B消息預(yù)約的時(shí)間是0.999s之后,按照預(yù)約時(shí)間的排隊(duì)機(jī)制,B消息要插隊(duì)到A消息之前,B成了這個(gè)隊(duì)列的No.1,A成了No.2,整個(gè)隊(duì)列的等待時(shí)間還是1s(因?yàn)橹霸O(shè)置了等待時(shí)間,所以不用喚醒),但是B消息的執(zhí)行過(guò)程長(zhǎng)達(dá)0.5s,已經(jīng)超過(guò)了之后的很多消息的預(yù)約執(zhí)行時(shí)間點(diǎn)了,這樣就不能保證某些重要的消息按時(shí)執(zhí)行。
于是就有了異步消息同步屏障的機(jī)制,這相當(dāng)于普通消息排隊(duì)時(shí)來(lái)了一個(gè)VIP消息,先按照預(yù)約時(shí)間找到自己的位置,然后大喝一聲:“都把腳給我挪開(kāi),我的前面不允許有人”,這個(gè)時(shí)候排在他之前的普通消息就只能全部挪到隊(duì)列的一邊,然后隊(duì)列重新按照這位VIP消息,設(shè)置等待時(shí)間,期間新來(lái)的普通消息也插到隊(duì)邊等待,保證精準(zhǔn)按時(shí)執(zhí)行VIP消息。等VIP消息執(zhí)行完,之后再把之前等待普通消息的隊(duì)列合并執(zhí)行。當(dāng)然之前等待的消息全耽誤了,但畢竟是普通消息不重要。
// 同步屏障的方法,此方法只在 ViewRootImpl類(lèi)中調(diào)用 private int postSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { final int token = mNextBarrierToken++; final Message msg = Message.obtain(); msg.markInUse(); msg.when = when; msg.arg1 = token; //沒(méi)有設(shè)置target Message prev = null; Message p = mMessages; if (when != 0) { while (p != null && p.when <= when) { prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; //mMessages變?yōu)橥狡琳舷?,next()下一次循環(huán),首先獲取到的是同步屏障 mMessages = msg; } return token; } // ViewRootImpl void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } //設(shè)置同步屏障之后,通過(guò)設(shè)置了Aysnc標(biāo)記位的Handler發(fā)送的Msg都是異步消息, //MessageQueue也優(yōu)先處理此類(lèi)異步消息,直到移除同步屏障標(biāo)記位,再恢復(fù)到普通消息隊(duì)列。
由此可見(jiàn),同步屏障的設(shè)置和View刷新機(jī)制有關(guān),因?yàn)橐WCVsync信號(hào)按時(shí)完成刷新操作,具體分析待續(xù)…
綜述,異步消息可以保證精準(zhǔn)的執(zhí)行,但也因此消息事件的先后順序被打亂,有可能在代碼執(zhí)行中執(zhí)行了Handler.sendMsg(1,time0.2)->AsyncHandler.sendMsg(2,time0.5)
,但是實(shí)際執(zhí)行的是 2->1。
再看Handler
Handler的成員變量如下
mLooper :初始化時(shí)獲取當(dāng)前線程的Looper對(duì)象引用 mQueue :通過(guò)Looper.mQueue
獲取到的MessageQueue隊(duì)列引用mAsynchronous :標(biāo)記當(dāng)前Handler是否發(fā)送異步消息 mCallback : Handler
自身的callback接口,此callback調(diào)用在Message.callback
之前mMessenger :IMessager
和進(jìn)程通信相關(guān)
以上成員變量大都是final類(lèi)型,表示Handler也是在其使用上也是final類(lèi)型,也就是說(shuō),沒(méi)有辦法通過(guò)將Handler與context的生命周期相剝離來(lái)避免內(nèi)存泄漏
Handler的方法如下
//Handler 發(fā)送Message第一種方法,設(shè)置Message的what,data //不設(shè)置 runnable:Callback boolean sendMessage(Message msg) -> boolean sendMessageDelayed(Message msg,long delayTime) -> boolean sendMessageAtTime(Message msg,SystemClock.uptimeMillis()+delayTime) -> mQueue.enqueueMessage(msg,uptime) //第二種方法,Message只設(shè)置runnable:Callback boolean postAtTime(Runnable r,Object token,long uptime) -> sendMessageAtTime(getPostMessage(r,token),uptime) --> Message getPostMessage(Runnable r,Object token){ Message.obtain().callback=r ... } //移除Message和檢驗(yàn)Message removeMessages() hasMessages() ... //Message 回調(diào)執(zhí)行 void dispatchMessage(Message msg){ if(msg.callback!=null){ handleCallback(msg) -> }else{ if(mCallback!=null){ mCallback.handleMessage(msg) } handleMessage(msg) } //可以看到 Message的回調(diào)分為三個(gè)等級(jí) //No.1 msg自身的callback //No.2 Handler自身的mCallback成員變量,mCallback是final類(lèi)型 //No.3 Handler的子類(lèi)重載的handleMessage方法
Message
Message 實(shí)現(xiàn)了Parcelable接口,也就是說(shuō)可以作為進(jìn)程間通信的載體
Message成員變量如下
int what //Handler發(fā)送主體下的Message的消息碼 int arg1 //低成本的參數(shù)傳遞 int arg2 Object obj //可以為空的token對(duì)象,一般在進(jìn)程通信中用到 Bundle data //線程通信中常用的參數(shù)容器 Handler target //發(fā)送主體 Runnable callback //Message自身回調(diào) Messenger replyTo //進(jìn)程通信,一般在AMS中用到 ------ // Message緩存池相關(guān) Object sPoolSync = new Object() // 同步標(biāo)記 Messsage next static Message sPool static int sPoolSize
Message方法如下
//可以看出這是一個(gè)非常巧妙的方法 static Message obtain(){ synchronized(sPoolsSync){ if(sPools!=null){ Message m= sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message(); } //主體上是一個(gè)帶緩存池鏈表的同步工廠模式,同時(shí)也考慮到較多線程阻塞時(shí) //可以直接聲明初始化對(duì)象 //回收Message對(duì)象到緩存池鏈表 void recycleUnchecked(){ ...參數(shù)=null synchronized(sPoolSync){ if(sPoolSize < MAX_SIZE){ next = sPool; sPools = this; sPoolSize++; } } }
到此這篇關(guān)于Android Handler機(jī)制詳解原理的文章就介紹到這了,更多相關(guān)Android Handler內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android?TextView的maxEms和maxLength屬性區(qū)別
這篇文章主要為大家介紹了Android?TextView的maxEms和maxLength屬性區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03詳解Android Material Design自定義動(dòng)畫(huà)的編寫(xiě)
這篇文章主要介紹了詳解Android Material Design自定義動(dòng)畫(huà)的編寫(xiě),其中對(duì)Activity的過(guò)渡動(dòng)畫(huà)進(jìn)行了重點(diǎn)講解,需要的朋友可以參考下2016-04-04Android仿今日頭條滑動(dòng)頁(yè)面導(dǎo)航效果
這篇文章主要為大家詳細(xì)介紹了Android仿今日頭條滑動(dòng)頁(yè)面導(dǎo)航效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01Android 開(kāi)發(fā)音頻組件(Vitamio FAQ)詳細(xì)介紹
本文主要介紹Android開(kāi)發(fā)音頻播放器,Vitamio是Android播放器組件,支持幾乎所有視頻格式和網(wǎng)絡(luò)視頻流,希望能幫助開(kāi)發(fā)Android 音頻播放的小伙伴2016-07-07Android使用MediaCodec將攝像頭采集的視頻編碼為h264
這篇文章主要為大家詳細(xì)介紹了Android使用MediaCodec將攝像頭采集的視頻編碼為h264,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10Android位圖(圖片)加載引入的內(nèi)存溢出問(wèn)題詳細(xì)解析
Android在加載大背景圖或者大量圖片時(shí),常常致使內(nèi)存溢出,下面這篇文章主要給大家介紹了關(guān)于Android位圖(圖片)加載引入的內(nèi)存溢出問(wèn)題的相關(guān)資料,需要的朋友可以參考下2022-12-12協(xié)程作用域概念迭代RxTask?實(shí)現(xiàn)自主控制
這篇文章主要為大家介紹了協(xié)程作用域概念迭代RxTask實(shí)現(xiàn)自主控制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Android圖片選擇器ImageEditContainer
這篇文章主要為大家詳細(xì)介紹了Android圖片選擇器ImageEditContainer的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07實(shí)例講解Android多線程應(yīng)用開(kāi)發(fā)中Handler的使用
這篇文章主要介紹了Android多線程應(yīng)用開(kāi)發(fā)中Handler的使用,Handle主要被用來(lái)更新UI和處理消息,需要的朋友可以參考下2016-01-01