湖南省建設(shè)廳網(wǎng)站首頁網(wǎng)絡(luò)顧問
前言
關(guān)于IdleHandler,比較多同學(xué)錯誤地認為,這個Handler的作用是主線程空閑狀態(tài)時才執(zhí)行它,那么用它做一些耗時操作也沒所謂??墒荌dleHandler在主線程的MessageQueue中,執(zhí)行queueIdle()
默認當(dāng)然也是執(zhí)行在主線程中的,這里的耗時操作其實很容易引起卡頓和ANR。
IdleHandler的介紹
IdleHandler是一種在只有當(dāng)消息隊列沒有消息時或者是隊列中的消息還沒有到執(zhí)行時間時才會執(zhí)行的IdleHandler。從源碼上看,IdleHandler是一個回調(diào)接口,當(dāng)線程中的消息隊列將要阻塞等待消息的時候,就會回調(diào)該接口,也就是說消息隊列中的消息都處理完畢了,沒有新的消息了,處于空閑狀態(tài)時就會回調(diào)該接口。
public static interface IdleHandler {boolean queueIdle();
}
IdleHandler的使用
IdleHandler是MessageQueue
的靜態(tài)內(nèi)部接口,通過靜態(tài)方法就能拿得到,不過要注意的事,當(dāng)前Looper
是主線程的Looper
的話,取到的也是主線程的MessageQueue
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {@Overridepublic boolean queueIdle() {//空閑時處理邏輯return false;}
});
IdleHandler的問題
IdleHandler如果是主線程的執(zhí)行超過5s同樣也是會報ANR,我們通過主線程模擬休眠,會發(fā)現(xiàn)App直接ANR
Looper.myQueue().addIdleHandler(object : MessageQueue.IdleHandler {override fun queueIdle(): Boolean {Log.e("TAG", "[queueIdle] sleep(5000) start")Thread.sleep(5000)Log.e("TAG", "[queueIdle] sleep(5000) end")return false}
})
IdleHandler的監(jiān)控分析
為了防止IdleHandler濫用,監(jiān)控起來也是很有必要,特別是第三方產(chǎn)商經(jīng)常通過這個接口做一些耗時操作。通過查看源碼,找到IdleHandler的Hook點
- 通過
Looper.myQueue().addIdleHandler()
開始,可以看到是每次通過mIdleHandlers
加入到隊列中
public void addIdleHandler(@NonNull IdleHandler handler) {if (handler == null) {throw new NullPointerException("Can't add a null IdleHandler");}synchronized (this) {mIdleHandlers.add(handler);}
}
mIdleHandlers
是一個列表,會保存每一個添加進來的IdleHandler
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
- 在
Message#next()
中,執(zhí)行完Handler消息空閑后,會將當(dāng)前的IdleHandler
列表循環(huán)遍歷執(zhí)行queueIdle()
Message next() {.....for (;;) {.....if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}// 1、取出所有IdleHandlermPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {// 2、執(zhí)行IdleHandler的queueIdle()keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}}
}
IdleHandler的監(jiān)控實現(xiàn)
- 從
IdleHandler
的執(zhí)行流程可以看出,Hook點就在mIdleHandlers
列表中,將當(dāng)前的MessageQueue
中的mIdleHandlers
列表替換成自己的列表
class IdleHandlerMonitor {private HandlerThread idleHandlerThread;private Handler idleHandlerHandler;private static String mIdleHandler = null;IdleHandlerMonitor() {// 1、創(chuàng)建子線程的handler(idleHandlerHandler),方便后續(xù)在子線程發(fā)送消息不被主線程卡住影響if (Build.VERSION.SDK_INT >= 23) {this.idleHandlerThread = new HandlerThread("IdleHandlerThread");this.idleHandlerThread.start();this.idleHandlerHandler = new Handler(this.idleHandlerThread.getLooper());this.detectIdleHandler();}}@RequiresApi(api = 23)private void detectIdleHandler() {// 2、修改MessageQueue中的mIdleHandlers變量,傳入自定義的Listtry {MessageQueue mainQueue = Looper.getMainLooper().getQueue();Field field = MessageQueue.class.getDeclaredField("mIdleHandlers");field.setAccessible(true);CustomArrayList<MessageQueue.IdleHandler> myIdleHandlerArrayList = new CustomArrayList();field.set(mainQueue, myIdleHandlerArrayList);} catch (Throwable var4) {var4.printStackTrace();}}private class CustomArrayList<T> extends ArrayList {public boolean add(Object o) {if (o instanceof MessageQueue.IdleHandler) {// 3、將原來的IdleHandler包裝進自己的CustomIdleHandlerCustomIdleHandler customIdleHandler = new CustomIdleHandler((MessageQueue.IdleHandler) o);return super.add(customIdleHandler);}return super.add(o);}public boolean remove(@Nullable Object o) {if (o instanceof CustomIdleHandler) {return super.remove(((CustomIdleHandler) o));}return super.remove(o);}}private class CustomIdleHandler implements MessageQueue.IdleHandler {private MessageQueue.IdleHandler idleHandler;CustomIdleHandler(MessageQueue.IdleHandler idleHandler) {this.idleHandler = idleHandler;}@Overridepublic boolean queueIdle() {mIdleHandler = this.idleHandler.toString();idleHandlerHandler.removeCallbacks(idleHanlderRunnable);idleHandlerHandler.postDelayed(idleHanlderRunnable, 3000L);// 4、將包裝起來的IdleHandler取出來,執(zhí)行queueIdle,包裝前設(shè)置多一項3s的延時任務(wù)。// 只要queueIdle在3s內(nèi)沒執(zhí)行完,將執(zhí)行當(dāng)前的idleHanlderRunnableboolean ret = this.idleHandler.queueIdle();idleHandlerHandler.removeCallbacks(idleHanlderRunnable);return ret;}}// 5、報告輸出當(dāng)前Idle信息超時通知private static Runnable idleHanlderRunnable = () -> {Log.e("TAG", "[queueIdle] more then 3000L \n message=" + mIdleHandler);};
}
hook的巧妙點在于將當(dāng)前的List換成自己的List,然后在List的添加和刪除中,偷梁換柱成自己的IdleHandler
進行加工處理
IdleHandler的驗證
Hook解決完之后,我們來通過Demo驗證下是否是我們想要的監(jiān)控,通過初始化IdleHandlerMonitor()
啟動監(jiān)控,然后模擬3次IdleHandler
發(fā)送不同時間的消息,最后看日志輸出,是否被捕獲到超時的Idle任務(wù)
private fun initIdelHandler() {IdleHandlerMonitor()Looper.myQueue().addIdleHandler(object : MessageQueue.IdleHandler {override fun queueIdle(): Boolean {Log.e("TAG", "[queueIdle] sleep(2000) start")Thread.sleep(2000)Log.e("TAG", "[queueIdle] sleep(2000) end")return false}})Looper.myQueue().addIdleHandler(object : MessageQueue.IdleHandler {override fun queueIdle(): Boolean {Log.e("TAG", "[queueIdle] sleep(5000) start")Thread.sleep(5000)Log.e("TAG", "[queueIdle] sleep(5000) end")return false}})Looper.myQueue().addIdleHandler(object : MessageQueue.IdleHandler {override fun queueIdle(): Boolean {Log.e("TAG", "[queueIdle] sleep(10000) start")Thread.sleep(10000)Log.e("TAG", "[queueIdle] sleep(10000) end")return false}})
}
通過日志輸出結(jié)果,可以看到當(dāng)前的Idle阻塞3s時候的代碼類位置
E/TAG: [queueIdle] sleep(2000) start
E/TAG: [queueIdle] sleep(2000) end
E/TAG: [queueIdle] sleep(5000) start
E/TAG: [queueIdle] more then 3000L message=com.example.syncbarriermonitor.MainActivity$initIdelHandler$2@4d9ab66
E/TAG: [queueIdle] sleep(5000) end
E/TAG: [queueIdle] sleep(10000) start
E/TAG: [queueIdle] more then 3000L message=com.example.syncbarriermonitor.MainActivity$initIdelHandler$3@be7cc0