網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
看 WMS 代碼的時(shí)候看到了 Handler.runWithScissors 方法,所以來(lái)惡補(bǔ)一下
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; }
通過(guò) DisplayThread.getHandler() 調(diào)用了 runWithScissors 方法。
該方法的設(shè)計(jì)初衷就是:在一個(gè)線程中通過(guò) Handler 向另外一個(gè)線程發(fā)送消息,并等待另一個(gè)線程處理完成后再繼續(xù)執(zhí)行。
runWithScissors
首先來(lái)看一下官方文檔的描述:
同步運(yùn)行指定的任務(wù)。如何當(dāng)前線程和處理線程相同,則立即執(zhí)行不用排隊(duì),否則就發(fā)送到別的線程進(jìn)行處理,并等待他完成后再返回。另外,這種方法很危險(xiǎn),使用不當(dāng)可能會(huì)造成死鎖,畢竟是兩個(gè)線程間的通信。
還有該方法被標(biāo)記為 @hide,因?yàn)橛幸恍╇[患,所以該方法不希望被開(kāi)發(fā)者使用,一般都用于 Framwork 層。
下面我們來(lái)分析一下代碼:
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,如果是同一個(gè),就直接執(zhí)行并返回 true,否則就繼續(xù)往下走。
如果所屬的 looper 不相同,則使用 BlockingRunnable
進(jìn)行包裝,并調(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();//運(yùn)行在 handler 線程 } finally { synchronized (this) { mDone = true; //標(biāo)記完成 notifyAll(); //喚醒線程 } } } public boolean postAndWait(Handler handler, long timeout) { //使用 post 進(jìn)行發(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 隊(duì)列中,如果成功返回 true,如果發(fā)送失敗,postAndWait 方法直接退出。
發(fā)送成功后,就會(huì)添加的隊(duì)里中,等到合適的時(shí)候 run 方法就會(huì)執(zhí)行,然后就會(huì)執(zhí)行 finally 塊,將 mDone 置為 true。
post() 方法執(zhí)行成功后,就會(huì)進(jìn)入 synchronized
代碼塊,需要注意的是 run 方法中也有一個(gè) synchronized,這兩個(gè)鎖對(duì)象都是 this,所以說(shuō),同一時(shí)刻只能有一個(gè)代碼塊被執(zhí)行,另一個(gè)只能進(jìn)行等待。
接著就是 timeout 大于 0 并且 mDone 標(biāo)志一直處于 false,則進(jìn)行 wait 等待,等待結(jié)束后如果任務(wù)還沒(méi)有完成,直接 return false,表示任務(wù)失敗。
如果 timeout 小于0,則不需要延時(shí),直接進(jìn)行阻塞,沒(méi)有超時(shí)時(shí)間,只能等待被喚醒。
最后 return true 表示任務(wù)成功。
梳理流程
1,首先判斷目標(biāo)線程和當(dāng)前線程是否相同,相同則立即執(zhí)行任務(wù),return true。
2,接著就使用 BlockingRunnable 進(jìn)行包裝,然后使用 post 發(fā)送。發(fā)送失敗表示目標(biāo)線程的 Looper 有問(wèn)題,直接 return false, 表示任務(wù)失敗。
3,發(fā)送成功以后,會(huì)有兩個(gè)分支,一個(gè)是 run 方法中的 synchronized,還有一個(gè)是 postAndWait 中的synchronized 。這兩個(gè)在同一時(shí)刻只能有一個(gè)執(zhí)行。run 方法中執(zhí)行任務(wù),postAndWait 中進(jìn)行延時(shí)或者直接等待。
4,最后就是延時(shí)等待結(jié)束后任務(wù)沒(méi)完成則表示任務(wù)失敗,如果沒(méi)有延時(shí)就直接進(jìn)行 wait 進(jìn)行阻塞,直到被喚醒。這里沒(méi)有超時(shí)邏輯,會(huì)存在一定的問(wèn)題。
存在的問(wèn)題
通過(guò)上面的分析,我們大底可以分析出問(wèn)題的關(guān)鍵了,具體如下所示:
- 沒(méi)有超時(shí)取消邏輯
- 延時(shí)完成后,任務(wù)如果沒(méi)有完成,直接回 return false,但是 Runable 依然在運(yùn)行在目標(biāo)線程的 MessageQueue 中,最終依然會(huì)得到執(zhí)行,但是不會(huì)符合我們的預(yù)期
死鎖
1,如果 Runable 在沒(méi)有執(zhí)行的時(shí)候被移除了,例如 Handler.removeCallBack,Looper.quit,這個(gè)任務(wù)就永遠(yuǎn)得不到執(zhí)行,就會(huì)導(dǎo)致 wait 一直等待。
2,如果 wait 一直無(wú)法被喚醒, 并且這個(gè)時(shí)候還持有者別的鎖,就會(huì)導(dǎo)致死鎖。
那么要如何解決呢,上面第一種也無(wú)需解決,如果它不符合你的業(yè)務(wù),你也就不需要使用它了,第二種只需要保證當(dāng)前線程沒(méi)有別的鎖,而且 looper 不能直接退出,需要退出的時(shí)候也需要安全退出(quitSafely方法)。
總結(jié)
通過(guò)分析我們也可以看出來(lái) runWithScissors 方法基本上不是偏向于業(yè)務(wù)的,而是偏向于 framwork 層的,因此該方法被標(biāo)注為了 hide 方法。如果我們業(yè)務(wù)真的需要使用這個(gè)方法,我們也完全可以仿照源碼自己寫(xiě)一個(gè)出來(lái),并且還可以隨意修改,豈不美滋滋。
原文鏈接:https://juejin.cn/post/7156821621557166087
相關(guān)推薦
- 2022-10-10 Go?語(yǔ)言前綴樹(shù)實(shí)現(xiàn)敏感詞檢測(cè)_Golang
- 2021-12-06 C語(yǔ)言PlaySound函數(shù)使用方法_C 語(yǔ)言
- 2022-09-08 返回最大值的index?pytorch方式_python
- 2022-09-25 linux系統(tǒng)下oracle數(shù)據(jù)庫(kù)的導(dǎo)入導(dǎo)出
- 2022-03-30 Python機(jī)器學(xué)習(xí)應(yīng)用之基于決策樹(shù)算法的分類預(yù)測(cè)篇_python
- 2022-11-20 spring?boot集成redis基礎(chǔ)入門(mén)實(shí)例詳解_Redis
- 2022-05-24 Go中的新增對(duì)模糊測(cè)試的支持_Golang
- 2022-04-04 css:動(dòng)畫(huà) 小米官網(wǎng)盒子陰影 心跳動(dòng)畫(huà)
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支