App啟動(dòng)優(yōu)化-Android性能優(yōu)化
通常用戶(hù)期望app響應(yīng)和加載速度越快越好。一個(gè)啟動(dòng)速度慢的app很可能會(huì)給用戶(hù)留下不好的印象,除了導(dǎo)致用戶(hù)在應(yīng)用市場(chǎng)上的打分低之外,很有可能導(dǎo)致致用戶(hù)直接卸載。
這篇文章提供了優(yōu)化app啟動(dòng)時(shí)間的方法。先解釋了app進(jìn)程啟動(dòng)的內(nèi)部流程。然后討論如何優(yōu)化啟動(dòng)的性能。最后列出幾個(gè)常見(jiàn)的啟動(dòng)問(wèn)題和解決方案。
一 啟動(dòng)內(nèi)幕App啟動(dòng)可能發(fā)生在以下三種狀態(tài) 之一,每一種都會(huì)影響到展現(xiàn)給用戶(hù)的時(shí)間:冷啟動(dòng)、熱啟動(dòng)和溫啟動(dòng)(翻譯的有點(diǎn)怪,介于冷和熱之間吧)。
冷啟動(dòng)下,app所做的事情不較多,其它兩種情況,系統(tǒng)只需要將app從后臺(tái)切到前臺(tái)。建議你在冷啟動(dòng)的基礎(chǔ)上做優(yōu)化,這樣也會(huì)提升熱啟動(dòng)和溫啟動(dòng)的性能。
為了更好地優(yōu)化app的啟動(dòng),了解系統(tǒng)和app層做了什么以及如何相互影響很有必要。
1.1 冷啟動(dòng)冷啟動(dòng)指:在app啟動(dòng)之前,系統(tǒng)的進(jìn)程還沒(méi)有,直到app啟動(dòng)創(chuàng)建app的進(jìn)程。冷啟動(dòng)會(huì)發(fā)生在device重啟或者app被殺死的情況下。這種啟動(dòng)在優(yōu)化啟動(dòng)時(shí)間上,有更大的挑戰(zhàn),因?yàn)橄啾绕渌鼉煞N啟動(dòng)方式,系統(tǒng)和app有更多的工作需要處理。
冷啟動(dòng)之前,系統(tǒng)會(huì)執(zhí)行以下三個(gè)task:
1、加載并啟動(dòng)app
2、在app啟動(dòng)后,立即展示空白的window
3、創(chuàng)建app進(jìn)程
一旦系統(tǒng)創(chuàng)建了app進(jìn)程,那么app進(jìn)程就會(huì)執(zhí)行以下步驟
1、創(chuàng)建app對(duì)象
2、啟動(dòng)main thread
3、創(chuàng)建MainActivity
4、Inflate view
5、布置屏幕
6、進(jìn)行首次繪制
一旦app進(jìn)程完成了第一次繪制,系統(tǒng)進(jìn)程就會(huì)用main activity替換已經(jīng)展示的background window。之后用戶(hù)才可以使用app。
下圖展示了系統(tǒng)和app進(jìn)程互相如何工作的,展示了app啟動(dòng)時(shí)期的幾個(gè)重要部分,在創(chuàng)建app和main activity之間,我們可以提升性能問(wèn)題
Application的創(chuàng)建
當(dāng)應(yīng)用啟動(dòng)的時(shí)候,空白的window在app第一次完成繪制之前都會(huì)存在。在那之后,系統(tǒng)進(jìn)程才會(huì)替換啟動(dòng)窗口,允許用戶(hù)開(kāi)始和app交互。
如果你復(fù)寫(xiě)了 Application.oncreate() 方法,app啟動(dòng)的時(shí)候,會(huì)調(diào)用該方法。之后,app會(huì)孵化主線(xiàn)程(UI線(xiàn)程),并通過(guò)它來(lái)創(chuàng)建main activity。
從這之后,系統(tǒng)和app級(jí)別的進(jìn)程將會(huì)按照 app lifecycle stages 執(zhí)行。
Activity的創(chuàng)建
在app進(jìn)程創(chuàng)建了Activity之后,Activity將會(huì)執(zhí)行以下操作
1、初始化值
2、調(diào)用構(gòu)造函數(shù)
3、調(diào)用毀掉方法,比如Activity.onCreate()。
通常,onCreate方法會(huì)對(duì)加載時(shí)間有比較大的影響。因?yàn)樗鼘?zhí)行繁重的工作:加載和填充view,并初始化Activity運(yùn)行期間需要用的對(duì)象。
1.2熱啟動(dòng)相對(duì)于冷啟動(dòng),熱啟動(dòng)會(huì)簡(jiǎn)單的多。如果app的所有Activities還存在內(nèi)存中,那么系統(tǒng)需要做的就是將activity切換到前臺(tái)。這樣app會(huì)避免進(jìn)行的對(duì)象初始化,布局填充和渲染。
但是,如果一些內(nèi)存在觸發(fā)內(nèi)存回調(diào)方法的時(shí)候被回收了,比如onTrimMemory(),那么這些對(duì)象就需要重新創(chuàng)建。
熱啟動(dòng)會(huì)和冷啟動(dòng)有相同的行為。系統(tǒng)也會(huì)展示一個(gè)空白的window,知道app完成Activity的渲染。
1.3 溫啟動(dòng)溫啟動(dòng)做的工作介于冷熱啟動(dòng)之間。這里列舉幾種可能被認(rèn)為是溫啟動(dòng)的狀態(tài):
1、用戶(hù)離開(kāi)了app,然后重新啟動(dòng)它。這時(shí)進(jìn)程還在繼續(xù)運(yùn)行,但是Activity被回收了,app需要重新創(chuàng)建activity。
2、系統(tǒng)將你的app回收了,然后用戶(hù)重新啟動(dòng)app。進(jìn)程和Activity都需要重新啟動(dòng),但它們可以從onCreate方法保存的bundle中恢復(fù)。
二優(yōu)化啟動(dòng)性能為了確定啟動(dòng)時(shí)間的性能問(wèn)題,我們需要先確定app啟動(dòng)花費(fèi)了多少時(shí)間。
2.1 Time to initial display從4.4(API 19)開(kāi)始,logcat會(huì)輸出帶有Displayed的log。該值代表從app啟動(dòng)進(jìn)程到完成Activity第一次繪制的時(shí)間。該時(shí)間內(nèi)完成了一下流程:
啟動(dòng)進(jìn)程初始化對(duì)象創(chuàng)建和初始化Activity填充布局第一次繪制app打出的log如下:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
如果你從命令行或者終端跟蹤log的話(huà),可以比較直接的定位到該log。如果在AndroidStudio中,別忘記關(guān)閉filter。
Displayed值并沒(méi)有捕獲所有資源都被加載和展示的總時(shí)間。那些不在layout文件中或者創(chuàng)建app初始化所需要對(duì)象的時(shí)間不包含在內(nèi)。因?yàn)檫@些資源是在一個(gè)內(nèi)部進(jìn)程中加載的,并且不會(huì)阻塞app的初始化展示。
2.2 Time to full display你可以調(diào)用 reportFullyDrawn() )方法去測(cè)量從應(yīng)用啟動(dòng)到所有資源和view層級(jí)都被繪制出來(lái)的時(shí)間。這對(duì)于app執(zhí)行懶加載的情況很有用。在懶加載中,app不會(huì)阻塞window的初始化繪制,但同步進(jìn)行資源加載和view的更新會(huì)阻塞。
由于懶加載,app的初始化展示不會(huì)包含所有的資源。你可以考慮完全加載并展示所有資源和view的時(shí)候作為一個(gè)考量。比如,UI可能完全加載了,包括一些text的繪制,但是由于圖片需要從網(wǎng)絡(luò)獲取,這時(shí)還沒(méi)有展示。
為了處理這種情況,你可以手動(dòng)地調(diào)用reportFullyDrawn方法讓系統(tǒng)知道你的activity已經(jīng)通過(guò)懶加載完成了。但你是用該方法的時(shí)候,logcat展示的時(shí)間就包含從應(yīng)用被創(chuàng)建到reportFullyDrawn方法被調(diào)用的時(shí)間。
定位瓶頸
兩種方式可以幫助你定位問(wèn)題:AndroidStudio中的Method Tracer和內(nèi)嵌tracing代碼的方式。更多可以參考 documentation .
如果無(wú)法使用Method Tracer Tool ,或者覺(jué)得trace的時(shí)機(jī)不夠準(zhǔn)確,那么你可以通過(guò)在app和Activity的onCreate方法中嵌入代碼進(jìn)行追蹤,比如寫(xiě)下追蹤代碼。更多信息,可以參考 Trace 、Systrace
三常見(jiàn)的問(wèn)題3.1 繁重的App初始化當(dāng)你繼承了Application對(duì)象,又在Application對(duì)象進(jìn)行初始化的時(shí)候執(zhí)行繁重的工作或者復(fù)雜的邏輯,那么就可能導(dǎo)致啟動(dòng)的性能問(wèn)題。在啟動(dòng)的時(shí)候花一些時(shí)間去初始化一些子類(lèi)可能完全沒(méi)必要。
在app初始化的時(shí)候,其它的挑戰(zhàn)包括垃圾回收事件,繁重的操作,比如I/O,都有可能會(huì)阻塞進(jìn)程的初始化。對(duì)于Dalvik運(yùn)行環(huán)境來(lái)說(shuō),垃圾回收是一個(gè)需要特別考慮的點(diǎn),Art運(yùn)行環(huán)境會(huì)并發(fā)的執(zhí)行垃圾回收,以便最小化垃圾回收產(chǎn)生的影響。
3.1.1定位為題
使用method tracing或者內(nèi)嵌代碼來(lái)定位這個(gè)問(wèn)題
Method tracing
Running the Method Tracer tool reveals that the callApplicationOnCreate() method eventually calls your com.example.customApplication.onCreate method. If the tool shows that these methods are taking a long time to finish executing, you should explore further to see what work is occurring there.
內(nèi)嵌代碼的方式
可以對(duì)以下代碼進(jìn)行追蹤
1、App的onCreate方法
2、onCreate中初始化的所有全局單例對(duì)象
3、所有I/O,反序列化,或者可能導(dǎo)致性能問(wèn)題的循環(huán)
3.1.2 解決方案
如果是由于不必要的初始化或者硬盤(pán)I/O操作導(dǎo)致的問(wèn)題,解決方案就是懶初始化對(duì)象:只初始化立即需要的。而不是在一開(kāi)始就創(chuàng)建全局的靜態(tài)對(duì)象,可以將它們的初始化放在一個(gè)單例中,當(dāng)app首次訪(fǎng)問(wèn)它們的時(shí)候再初始化對(duì)象。
3.2繁重的Activity初始化Activity的創(chuàng)建有時(shí)會(huì)承擔(dān)大量的復(fù)雜操作。通常這里存在可以?xún)?yōu)化的點(diǎn)。常見(jiàn)的問(wèn)題有:
1、填充大量復(fù)雜的布局
2、硬盤(pán)操作或者網(wǎng)絡(luò)操作阻塞了繪制
3、加載或者編碼bitmap
4、柵欄化VectorDrawable對(duì)象
5、Activity中其它子系統(tǒng)的初始化
3.2.1定位問(wèn)題和定位App啟動(dòng)問(wèn)題類(lèi)似,也是通過(guò)method tracing或者嵌入代碼來(lái)定位。
Method tracing
當(dāng)執(zhí)行Method Tracer tool的時(shí)候,你應(yīng)該關(guān)注繼承于Application的子類(lèi)的構(gòu)造函數(shù)和onCreate方法。
如果該工具表明代碼中花了很長(zhǎng)時(shí)間去執(zhí)行,那么你就應(yīng)該進(jìn)一步查看這里的具體操作。
嵌入代碼的方式
追蹤的部分可能是以下代碼塊(和App初始化一樣)
1、App的onCreate方法
2、啟動(dòng)時(shí)初始化的所有全局單例對(duì)象
3、所有I/O,反序列化,或者可能導(dǎo)致性能問(wèn)題的循環(huán)
3.2.2 解決方案上面可能有很多潛在的問(wèn)題,這列舉兩種通用的問(wèn)題和解決方案:
view的層級(jí)越龐大,app就會(huì)花越多的時(shí)間去填充它
減少多余的或者嵌套的布局
不填充哪些不需要在啟動(dòng)時(shí)就需要展示的view。可通過(guò)ViewStub來(lái)實(shí)現(xiàn),在需要的時(shí)候再填充
在main thread中做資源的初始化也會(huì)減慢啟動(dòng)速度。可以通過(guò)下面來(lái)解決
延遲所有的資源初始化或者放在其它線(xiàn)程中去做
允許app先加載和展示view,那些依賴(lài)于bitmap或者其它資源之后再去更新
三 主題化的啟動(dòng)屏幕我們可以通過(guò)主題化app的啟動(dòng)屏幕來(lái)改善啟動(dòng)體驗(yàn)。這樣整個(gè)app的啟動(dòng)和接下來(lái)的操作會(huì)顯得更加連貫。但這樣只是將Activity的慢啟動(dòng)問(wèn)題隱藏了。
一種常用的方式實(shí)現(xiàn)主題啟動(dòng)屏幕的方式是使用 windowDisablePreview 主題屬性關(guān)閉系統(tǒng)進(jìn)程在app啟動(dòng)時(shí)繪制的初始化空白屏幕。但是,這種方式會(huì)導(dǎo)致更長(zhǎng)時(shí)間。同樣的,這樣也會(huì)迫使用戶(hù)等到Activity啟動(dòng)后才會(huì)得到反饋,會(huì)讓用戶(hù)產(chǎn)生app本身是否有問(wèn)題的困惑。
解決方案建議你不應(yīng)該禁止預(yù)覽窗口,你應(yīng)該遵循 Material Design 標(biāo)準(zhǔn)。使用Activity的windowBackground主題屬性來(lái)為啟動(dòng)的Activity提供一個(gè)簡(jiǎn)單的drawable。
布局文件
<layer-list xmlns:android='http://schemas.android.com/apk/res/android' android:opacity='opaque'> <!-- The background color, preferably the same as your normal theme --> <item android:drawable='@android:color/white'/> <!-- Your product logo - 144dp color version of your app icon --> <item> <bitmap android:src='http://www.baoyu77737.com/bcjs/@drawable/product_logo_144dp' android:gravity='center'/> </item> </layer-list>
Manifest file:
<activity ... android:theme='@style/AppTheme.Launcher' />
然后在代碼中將主題切換回app的主題,最簡(jiǎn)單的方式是在 super.onCreate() 和 setContentView()方法之前 調(diào)用 setTheme(R.style.AppTheme)
public class MyMainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { // Make sure this is before calling super.onCreate setTheme(R.style.Theme_MyApp); super.onCreate(savedInstanceState); // ... }}
來(lái)自:http://www.lightskystreet.com/2016/10/15/android-optimize-start/
相關(guān)文章:
1. asp文件如何打開(kāi)2. 怎樣打開(kāi)XML文件?xml文件如何打開(kāi)?3. ASP.NET MVC實(shí)現(xiàn)登錄后跳轉(zhuǎn)到原界面4. JSP出現(xiàn)中文亂碼問(wèn)題解決方法詳解5. jsp實(shí)現(xiàn)簡(jiǎn)單用戶(hù)7天內(nèi)免登錄6. ASP和PHP文件操作速度的對(duì)比7. Spring依賴(lài)注入的三種方式實(shí)例詳解8. ASP.NET MVC限制同一個(gè)IP地址單位時(shí)間間隔內(nèi)的請(qǐng)求次數(shù)9. ASP基礎(chǔ)入門(mén)第二篇(ASP基礎(chǔ)知識(shí))10. jsp實(shí)現(xiàn)局部刷新頁(yè)面、異步加載頁(yè)面的方法
