Android性能優化之加快應用啓動速度

應用的啓動

啓動方式

通常來說,在安卓中應用的啓動方式分爲兩種:冷啓動和熱啓動。

1、冷啓動:當啓動應用時,後臺沒有該應用的進程,這時系統會重新創建一個新的進程分配給該應用,這個啓動方式就是冷啓動。

2、熱啓動:當啓動應用時,後臺已有該應用的進程(例:按back鍵、home鍵,應用雖然會退出,但是該應用的進程是依然會保留在後臺,可進入任務列表查看),所以在已有進程的情況下,這種啓動會從已有的進程中來啓動應用,這個方式叫熱啓動。

特點

1、冷啓動:冷啓動因爲系統會重新創建一個新的進程分配給它,所以會先創建和初始化Application類,再創建和初始化MainActivity類(包括一系列的測量、佈局、繪製),最後顯示在界面上。

2、熱啓動:熱啓動因爲會從已有的進程中來啓動,所以熱啓動就不會走Application這步了,而是直接走MainActivity(包括一系列的測量、佈局、繪製),所以熱啓動的過程只需要創建和初始化一個MainActivity就行了,而不必創建和初始化Application,因爲一個應用從新進程的創建到進程的銷燬,Application只會初始化一次。

上面說的啓動是點擊app的啓動圖標來啓動的,而另外一種方式是進入最近使用的列表界面來啓動應用,這種不應該叫啓動,應該叫恢復。

應用啓動的流程

在安卓系統上,應用在沒有進程的情況下,應用的啓動都是這樣一個流程:當點擊app的啓動圖標時,安卓系統會從Zygote進程中fork創建出一個新的進程分配給該應用,之後會依次創建和初始化Application類、創建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設置給MainActivity以及配置Activity層級上的一些屬性、再inflate佈局、當onCreate/onStart/onResume方法都走完了後最後才進行contentView的measure/layout/draw顯示在界面上,所以直到這裏,應用的第一次啓動纔算完成,這時候我們看到的界面也就是所說的第一幀。

所以,總結一下,應用的啓動流程如下:

Application的構造器方法——>attachBaseContext()——>onCreate()——>Activity的構造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測量佈局繪製顯示在界面上。

測量應用啓動的時間

在上面這個啓動流程中,任何一個地方有耗時操作都會拖慢我們應用的啓動速度,而應用啓動時間是用毫秒度量的,對於毫秒級別的快慢度量我們還是需要去精確的測量到到底應用啓動花了多少時間,而根據這個時間來做衡量。

什麼纔是應用的啓動時間

從點擊應用的啓動圖標開始創建出一個新的進程直到我們看到了界面的第一幀,這段時間就是應用的啓動時間。

我們要測量的也就是這段時間,測量這段時間可以通過adb shell命令的方式進行測量,這種方法測量的最爲精確,命令爲:

<code class="hljs sql has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">adb shell am <span class="hljs-operator" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">start</span> -W [packageName]/[packageName.MainActivity]</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

執行成功後將返回三個測量到的時間: 
1、ThisTime:一般和TotalTime時間一樣,除非在應用啓動時開了一個透明的Activity預先處理一些事再顯示出主Activity,這樣將比TotalTime小。 
2、TotalTime:應用的啓動時間,包括創建進程+Application初始化+Activity初始化到界面顯示。 
3、WaitTime:一般比TotalTime大點,包括系統影響的耗時。

下面是測量一個應用冷啓動和熱啓動的時間: 
冷啓動: 
這裏寫圖片描述 
熱啓動: 
這裏寫圖片描述

可以看到在進程已經存在的情況下,只需要重新初始化MainActivity,這樣的啓動比較快,不過大多數情況下應用的啓動都是冷啓動,因爲用戶都會在任務列表中手動關閉遺留的應用進程。

減少應用啓動時的耗時

針對冷啓動時候的一些耗時,如上測得這個應用算是中型的app,在冷啓動的時候耗時已經快700ms了,如果項目再大點在Application中配置了更多的初始化操作,這樣將可能達到1s,這樣每次啓動都明顯感覺延遲,所以在進行應用初始化的時候採取以下策略: 
1、在Application的構造器方法、attachBaseContext()、onCreate()方法中不要進行耗時操作的初始化,一些數據預取放在異步線程中,可以採取Callable實現。 
2、對於sp的初始化,因爲sp的特性在初始化時候會對數據全部讀出來存在內存中,所以這個初始化放在主線程中不合適,反而會延遲應用的啓動速度,對於這個還是需要放在異步線程中處理。 
3、對於MainActivity,由於在獲取到第一幀前,需要對contentView進行測量佈局繪製操作,儘量減少佈局的層次,考慮StubView的延遲加載策略,當然在onCreate、onStart、onResume方法中避免做耗時操作。

遵循上面三種策略可明顯提高app啓動速度。

優化應用啓動時的體驗

對於應用的啓動時間,只能是儘量的避免一些耗時的、非必要的操作在主線程中,這樣相對可以縮減一部分啓動的耗時,另外一方面在等待第一幀顯示的時間裏,可以加入一些配置以增加體驗,比如加入Activity的background,這個背景會在顯示第一幀前提前顯示在界面上。 
1、先爲主界面單獨寫一個主題style,設置一張待顯示的圖片,這裏我設置了一個顏色,然後在manifest中設置給MainActivity:

<code class="hljs xml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"AppTheme.Launcher"</span>></span><span class="css" style="box-sizing: border-box;">
    <<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">item</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">name</span>="<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">android</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:windowBackground"</span>><span class="hljs-at_rule" style="box-sizing: border-box;">@<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">drawable/bule</item></span>
</span></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span>></span>
//...
        <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">activity
</span>            <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">".MainActivity"</span>
            <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:label</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@string/app_name"</span>
            <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:theme</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@style/AppTheme.Launcher"</span>></span>
            <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">intent-filter</span>></span>
                <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">action</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"android.intent.action.MAIN"</span> /></span>
                <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">category</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"android.intent.category.LAUNCHER"</span> /></span>
            <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">intent-filter</span>></span>
        <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">activity</span>></span>
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul>

2、然後在MainActivity中加載佈局前把AppTheme重新設置給MainActivity:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {

        setTheme(R.style.AppTheme);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
}        </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

這樣在啓動時會先顯示background,然後待界面繪製完成再顯示主界面 
這裏寫圖片描述

發佈了21 篇原創文章 · 獲贊 15 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章