一胳挎、背景
我們在做啟動性能優(yōu)化的時候饼疙,需要盡可能多地減少啟動階段主線程執(zhí)行的任務(wù)時長。對一些非啟動階段一定需要完成的任務(wù)慕爬,我們可以把他放到應(yīng)用啟動完成之后去執(zhí)行窑眯,這就是啟動性能優(yōu)化中的延遲加載方案。
二医窿、常規(guī)方案
一般的方案是通過handler.postDelay延遲一段時間執(zhí)行磅甩。但這種方案延遲的時間不好把握,配置高的機(jī)器和配置低的機(jī)器時間也不一樣姥卢。而且如果延遲執(zhí)行的任務(wù)較多卷要, 且需要在主線程中執(zhí)行,則在執(zhí)行延遲任務(wù)時會因?yàn)橐淮涡詧?zhí)行多個任務(wù)而導(dǎo)致主線程被占用一段時間造成用戶操作卡頓独榴。
三僧叉、更優(yōu)方案
IdleHandler能在當(dāng)前線程消息隊(duì)列空閑時執(zhí)行一些事情,且可以一個個任務(wù)單獨(dú)執(zhí)行括眠,不用一次性執(zhí)行所有任務(wù)彪标,緩解主線程卡頓現(xiàn)象。使用方法如下:
//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
//你要處理的事情
return false;
}
});
queueIdle返回false表示只執(zhí)行一次掷豺,如果返回true捞烟,則下次在消息隊(duì)列空閑的時候還會執(zhí)行,這樣就可以實(shí)現(xiàn)一個任務(wù)一個任務(wù)的執(zhí)行当船,不會一次性占用當(dāng)前線程過多時間而造成卡頓题画。
四、IdleHandler源碼分析
/**
* 獲取當(dāng)前線程隊(duì)列使用Looper.myQueue()德频,獲取主線程隊(duì)列可用getMainLooper().myQueue()
*/
public final class MessageQueue {
......
/**
* 當(dāng)前隊(duì)列將進(jìn)入阻塞等待消息時調(diào)用該接口回調(diào)苍息,即隊(duì)列空閑
*/
public static interface IdleHandler {
/**
* 返回true就是單次回調(diào)后不刪除,下次進(jìn)入空閑時繼續(xù)回調(diào)該方法壹置,false只回調(diào)單次竞思。
*/
boolean queueIdle();
}
/**
* <p>This method is safe to call from any thread.
* 判斷當(dāng)前隊(duì)列是不是空閑的,輔助方法
*/
public boolean isIdle() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
return mMessages == null || now < mMessages.when;
}
}
/**
* <p>This method is safe to call from any thread.
* 添加一個IdleHandler到隊(duì)列钞护,如果IdleHandler接口方法返回false則執(zhí)行完會自動刪除盖喷,
* 否則需要手動removeIdleHandler。
*/
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
/**
* <p>This method is safe to call from any thread.
* 刪除一個之前添加的 IdleHandler难咕。
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
......
//Looper的prepare()方法會通過ThreadLocal準(zhǔn)備當(dāng)前線程的MessageQueue實(shí)例课梳,
//然后在loop()方法中死循環(huán)調(diào)用當(dāng)前隊(duì)列的next()方法獲取Message距辆。
Message next() {
......
for (;;) {
......
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
......
//把通過addIdleHandler添加的IdleHandler轉(zhuǎn)成數(shù)組存起來在mPendingIdleHandlers中
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//循環(huán)遍歷所有IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//調(diào)用IdleHandler接口的queueIdle方法并獲取返回值。
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果IdleHandler接口的queueIdle方法返回false說明只執(zhí)行一次需要刪除暮刃。
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
......
}
}
}
五跨算、總結(jié)
綜上所述,在設(shè)計(jì)延遲加載方案的時候椭懊,可以考慮用IdleHandler這種優(yōu)雅的方式來實(shí)現(xiàn)诸蚕,而不是寫死個時間延遲執(zhí)行。