Android Handler runWithScissors 梳理流程解析
前言
看 WMS 代碼的時候看到了 Handler.runWithScissors 方法,所以來惡補一下
public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
return sInstance;
}
通過 DisplayThread.getHandler() 調(diào)用了 runWithScissors 方法。
該方法的設(shè)計初衷就是:在一個線程中通過 Handler 向另外一個線程發(fā)送消息,并等待另一個線程處理完成后再繼續(xù)執(zhí)行。
runWithScissors
首先來看一下官方文檔的描述:
同步運行指定的任務(wù)。如何當(dāng)前線程和處理線程相同,則立即執(zhí)行不用排隊,否則就發(fā)送到別的線程進行處理,并等待他完成后再返回。另外,這種方法很危險,使用不當(dāng)可能會造成死鎖,畢竟是兩個線程間的通信。
還有該方法被標記為 @hide,因為有一些隱患,所以該方法不希望被開發(fā)者使用,一般都用于 Framwork 層。
下面我們來分析一下代碼:
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
首先獲取當(dāng)前線程的 looper,在拿到 Handler 所屬的looper,如果是同一個,就直接執(zhí)行并返回 true,否則就繼續(xù)往下走。
如果所屬的 looper 不相同,則使用 BlockingRunnable 進行包裝,并調(diào)用 postAndWait 方法:
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();//運行在 handler 線程
} finally {
synchronized (this) {
mDone = true; //標記完成
notifyAll(); //喚醒線程
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
//使用 post 進行發(fā)送
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
在 postAndWait 方法中,首先調(diào)用 post 添加到 queue 隊列中,如果成功返回 true,如果發(fā)送失敗,postAndWait 方法直接退出。
發(fā)送成功后,就會添加的隊里中,等到合適的時候 run 方法就會執(zhí)行,然后就會執(zhí)行 finally 塊,將 mDone 置為 true。
post() 方法執(zhí)行成功后,就會進入 synchronized 代碼塊,需要注意的是 run 方法中也有一個 synchronized,這兩個鎖對象都是 this,所以說,同一時刻只能有一個代碼塊被執(zhí)行,另一個只能進行等待。
接著就是 timeout 大于 0 并且 mDone 標志一直處于 false,則進行 wait 等待,等待結(jié)束后如果任務(wù)還沒有完成,直接 return false,表示任務(wù)失敗。
如果 timeout 小于0,則不需要延時,直接進行阻塞,沒有超時時間,只能等待被喚醒。
最后 return true 表示任務(wù)成功。
梳理流程
1,首先判斷目標線程和當(dāng)前線程是否相同,相同則立即執(zhí)行任務(wù),return true。
2,接著就使用 BlockingRunnable 進行包裝,然后使用 post 發(fā)送。發(fā)送失敗表示目標線程的 Looper 有問題,直接 return false, 表示任務(wù)失敗。
3,發(fā)送成功以后,會有兩個分支,一個是 run 方法中的 synchronized,還有一個是 postAndWait 中的synchronized 。這兩個在同一時刻只能有一個執(zhí)行。run 方法中執(zhí)行任務(wù),postAndWait 中進行延時或者直接等待。
4,最后就是延時等待結(jié)束后任務(wù)沒完成則表示任務(wù)失敗,如果沒有延時就直接進行 wait 進行阻塞,直到被喚醒。這里沒有超時邏輯,會存在一定的問題。
存在的問題
通過上面的分析,我們大底可以分析出問題的關(guān)鍵了,具體如下所示:
- 沒有超時取消邏輯
- 延時完成后,任務(wù)如果沒有完成,直接回 return false,但是 Runable 依然在運行在目標線程的 MessageQueue 中,最終依然會得到執(zhí)行,但是不會符合我們的預(yù)期
死鎖
1,如果 Runable 在沒有執(zhí)行的時候被移除了,例如 Handler.removeCallBack,Looper.quit,這個任務(wù)就永遠得不到執(zhí)行,就會導(dǎo)致 wait 一直等待。
2,如果 wait 一直無法被喚醒, 并且這個時候還持有者別的鎖,就會導(dǎo)致死鎖。
那么要如何解決呢,上面第一種也無需解決,如果它不符合你的業(yè)務(wù),你也就不需要使用它了,第二種只需要保證當(dāng)前線程沒有別的鎖,而且 looper 不能直接退出,需要退出的時候也需要安全退出(quitSafely方法)。
總結(jié)
通過分析我們也可以看出來 runWithScissors 方法基本上不是偏向于業(yè)務(wù)的,而是偏向于 framwork 層的,因此該方法被標注為了 hide 方法。如果我們業(yè)務(wù)真的需要使用這個方法,我們也完全可以仿照源碼自己寫一個出來,并且還可以隨意修改,豈不美滋滋。
以上就是Android Handler runWithScissors 梳理流程解析的詳細內(nèi)容,更多關(guān)于Android Handler runWithScissors 的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
???????Android?H5通用容器架構(gòu)設(shè)計詳解
這篇文章主要介紹了???????Android?H5通用容器架構(gòu)設(shè)計詳解,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
Android自定義ViewGroup的實現(xiàn)方法
這篇文章主要為大家詳細介紹了Android自定義ViewGroup的實現(xiàn)方法,感興趣的小伙伴們可以參考一下2016-05-05
Android實現(xiàn)自定義驗證碼輸入框效果(實例代碼)
這篇文章主要介紹了Android實現(xiàn)自定義驗證碼輸入框效果,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01
Jetpack?Compose實現(xiàn)對角線滾動效果
這篇文章主要為大家詳細介紹了如何利用Jetpack?Compose實現(xiàn)一個簡單的對角線滾動效果,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-02-02
Android短信接收監(jiān)聽、自動回復(fù)短信操作例子
本文實現(xiàn)了短信接收監(jiān)聽,當(dāng)接收到短信時,可自動回復(fù)短信,或自動回撥電話,同時監(jiān)聽短信的發(fā)送狀態(tài)2014-04-04
Android ProgressBar 模擬進度條效果的實現(xiàn)
這篇文章主要介紹了Android ProgressBar 模擬進度條效果的實現(xiàn),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04

