網(wǎng)站首頁 編程語言 正文
前言
看 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 方法。
該方法的設計初衷就是:在一個線程中通過 Handler 向另外一個線程發(fā)送消息,并等待另一個線程處理完成后再繼續(xù)執(zhí)行。
runWithScissors
首先來看一下官方文檔的描述:
同步運行指定的任務。如何當前線程和處理線程相同,則立即執(zhí)行不用排隊,否則就發(fā)送到別的線程進行處理,并等待他完成后再返回。另外,這種方法很危險,使用不當可能會造成死鎖,畢竟是兩個線程間的通信。
還有該方法被標記為 @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); }
首先獲取當前線程的 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 等待,等待結束后如果任務還沒有完成,直接 return false,表示任務失敗。
如果 timeout 小于0,則不需要延時,直接進行阻塞,沒有超時時間,只能等待被喚醒。
最后 return true 表示任務成功。
梳理流程
1,首先判斷目標線程和當前線程是否相同,相同則立即執(zhí)行任務,return true。
2,接著就使用 BlockingRunnable 進行包裝,然后使用 post 發(fā)送。發(fā)送失敗表示目標線程的 Looper 有問題,直接 return false, 表示任務失敗。
3,發(fā)送成功以后,會有兩個分支,一個是 run 方法中的 synchronized,還有一個是 postAndWait 中的synchronized 。這兩個在同一時刻只能有一個執(zhí)行。run 方法中執(zhí)行任務,postAndWait 中進行延時或者直接等待。
4,最后就是延時等待結束后任務沒完成則表示任務失敗,如果沒有延時就直接進行 wait 進行阻塞,直到被喚醒。這里沒有超時邏輯,會存在一定的問題。
存在的問題
通過上面的分析,我們大底可以分析出問題的關鍵了,具體如下所示:
- 沒有超時取消邏輯
- 延時完成后,任務如果沒有完成,直接回 return false,但是 Runable 依然在運行在目標線程的 MessageQueue 中,最終依然會得到執(zhí)行,但是不會符合我們的預期
死鎖
1,如果 Runable 在沒有執(zhí)行的時候被移除了,例如 Handler.removeCallBack,Looper.quit,這個任務就永遠得不到執(zhí)行,就會導致 wait 一直等待。
2,如果 wait 一直無法被喚醒, 并且這個時候還持有者別的鎖,就會導致死鎖。
那么要如何解決呢,上面第一種也無需解決,如果它不符合你的業(yè)務,你也就不需要使用它了,第二種只需要保證當前線程沒有別的鎖,而且 looper 不能直接退出,需要退出的時候也需要安全退出(quitSafely方法)。
總結
通過分析我們也可以看出來 runWithScissors 方法基本上不是偏向于業(yè)務的,而是偏向于 framwork 層的,因此該方法被標注為了 hide 方法。如果我們業(yè)務真的需要使用這個方法,我們也完全可以仿照源碼自己寫一個出來,并且還可以隨意修改,豈不美滋滋。
原文鏈接:https://juejin.cn/post/7156821621557166087
相關推薦
- 2023-10-17 css標簽畫圓形進度條
- 2022-09-05 C語言如何實現(xiàn)頭插法建立單鏈表_C 語言
- 2022-11-02 使用ggsignif優(yōu)雅添加顯著性標記詳解_R語言
- 2022-06-04 tomcat的catalina.out日志按自定義時間格式進行分割的操作方法_Tomcat
- 2022-07-12 解決錯誤:Error: TomEE required to support EAR/EJB depl
- 2022-08-06 詳解Python如何優(yōu)雅地解析命令行_python
- 2022-05-15 Python語言實現(xiàn)二分法查找_python
- 2022-09-15 Python移動測試開發(fā)subprocess模塊項目實戰(zhàn)_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支