面試官:"Handler的runWithScissors()瞭解嗎?為什麼Google不讓開發者用?"
一、序
大家好,冷門知識又來了!
runWithScissors()
是 Handler 的一個方法,被標記為 @hide,不允許普通開發者呼叫。
這個方法算是比較冷門,如果面試中被問及,面試者不知道時,通常面試官會換個問法:"如何在子執行緒透過 Handler 向主執行緒傳送一個任務,並等主執行緒處理此任務後,再繼續執行?"。
這個場景,就可以藉助
runWithScissors()
來實現。雖然該方法被標記為 @hide,但是在 Framework 中,也有不少場景使用到它。不過它也有一些隱患,正是因為這些隱患,讓 Android 工程師將其標為 @hide,不允許普通開發者使用。
今天我們就來聊聊 Handler 的這個冷門的方法
runWithScissors()
,以及它可能出現的一些問題。
二、Handler.runWithScissors()
2.1 runWithScissors()
先撇開
runWithScissors()
方法,既然這裡存在 2 個執行緒的通訊,那肯定需要考慮多執行緒同步。
首先想到的就是 Synchronized 鎖和它的等待/通知機制,而透過 Handler 跨執行緒通訊時,想要傳送一個「任務」,Runnable 肯定比 Message 更適合。
接下來,我們看看
runWithScissors()
的實現是不是如我們預想一樣。
可以看到,
runWithScissors()
接受一個 Runnable,並且可以設定超時時間。
流程也非常簡單:
- 先簡單的對入參進行校驗;
- 如果當前執行緒和 Handler 的處理執行緒一致,則直接執行
run()
方法; - 執行緒不一致,則透過 BlockingRunnable 包裝一下,並執行其
postAndWait()
方法;
那再繼續看看 BlockingRunnable 的原始碼。
待執行的任務,會記入 BlockingRunnable 的 mTask,等待後續被呼叫執行。
postAndWait()
的邏輯也很簡單,先透過 handler 嘗試將 BlockingRunnable 發出去,之後進入 Synchronized 臨界區,嘗試
wait()
阻塞。
如果設定了 timeout,則使用
wait(timeout)
進入阻塞,若被超時喚醒,則直接返回 false,表示任務執行失敗。
那麼現在可以看到
postAndWait()
返回 false 有 2 個場景:
- Handler
post()
失敗,表示 Looper 出問題了; - 等待超時,任務還沒有執行結束;
除了超時喚醒外,我們還需要在任務執行完後,喚醒當前執行緒。
回看 BlockingRunnable 的
run()
方法,
run()
被 Handler 排程並在其執行緒執行。在其中呼叫
mTask.run()
,mTask 即我們需要執行的 Runnable 任務。執行結束後,標記 mDone 並透過
notifyAll()
喚醒等待。
任務發起執行緒,被喚醒後,會判斷 mDone,若為 true 則任務執行完成,直接返回 true 退出。
2.2 Framework 中的使用
runWithScissors()
被標記為 @hide,應用開發一般是用不上的,但是在 Framework 中,卻有不少使用場景。
例如比較熟悉的 WMS 啟動流程中,分別在
main()
和
initPolicy()
中,透過
runWithScissors()
切換到 "android.display" 和 "android.ui" 執行緒去做一些初始工作。
例如上面程式碼,就是從 "android.display" 執行緒,透過切換到 "android.ui" 執行緒去執行任務。
三、runWithScissors() 的問題
看似
runWithScissors()
透過 Synchronized 的等待通知機制,配合 Handler 傳送 Runnable 執行阻塞任務,看似沒有問題,但依然被 Android 工程師設為 @hide。
我們繼續看看它的問題。
3.1 如果超時了,沒有取消的邏輯
透過
runWithScissors()
傳送 Runnable 時,可以指定超時時間。當超時喚醒時,是直接 false 退出。
當超時退出時,這個 Runnable 依然還在目標執行緒的 MessageQueue 中,沒有被移除掉,它最終還是會被 Handler 執行緒排程並執行。
此時的執行,顯然並不符合我們的業務預期。
3.2 可能造成死鎖
而更嚴重的是,使用
runWithScissors()
可能造成呼叫執行緒進入阻塞,而得不到喚醒,如果當前持有別的鎖,還會造成死鎖。
我們透過 Handler 傳送的 MessageQueue 的訊息,一般都會得到執行,而當執行緒 Looper 透過
quit()
退出時,會清理掉還未執行的任務,此時傳送執行緒,則永遠得不到喚醒。
那麼在使用
runWithScissors()
時,就要求 Handler 所在的執行緒 Looper,不允許退出,或者使用
quitSafely()
方式退出。
quit()
和
quitSafely()
都表示退出,會去清理對應的 MessageQueue,區別在於,
qiut()
會清理 MessageQueue 中所有的訊息,而
quitSafely()
只會清理掉當前時間點之後(when > now)的訊息,當前時間之前的訊息,依然會得到執行。
那麼只要使用
quitSafely()
退出,透過
runWithScissors()
傳送的任務,依然會被執行。
也就是說,安全使用
runWithScissors()
要滿足 2 個條件:
- Handler 的 Looper 不允許退出,例如 Android 主執行緒 Looper 就不允許退出;
- Looper 退出時,使用安全退出
quitSafely()
方式退出;
四、總結時刻
今天我們介紹了一個冷門的方法
runWithScissors()
以及其原理,可以透過阻塞的方式,向目標執行緒傳送任務,並等待任務執行結束。
雖然被它標記為 @hide,無法直接使用,但這都是純軟體實現,我們其實可以自己實現一個 BlockingRunnable 去使用。當然原本存在的問題,在使用時也需要注意。
我知道就算這個方法不被標記為 @hide,使用的場景也非常的少,但是它依然可以幫助我們思考一些臨界問題,執行緒的同步、死鎖,以及 Handler 的退出方式對訊息的影響。
只要是程式設計師,不管是Java還是Android,如果不去閱讀原始碼,只看API文件,那就只是浮於表象,這對我們的知識體系的建立和完備以及實戰技術的提升都是不利的。
真正最能鍛鍊能力的便是直接去閱讀原始碼,不僅限於閱讀Android系統原始碼,還包括各種優秀的開源庫。
最後為了幫助大家深刻理解Handler相關知識點的原理以及面試相關知識,這裡還為大家整理了 Android開發相關原始碼精編解析:
深入解析 Handler 原始碼解析
- 傳送訊息
- 訊息入隊
- 訊息迴圈
- 訊息遍歷
- 訊息的處理
- 同步屏障機制
- 阻塞喚醒機制
還有Handler相關面試題解析幫助熟練掌握Handler知識:
最後為了幫助大家深刻理解 Android相關知識點的原理以及面試相關知識,這裡放上我搜集整理的 2019-2020BAT 面試真題解析,我把大廠面試中 常被問到的技術點整理成了PDF,包知識脈絡 + 諸多細節。
節省大家在網上搜尋資料的時間來學習,也可以分享給身邊好友一起學習。
《960全網最全Android開發筆記》
《379頁Android開發面試寶典》
歷時半年,我們整理了這份市面上最全面的安卓面試題解析大全
包含了騰訊、百度、小米、阿里、樂視、美團、58、360、新浪、搜狐等一線網際網路公司面試被問到的題目。熟悉本文中列出的知識點會大大增加透過前兩輪技術面試的機率。
如何使用它?
1.可以透過目錄索引直接翻看需要的知識點,查漏補缺。
2.五角星數表示面試問到的頻率,代表重要推薦指數
《507頁Android開發相關原始碼解析》
只要是程式設計師,不管是Java還是Android,如果不去閱讀原始碼,只看API文件,那就只是停留於皮毛,這對我們知識體系的建立和完備以及實戰技術的提升都是不利的。
真正最能鍛鍊能力的便是直接去閱讀原始碼,不僅限於閱讀各大系統原始碼,還包括各種優秀的開源庫。
資料太多,全部展示會影響篇幅,暫時就先列舉這些部分截圖,以上資源均免費分享,以上內容均放在了開源專案: 【 github 】 中已收錄,大家可以自行獲取。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2733645/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 面試官:你瞭解Webpack嗎?面試Web
- 面試官:說說你對NoSQL的瞭解,為什麼要有NoSQL面試SQL
- 面試官: 你瞭解前端路由嗎?面試前端路由
- 面試官瘋了嗎,問我為什麼浮點數不精確?面試
- 面試官:Redis的共享物件池瞭解嗎?面試Redis物件
- 面試官:Redis中的緩衝區瞭解嗎面試Redis
- 面試官問:你瞭解HTTP2.0嗎?面試HTTP
- 面試官:雙親委派模型你瞭解嗎?面試模型
- 面試官:你瞭解git cherry-pick嗎?面試Git
- 為什麼有些公司不讓用 Lombok ?Lombok
- 面試官:你瞭解es6的知識嗎?面試
- 面試官:你瞭解 vue 的diff演算法嗎?面試Vue演算法
- 面試官:你真的瞭解Redis分散式鎖嗎?面試Redis分散式
- 面試官:哥們Go語言的互斥鎖瞭解到什麼程度?面試Go
- 面試官: 你為什麼使用前端框架?面試前端框架
- 【49】瞭解new_handler的行為
- 面試官:react和vue有什麼區別嗎?面試ReactVue
- 為什麼不讓用join?《死磕MySQL系列 十六》MySql
- Redis 用的很溜,瞭解過它用的什麼協議嗎?Redis協議
- 面試官:為什麼 Promise 比setTimeout() 快?面試Promise
- 面試官:你為什麼要離開之前的公司?面試
- 面試官:Java 反射是什麼?我回答不上來!面試Java反射
- 面試官: 你瞭解過Babel嗎?寫過Babel外掛嗎? 答: 沒有。卒面試Babel
- 面試官:為什麼需要Java記憶體模型?面試Java記憶體模型
- 作為技術面試官,為什麼把你pass了面試
- 作為技術面試官,我在面試時考慮什麼?面試
- 你真正瞭解什麼是CloudNative嗎?Cloud
- 什麼鬼,面試官竟然讓我用Redis實現一個訊息佇列!!?面試Redis佇列
- 面試官:說說你對ThreadLocal的瞭解面試thread
- 面試:你知道為什麼會有 Generator 嗎面試
- 面試官:為什麼 Java 不把基本型別放在堆中?我竟然答不上來。。面試Java型別
- 面試官問:ThreadLocal中的鍵為什麼是弱引用?面試thread
- 分散式專題|面試官問我瞭解Mysql主從複製原理麼,我能說不會麼?分散式面試MySql
- 什麼是Linux?作為熱門的作業系統你瞭解嗎?Linux作業系統
- 面試官:Dubbo是什麼,他有什麼特性?面試
- 面試官為什麼喜歡拿 Kafka 考驗求職者面試Kafka求職
- 面試官問:Mybatis中的TypeHandler你用過嗎?面試MyBatis
- 面試官常說,培訓機構出身的程式設計師“程式碼不乾淨”,為什麼?面試程式設計師