Android App秒開,狹義的講是指你的App的Activity從啓動到顯示所花費的時間在1秒之內,廣義的講是指這個過程所花費的時間越少越好。這個時間越短,你的App給用戶的感受就是響應越快,使用越流暢,用戶體驗更好。秒開是Android App的一個很重要的性能指標。須要咱們持續的給予關注和優化。 #如何優化秒開 Google提供了不少性能優化的建議和官方的工具,網上也有很是多的關於Android App性能優化的文章和工具,能夠幫助你解決大部分卡頓的問題。可是現實卻多是即便你付出了不少精力去作優化,你的App仍是在啓動新Activity的時候花費過多的時間。特別是隨着需求的不斷增加,你的App會變得複雜而龐大,要作優化首先要定位須要優化的點,而這會變得愈發困難。同時大型App在啓動新Activity的時間花費過多狀況出現的可能性反而會愈來愈大。html
在衆多的優化建議中,有一條比較基本的原則是儘可能避免在主線程(或者說UI線程)中進行耗時操做。例如文件讀寫操做、網絡請求、大量計算、循環等等。直觀的理解是由於啓動新Activity須要在主線程執行不少代碼,例如onCreate()等生命週期的回調。若是此時有耗時操做的代碼在主線程被執行,到新Activity展現出來所須要的時間就會延長。要優化秒開,首先要能監測主線程的運行狀態,那麼問題來了,主線程究竟是怎樣在運行呢?你的代碼又是何時,如何在主線程被執行的呢?java
要了解主線程的工做過程,首先要了解Android的消息機制。android
先看一下現實生活中的一個例子,雖然如今都是移動支付了,但相信你們都去銀行取過錢。當你到達銀行的時候,若是你是第一個,那恭喜你,你能夠立刻到櫃員那裏辦理你的業務;若是你前面還有人,那就比較慘了,你須要排隊,得等到你前面的人都辦完業務纔會輪到你;更可怕的是若是你前面有幾位須要辦理的業務花費的時間比較長,那你須要等更長的時間;後面來的人則會按順序排在你身後,和你同樣不耐煩的琢磨何時才能輪到本身。git
抽象一下,消息機制其實和這個例子十分相似。每一個人都看作是個消息,何時到的銀行是不肯定的。櫃員能夠看作一個消息處理器,他幫你辦業務就至關於在處理你的消息;而人們按照前後順序排起來的隊伍能夠看作是個消息隊列。因此這個過程能夠抽象爲有個消息處理器,他有個消息隊列,隨機來到的消息按照必定順序排列在這個隊列裏,消息處理器不停的從隊列頭部獲取消息而後處理之,周而復始的循環重複這個過程。以下圖所示: github
那麼Android是怎樣怎樣實現這個消息機制的呢?消息機制首先得有消息,在Android中就是Message。怎樣能肯定一個消息呢?消息要有來源或者目標,也就是target;消息要代表本身要作什麼,也就是what或者callback;消息要代表本身但願在何時執行,也就是when。有了這幾個要素,基本上這個消息就是個完備的消息了,能夠被加入到消息隊列中了。Android中的消息隊列是MessageQueue。消息處理循環是Looper。Looper是個死循環,不停的從MessageQueue中獲取消息而後處理之,具體的執行是在Handler裏面進行的。另外消息加入消息隊列也須要Handler來操做。Message,MessageQueue,Looper,Handler組合在一塊兒,就構成了整個Android的消息機制。性能優化
Android的主線程就運行着這樣一個消息機制。bash
主線程是在ActivityThread中建立的,能夠看到在main函數中網絡
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
複製代碼
主線程實現了一個消息機制。因此Android的主線程就是個消息處理的循環。它所作的工做就是在不停的從消息隊列獲取消息,處理消息,周而復始。你的App全部的在UI上的操做,例如點擊事件的處理、頁面動畫、顯示更新頁面、View繪製、啓動新Activity等操做都是在給主線程發消息,主線程而後挨個處理這些消息。app
咱們瞭解了主線程的工做機制後,就要看看主線程中的消息處理是如何影響Activity秒開的。 當咱們要啓動一個新的Activity的時候,從調用startActivity開始到新Activity顯示出來,Android系統會發送一系列的消息給主線程。這一系列的消息處理所花費的總時間會影響頁面的秒開,若是執行時間過長,用戶就會有響應很是慢的感受。此外,除了Android系統會給主線程發消息,App自身也會給主線程發消息,若是在啓動新Activity的過程當中,這些App本身的消息正好插入這一系列的Android系統消息中,那也會致使總的處理時間延長,形成不能秒開。 ide
上圖表明瞭啓動新Activity的主線程的三種狀況,每一個矩形表明主線程處理一個消息所花的時間,越寬表明處理的時間越長。綠色填充的表明這是一個Android系統發過來的消息;藍色填充的表明這是一個App本身發過來的消息。最下方的向右箭頭表明時間,起點是startActivity被調用的時刻。瞭解了影響秒開的因素以後,咱們只要有辦法能監測主線程中每一個消息處理時間,咱們就能定位到形成頁面卡慢的緣由,而後再作優化。 幸虧Android工程師爲咱們在Looper中預留了打log的位置。
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
...
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
msg.recycleUnchecked();
}
}
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
複製代碼
可見在消息被處理的開始和處理結束以後都會打印log。 你只須要在代碼中調用Looper.setMessageLogging()設置一下就好。
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String s) {
Log.v("debug", s);
}
});
複製代碼
編譯運行你的程序,你會在logcat輸出看到相似這樣的log:
每行 「>>>>> Dispatching to」開頭的log表明一個消息即將開始被處理;緊接着下一行「<<<<< Finished to」開頭的log表明這一消息處理完畢。經過這些log你能夠知道全部被主線程處理的消息,並能夠根據開始結束的時間差知道每一個消息消耗的時間。有了這些信息你能夠找到致使你的app卡慢的消息,而後進一步去debug問題。在你啓動一個新的Activity的時候你能夠觀測這樣的log輸出,看看裏面有沒有處理時間比較長的消息,或者看看裏面有沒有App本身的消息被處理,若是有的話,這些都是須要優化的點。
然而直接看log的缺點是這樣的log會比較多,並且並不容易定位啓動Activity的開始和結束時間點,另外每一個消息處理的時間也要本身計算,並非十分直觀。
爲了方便的進行秒開優化,我作了個工具叫StallBuster來協助定位Activity秒開失敗的緣由。
集成StallBuster很是簡單,只須要兩步就能夠了
dependencies {
compile 'com.github.zhangjianli:stallbuster:1.1'
}
複製代碼
public class YourApplication extends Application {
@Override
public void onCreate() {
StallBuster.getInstance().init(this);
super.onCreate();
}
}
複製代碼
這樣就能夠了,編譯運行你的App。在你的App中打開新的Activity,StallBuster會發出一個Notification。告訴你剛啓動這個Activity花了多少毫秒
點擊這個Notification就會打開StallBuster的歷史記錄頁面。 這個頁面按照時間順序列出了你的App啓動每一個Activity的歷史記錄。每條記錄最左邊是啓動所花費的時間。綠色表明所費時間符合秒開要求;紅色表明時間太長。須要關注。右邊是這條記錄對應的Activity名稱。點擊某條記錄就會進入詳情頁。 在詳情頁裏你能夠看到啓動這個Activity的過程當中主線程處理過的消息。上方的複選框能夠過濾執行時間比較短的消息,方便定位問題。對於每條記錄,首先顯示的是這條消息開始被處理的時間戳。而後是cost字段,表示處理這條消息花了多長時間。正常狀況下是字體是黑色的;若是處理時間過長,則顯示爲紅色。代表這裏多是咱們須要優化的地方。
接下來是target字段,對應的是這個消息是被哪一個Handler處理的。Android系統的Handler會顯示爲黑色;App本身的Handler會顯示爲紅色,代表這個消息不該該在啓動Activity的時候出現,這裏也多是須要優化的地方。
例如上圖中第一條記錄,.MainActivity$StallHandler處理這個消息花費了142ms。這會使啓動SubActivity的時間至少延長了142ms。而這個Handler是App本身的Handler。咱們須要調試代碼使得在啓動這個Activity的時候確保不會有來自這個Handler的消息,142ms的時間就會節省下來。
最後一個字段是message或者callback。對應的是Message中的what或callback。有了這些信息咱們就能很方便的定位主線程中影響秒開的消息,進而優化咱們的App。
StallBuster就給你們介紹到這裏,但願StallBuster能幫到你。若是你們有任何建議或者問題請給我留言。
App秒開是是一項很是重要的性能指標。秒開的優化是個複雜的工做,有不少因素會影響App秒開。其中比較重要的一個因素是啓動Activity的時候主線程的消息處理狀況。在啓動Activity過程當中須要避免消息處理時間過長,也要避免在此期間有App本身的消息須要處理。優化的關鍵點是要定位到主線程中的耗時操做,咱們能夠經過打印分析主線程的消息處理log來定位,但這種方式並非很直觀方便。這時可使用StallBuster幫助你快速定位秒開問題點,讓秒開優化變的更加簡單。