偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

面試官:"Handler的runWithScissors()了解嗎?為什么Google不讓開(kāi)發(fā)者用?"

開(kāi)發(fā) 開(kāi)發(fā)工具
runWithScissors() 是 Handler 的一個(gè)方法,被標(biāo)記為 @hide,不允許普通開(kāi)發(fā)者調(diào)用。

 [[334762]]

一、序

大家好,這里是承香墨影!

runWithScissors() 是 Handler 的一個(gè)方法,被標(biāo)記為 @hide,不允許普通開(kāi)發(fā)者調(diào)用。

這個(gè)方法算是比較冷門,如果面試中被問(wèn)及,面試者不知道時(shí),通常面試官會(huì)換個(gè)問(wèn)法:"如何在子線程通過(guò) Handler 向主線程發(fā)送一個(gè)任務(wù),并等主線程處理此任務(wù)后,再繼續(xù)執(zhí)行?"。

這個(gè)場(chǎng)景,就可以借助 runWithScissors() 來(lái)實(shí)現(xiàn)。雖然該方法被標(biāo)記為 @hide,但是在 Framework 中,也有不少場(chǎng)景使用到它。不過(guò)它也有一些隱患,正是因?yàn)檫@些隱患,讓 Android 工程師將其標(biāo)為 @hide,不允許普通開(kāi)發(fā)者使用。

今天我們就來(lái)聊聊 Handler 的這個(gè)冷門的方法 runWithScissors(),以及它可能出現(xiàn)的一些問(wèn)題。

二、Handler.runWithScissors()

2.1 runWithScissors()

先撇開(kāi) runWithScissors() 方法,既然這里存在 2 個(gè)線程的通信,那肯定需要考慮多線程同步。

首先想到的就是 Synchronized 鎖和它的等待/通知機(jī)制,而通過(guò) Handler 跨線程通信時(shí),想要發(fā)送一個(gè)「任務(wù)」,Runnable 肯定比 Message 更適合。

接下來(lái),我們看看 runWithScissors() 的實(shí)現(xiàn)是不是如我們預(yù)想一樣。

  1. public final boolean runWithScissors(final Runnable r, long timeout) { 
  2.   if (r == null) { 
  3.     throw new IllegalArgumentException("runnable must not be null"); 
  4.   } 
  5.   if (timeout < 0) { 
  6.     throw new IllegalArgumentException("timeout must be non-negative"); 
  7.   } 
  8.  
  9.   if (Looper.myLooper() == mLooper) { 
  10.     r.run(); 
  11.     return true
  12.   } 
  13.  
  14.   BlockingRunnable br = new BlockingRunnable(r); 
  15.   return br.postAndWait(this, timeout); 

可以看到,runWithScissors() 接受一個(gè) Runnable,并且可以設(shè)置超時(shí)時(shí)間。

流程也非常簡(jiǎn)單:

  1. 先簡(jiǎn)單的對(duì)入?yún)⑦M(jìn)行校驗(yàn);
  2. 如果當(dāng)前線程和 Handler 的處理線程一致,則直接運(yùn)行 run() 方法;
  3. 線程不一致,則通過(guò) BlockingRunnable 包裝一下,并執(zhí)行其 postAndWait() 方法;

那再繼續(xù)看看 BlockingRunnable 的源碼。

  1. private static final class BlockingRunnable implements Runnable { 
  2.   private final Runnable mTask; 
  3.   private boolean mDone; 
  4.  
  5.   public BlockingRunnable(Runnable task) { 
  6.     mTask = task; 
  7.   } 
  8.  
  9.   @Override 
  10.   public void run() { 
  11.     try { 
  12.       // 運(yùn)行在 Handler 線程 
  13.       mTask.run(); 
  14.     } finally { 
  15.       synchronized (this) { 
  16.         mDone = true
  17.         notifyAll(); 
  18.       } 
  19.     } 
  20.   } 
  21.  
  22.   public boolean postAndWait(Handler handler, long timeout) { 
  23.     if (!handler.post(this)) { 
  24.       return false
  25.     } 
  26.  
  27.     synchronized (this) { 
  28.       if (timeout > 0) { 
  29.         final long expirationTime = SystemClock.uptimeMillis() + timeout; 
  30.         while (!mDone) { 
  31.           long delay = expirationTime - SystemClock.uptimeMillis(); 
  32.           if (delay <= 0) { 
  33.             return false; // timeout 
  34.           } 
  35.           try { 
  36.             wait(delay); 
  37.           } catch (InterruptedException ex) { 
  38.           } 
  39.         } 
  40.       } else { 
  41.         while (!mDone) { 
  42.           try { 
  43.             wait(); 
  44.           } catch (InterruptedException ex) { 
  45.           } 
  46.         } 
  47.       } 
  48.     } 
  49.     return true
  50.   } 

待執(zhí)行的任務(wù),會(huì)記入 BlockingRunnable 的 mTask,等待后續(xù)被調(diào)用執(zhí)行。

postAndWait() 的邏輯也很簡(jiǎn)單,先通過(guò) handler 嘗試將 BlockingRunnable 發(fā)出去,之后進(jìn)入 Synchronized 臨界區(qū),嘗試 wait() 阻塞。

如果設(shè)置了 timeout,則使用 wait(timeout) 進(jìn)入阻塞,若被超時(shí)喚醒,則直接返回 false,表示任務(wù)執(zhí)行失敗。

那么現(xiàn)在可以看到 postAndWait() 返回 false 有 2 個(gè)場(chǎng)景:

  1. Handler post() 失敗,表示 Looper 出問(wèn)題了;
  2. 等待超時(shí),任務(wù)還沒(méi)有執(zhí)行結(jié)束;

除了超時(shí)喚醒外,我們還需要在任務(wù)執(zhí)行完后,喚醒當(dāng)前線程。

回看 BlockingRunnable 的 run() 方法,run() 被 Handler 調(diào)度并在其線程執(zhí)行。在其中調(diào)用 mTask.run(),mTask 即我們需要執(zhí)行的 Runnable 任務(wù)。執(zhí)行結(jié)束后,標(biāo)記 mDone 并通過(guò) notifyAll() 喚醒等待。

任務(wù)發(fā)起線程,被喚醒后,會(huì)判斷 mDone,若為 true 則任務(wù)執(zhí)行完成,直接返回 true 退出。

2.2 Framework 中的使用

runWithScissors() 被標(biāo)記為 @hide,應(yīng)用開(kāi)發(fā)一般是用不上的,但是在 Framework 中,卻有不少使用場(chǎng)景。

例如比較熟悉的 WMS 啟動(dòng)流程中,分別在 main() 和 initPolicy() 中,通過(guò) runWithScissors() 切換到 "android.display" 和 "android.ui" 線程去做一些初始工作。

  1. private void initPolicy() { 
  2.   UiThread.getHandler().runWithScissors(new Runnable() { 
  3.     public void run() { 
  4.       // 運(yùn)行在"android.ui"線程 
  5.       WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); 
  6.       mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); 
  7.     } 
  8.   }, 0); 

例如上面代碼,就是從 "android.display" 線程,通過(guò)切換到 "android.ui" 線程去執(zhí)行任務(wù)。

三、runWithScissors() 的問(wèn)題

看似 runWithScissors() 通過(guò) Synchronized 的等待通知機(jī)制,配合 Handler 發(fā)送 Runnable 執(zhí)行阻塞任務(wù),看似沒(méi)有問(wèn)題,但依然被 Android 工程師設(shè)為 @hide。

我們繼續(xù)看看它的問(wèn)題。

3.1 如果超時(shí)了,沒(méi)有取消的邏輯

通過(guò) runWithScissors() 發(fā)送 Runnable 時(shí),可以指定超時(shí)時(shí)間。當(dāng)超時(shí)喚醒時(shí),是直接 false 退出。

當(dāng)超時(shí)退出時(shí),這個(gè) Runnable 依然還在目標(biāo)線程的 MessageQueue 中,沒(méi)有被移除掉,它最終還是會(huì)被 Handler 線程調(diào)度并執(zhí)行。

此時(shí)的執(zhí)行,顯然并不符合我們的業(yè)務(wù)預(yù)期。

3.2 可能造成死鎖

而更嚴(yán)重的是,使用 runWithScissors() 可能造成調(diào)用線程進(jìn)入阻塞,而得不到喚醒,如果當(dāng)前持有別的鎖,還會(huì)造成死鎖。

我們通過(guò) Handler 發(fā)送的 MessageQueue 的消息,一般都會(huì)得到執(zhí)行,而當(dāng)線程 Looper 通過(guò) quit() 退出時(shí),會(huì)清理掉還未執(zhí)行的任務(wù),此時(shí)發(fā)送線程,則永遠(yuǎn)得不到喚醒。

那么在使用 runWithScissors() 時(shí),就要求 Handler 所在的線程 Looper,不允許退出,或者使用 quitSafely() 方式退出。

quit() 和 quitSafely() 都表示退出,會(huì)去清理對(duì)應(yīng)的 MessageQueue,區(qū)別在于,qiut() 會(huì)清理 MessageQueue 中所有的消息,而 quitSafely() 只會(huì)清理掉當(dāng)前時(shí)間點(diǎn)之后(when > now)的消息,當(dāng)前時(shí)間之前的消息,依然會(huì)得到執(zhí)行。

那么只要使用 quitSafely() 退出,通過(guò) runWithScissors() 發(fā)送的任務(wù),依然會(huì)被執(zhí)行。

也就是說(shuō),安全使用 runWithScissors() 要滿足 2 個(gè)條件:

Handler 的 Looper 不允許退出,例如 Android 主線程 Looper 就不允許退出;

Looper 退出時(shí),使用安全退出 quitSafely() 方式退出;

四、總結(jié)時(shí)刻

今天我們介紹了一個(gè)冷門的方法 runWithScissors() 以及其原理,可以通過(guò)阻塞的方式,向目標(biāo)線程發(fā)送任務(wù),并等待任務(wù)執(zhí)行結(jié)束。

雖然被它標(biāo)記為 @hide,無(wú)法直接使用,但這都是純軟件實(shí)現(xiàn),我們其實(shí)可以自己實(shí)現(xiàn)一個(gè) BlockingRunnable 去使用。當(dāng)然原本存在的問(wèn)題,在使用時(shí)也需要注意。

我知道就算這個(gè)方法不被標(biāo)記為 @hide,使用的場(chǎng)景也非常的少,但是它依然可以幫助我們思考一些臨界問(wèn)題,線程的同步、死鎖,以及 Handler 的退出方式對(duì)消息的影響。

 

 

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專欄
相關(guān)推薦

2017-02-14 15:18:44

GoogleAndroid

2022-07-26 08:40:42

Java并發(fā)工具類

2022-08-02 06:31:32

Java并發(fā)工具類

2021-02-19 10:02:57

HTTPSJava安全

2022-06-30 08:14:05

Java阻塞隊(duì)列

2022-07-06 13:48:24

RedisSentinel機(jī)制

2022-07-11 10:47:46

容器JAVA

2023-12-06 09:10:28

JWT微服務(wù)

2020-10-24 15:50:54

Java值傳遞代碼

2021-01-21 07:53:29

面試官Promis打印e

2016-03-17 11:06:46

跳槽加薪面試

2024-09-09 08:30:56

代碼

2022-06-30 14:31:57

Java阻塞隊(duì)列

2010-08-17 09:01:39

jQueryAPI

2022-12-27 08:39:54

MySQL主鍵索引

2012-09-17 17:42:48

Google Play盈利開(kāi)發(fā)者

2022-06-08 13:54:23

指令重排Java

2013-09-03 09:42:13

Android開(kāi)發(fā)者

2021-12-20 10:30:33

forforEach前端

2022-06-10 13:56:42

Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)