Activity的初始化優化

在Android 應用開發中,我們最關注的莫過於Activity的啓動速度了.可以說啓動速度的好壞一直是我們應用能否成功吸引用戶的關鍵所在. 試想一個界面從點擊入口到真正界面顯示的過程,如果太長,用戶將失去耐心,進而無情拋棄這個應用.
那麼如何加快一個界面的顯示速度呢?我們一般都會去研究Activity的初始化流程.實際上,如果我們新建一個Activity,在它的生命週期內什麼都不幹,那麼他的啓動和顯示已經足夠快了. 因爲它是系統級的流程控制.我們不需要過多的去關注.

到最後我們都會得出一個很無奈的結論,Activity的啓動速度實際跟我們初始化耗時是相關的. 初始化代碼快,啓動就快,初始化慢,啓動就慢.

方案一: 異步線程進行初始化.

有人可能會想那如果我把初始化過程放在異步任務中去做不就可以了嗎?
確實有很多應用是這麼做的, 在onCreate()的時候去開啓一個異步線程,然後開始幹活,最後把結果Handler到主線程.
但這麼做仍然降低了啓動速度! 啓動線程的損耗, 耗時任務搶佔CPU的損耗,最終導致界面仍然會慢幾個節奏.

方案二: postDelay(r,100)

有的人則會想到如果等界面完全顯示出來了,再行初始化不就好了嗎?
於是在onCreate()中用postDelay()的方式,進行人工的延時處理.此舉確實解決了延時初始化的問題,但是卻引入了另一個問題, 100ms的延時時間真的就是界面初始化顯示完成的時間嗎? 對於不同的手機,這將是一個未知數. 因此,方案最終表現出來的效果是,在有的手機上界面很快顯示,但卻要繼續等一段時間纔開始初始化. 而另一些手機,則因爲延時太短導致界面沒有顯示完全時就開始初始化,最終搶佔了主線程,降低了啓動速度.

方案三:正確的界面初始化完成時機

通過在Activity的各個生命週期函數中打上日誌,你以爲界面完全顯示是在onStart()中嗎?NO! 你以爲界面完全顯示是在onResume()中嗎? NO! 你以爲是在onAttachToWindow()中嗎? NO! 當你在這些生命週期中進行初始化的時候,你會發現界面仍然需要等到初始化完成纔會顯示出來.
爲什麼呢? 最終問題的思路定位到了Message-Loop中. 主線程是一個完全不能承受耗時操作的循環.而Activity的生命週期函數都是通過Loop的消息依次被執行的. 所以,如果有哪一個生命週期函數被耗時操作卡住,那麼這個循環是無法繼續滾動,也無法繼續發送界面渲染消息進行界面繪製的. 相信大家都知道,View.setVisible(VISIBLE) 實際上是發送了一個渲染的消息的.而不是真正的去直接繪製界面了. 因此如果這個消息被阻塞,那麼界面仍然會是一片空白.

那麼問題來了?我們如何確定Activity一定被繪製完成呢? 通過追尋Loop的實現,我們找到了一個東西.
Looper.myQueue().addIdleHandler().這個函數用於給Looper中添加一個用於處理Looper空閒時的事件響應-Handle. Looper何時會空閒呢?答案就是當一個Activity的所有初始化的生命週期過程被執行完成,並且界面渲染也完成的時候,Looper裏邊就沒有其他的消息了. 於是我們就可以開始進行初始化了.不早也不晚.
甚至非常準時的在界面迅速顯示完成的時候就開始初始化內容.
爲此我們寫了一個簡單的封裝:

public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Looper.myQueue().addIdleHandler(new IdleHandler() {
            @Override
            public boolean queueIdle() {
                Log.i("IdleHandler","queueIdle");
                onInit();
                return false; //false 表示只監聽一次IDLE事件,之後就不會再執行這個函數了.
            }
        });
    }
    //子類重寫此函數即可,而不需要在onCreate()中去初始化.
    protected void onInit() {
        Log.e("BaseActivity", "onInit");
    }
}

經測試,在onInit()中做耗時操作時界面的顯示速度完全沒有影響,因爲在做耗時操作前界面已經成功顯示到屏幕上了!
當然並不是說就可以在onInit()中做非常耗時的操作了,它畢竟還是在主線程中執行的,因此會卡住界面直到執行完成,此期間如果想點擊某個按鈕,抱歉,我正在耗時,你點不動.

但是對於100ms,50ms這樣的初始化耗時來說,完全可以接受的,因爲從界面顯示出來到人眼反應過來,最後再到人手有意識的開始操作一般都會在1s以上. 而在1s內,初始化已經完成了.所以onInit()中仍然僅適合做一些微小耗時的操作. 而不適合做同步任務.你仍然需要將一些非常耗時的操作放入異步任務中去.

這裏的優化,真正的意義是,加快Activity界面展現的速度,讓用戶第一眼儘快看到界面.然後纔開始做你的初始化過程.而不是等初始化完成纔給用戶看界面.

來源:https://blog.csdn.net/u014700919/article/details/51178877

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章