APP啟動(dòng)慢怎么辦,Android官方這樣說(shuō)
這篇文章從干貨總量方面不如第一篇,而且一連發(fā)了兩篇類(lèi)似文檔,總感覺(jué)沒(méi)有進(jìn)步與新意。但是后來(lái)琢磨了一下,這篇質(zhì)量也不差,APP啟動(dòng)時(shí)間也是此次項(xiàng)目我新碰到的一個(gè)點(diǎn),估計(jì)也有相當(dāng)多的同學(xué)碰到這個(gè)問(wèn)題。之前并沒(méi)有太在意這個(gè)點(diǎn),網(wǎng)上也已經(jīng)有比較好的文章來(lái)解決這個(gè)問(wèn)題。但是還是跟第一篇的原因類(lèi)似,官方的文章還是從原理上分析的比較好, 不僅授之以魚(yú),還授之以漁 。所以還是想翻過(guò)來(lái)分享給大家,希望多指教,后面會(huì)豐富文章的類(lèi)型,分享更多更好的文章。
啟動(dòng)時(shí)間性能用戶(hù)期望應(yīng)用程序響應(yīng)速度快,加載速度快。 啟動(dòng)時(shí)間較慢的應(yīng)用程序不滿(mǎn)足此預(yù)期,可能會(huì)令用戶(hù)失望。 這種糟糕的體驗(yàn)可能會(huì)導(dǎo)致用戶(hù)在應(yīng)用商店上差評(píng)你的應(yīng)用,甚至完全放棄你的應(yīng)用。
本文檔提供了有助于你優(yōu)化應(yīng)用程序啟動(dòng)時(shí)間的信息。 它首先解釋啟動(dòng)過(guò)程的內(nèi)部。 接下來(lái),它討論如何配置啟動(dòng)性能。 最后,它描述了一些常見(jiàn)的啟動(dòng)時(shí)間問(wèn)題,并提供了一些提示如何解決。
啟動(dòng)內(nèi)部應(yīng)用程序啟動(dòng)可以在三個(gè)狀態(tài)之一發(fā)生,每個(gè)狀態(tài)都會(huì)影響應(yīng)用程序?qū)τ脩?hù)可見(jiàn)所需的時(shí)間:冷啟動(dòng),熱啟動(dòng)和溫啟動(dòng)。 在冷啟動(dòng),你的應(yīng)用程序從頭開(kāi)始。 在其他狀態(tài)下,系統(tǒng)需要將應(yīng)用程序從后臺(tái)運(yùn)行到前臺(tái)。 我們建議你總是基于冷啟動(dòng)的假設(shè)進(jìn)行優(yōu)化。 這樣做可以提高熱啟動(dòng)和溫啟動(dòng)的性能。
為了優(yōu)化你的應(yīng)用程序以便快速啟動(dòng),有必要了解系統(tǒng)和應(yīng)用程序級(jí)別上發(fā)生的情況,以及這些狀態(tài)和在這些狀態(tài)中的交互方式。
冷啟動(dòng)
冷啟動(dòng)指的是應(yīng)用程序從頭開(kāi)始:系統(tǒng)中沒(méi)有你的應(yīng)用程序進(jìn)程,直到此開(kāi)始,才創(chuàng)建了你的應(yīng)用程序進(jìn)程。 在應(yīng)用程序自啟動(dòng)以來(lái)第一次啟動(dòng)或系統(tǒng)終止應(yīng)用程序等情況下會(huì)發(fā)生冷啟動(dòng)。 這種類(lèi)型的啟動(dòng)在最小化啟動(dòng)時(shí)間方面是最大的挑戰(zhàn),因?yàn)橄到y(tǒng)和應(yīng)用程序比其他啟動(dòng)狀態(tài)要做更多的工作。
在冷啟動(dòng)的開(kāi)始,系統(tǒng)有三個(gè)任務(wù)。這些任務(wù)是:
加載和啟動(dòng)應(yīng)用程序。在啟動(dòng)后立即顯示應(yīng)用程序的空白開(kāi)始窗口。創(chuàng)建 應(yīng)用程序進(jìn)程 。一旦系統(tǒng)創(chuàng)建應(yīng)用程序進(jìn)程,應(yīng)用程序進(jìn)程將負(fù)責(zé)下一階段。 這些階段是:
創(chuàng)建應(yīng)用程序?qū)ο髣?chuàng)建主Activity填充視圖放置屏幕執(zhí)行初始繪制一旦應(yīng)用進(jìn)程完成了第一次繪制,系統(tǒng)進(jìn)程就會(huì)替換當(dāng)前顯示的背景窗口,將其替換為主Activity。此時(shí),用戶(hù)可以開(kāi)始使用應(yīng)用程序。
圖1顯示了系統(tǒng)和應(yīng)用程序進(jìn)程如何在彼此之間移交工作。
圖1.冷應(yīng)用程序啟動(dòng)的重要部分的可視化表示。
在創(chuàng)建應(yīng)用程序和創(chuàng)建activity時(shí)可能會(huì)出現(xiàn)性能問(wèn)題。
應(yīng)用程序創(chuàng)建
當(dāng)應(yīng)用程序啟動(dòng)時(shí),空白的開(kāi)始窗口保留在屏幕上,直到系統(tǒng)第一次完成繪制應(yīng)用程序。 此時(shí),系統(tǒng)過(guò)程將切換應(yīng)用程序的開(kāi)始窗口,允許用戶(hù)開(kāi)始與應(yīng)用程序交互。
如果你在自己的應(yīng)用程序中重載了 Application.oncreate( ) ,應(yīng)用程序?qū)⑼ㄟ^(guò)在你的應(yīng)用程序?qū)ο笊险{(diào)用此方法來(lái)啟動(dòng)。 之后,應(yīng)用程序產(chǎn)生主線程,也稱(chēng)為UI線程,并創(chuàng)建你的主activity的任務(wù)。
從此刻開(kāi)始,系統(tǒng)級(jí)和應(yīng)用級(jí)進(jìn)程將根據(jù)應(yīng)用程序生命周期階段進(jìn)行。
Activity創(chuàng)建
應(yīng)用程序進(jìn)程創(chuàng)建你的activity后,該activity將執(zhí)行以下操作:
初始化值。調(diào)用構(gòu)造函數(shù)。調(diào)用適用于activity的當(dāng)前生命周期狀態(tài)的回調(diào)方法,例如 Activity.onCreate( ) 。通常, onCreate( ) 方法對(duì)加載時(shí)間的影響最大,因?yàn)樗宰罡叩拈_(kāi)銷(xiāo)執(zhí)行工作:加載和擴(kuò)充視圖,并初始化活動(dòng)運(yùn)行所需的對(duì)象。
熱啟動(dòng)與冷啟動(dòng)相比,熱啟動(dòng)應(yīng)用程序要簡(jiǎn)單得多,開(kāi)銷(xiāo)更低。 在熱啟動(dòng),所有的系統(tǒng)都是把你的activity推到前臺(tái)。 如果所有應(yīng)用程序的activity仍駐留在內(nèi)存中,那么應(yīng)用程序可以避免重復(fù)對(duì)象初始化,布局膨脹和呈現(xiàn)。
但是,如果某些內(nèi)存已經(jīng)響應(yīng)內(nèi)存調(diào)整事件(例如 onTrimMemory( ) )而被清除,那么將需要根據(jù)熱啟動(dòng)事件重新創(chuàng)建這些對(duì)象。
熱啟動(dòng)顯示與冷啟動(dòng)場(chǎng)景相同的屏幕行為:系統(tǒng)進(jìn)程顯示空白屏幕,直到應(yīng)用程序完成呈現(xiàn)活動(dòng)。
溫啟動(dòng)溫啟動(dòng)包括在冷啟動(dòng)期間發(fā)生的操作的一些子集;同時(shí),它表示比熱啟動(dòng)少的開(kāi)銷(xiāo)。有許多潛在的狀態(tài)可以被認(rèn)為是溫暖的開(kāi)始。例如:
用戶(hù)退出你的應(yīng)用,但隨后重新啟動(dòng)它。該過(guò)程可能已繼續(xù)運(yùn)行,但應(yīng)用程序必須通過(guò)調(diào)用 onCreate( ) 從頭開(kāi)始重新創(chuàng)建活動(dòng)。
系統(tǒng)從內(nèi)存中退出你的應(yīng)用程序,然后用戶(hù)重新啟動(dòng)它。進(jìn)程和Activity需要重新啟動(dòng),但任務(wù)可以從保存的實(shí)例狀態(tài)包傳遞到 onCreate( ) 中受益。
分析啟動(dòng)性能為了正確診斷開(kāi)始時(shí)間性能,你可以跟蹤顯示應(yīng)用程序啟動(dòng)所需時(shí)間的指標(biāo)。
初始顯示時(shí)間
從Android 4.4(API級(jí)別19),logcat包括一個(gè)輸出行,包含一個(gè)名為 Displayed 的值。 此值表示在啟動(dòng)過(guò)程和完成在屏幕上繪制相應(yīng)活動(dòng)之間經(jīng)過(guò)的時(shí)間量。 經(jīng)過(guò)的時(shí)間包括以下事件序列:
啟動(dòng)過(guò)程。初始化對(duì)象。創(chuàng)建并初始化活動(dòng)。膨脹布局。第一次繪制你的應(yīng)用程序。報(bào)告的日志行看起來(lái)類(lèi)似于以下示例:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
如果你正在從命令行或終端中跟蹤logcat輸出,則查找已用時(shí)間很簡(jiǎn)單。 要在Android Studio中查找耗用時(shí)間,必須在logcat視圖中禁用過(guò)濾器。 禁用過(guò)濾器是必要的,因?yàn)橄到y(tǒng)服務(wù)器會(huì)(而不是應(yīng)用程序本身)提供此日志。
完成相應(yīng)設(shè)置后,你可以輕松搜索正確的字詞以查看時(shí)間。圖2顯示了如何禁用過(guò)濾器,并在底部的第二行輸出中顯示了顯示時(shí)間的logcat輸出示例。
圖2.禁用過(guò)濾器,并在logcat中查找顯示的值。
logcat輸出中 Displayed 指標(biāo)不一定捕獲所有資源加載和顯示之前的時(shí)間量:它不包括布局文件中未引用的資源,或者應(yīng)用程序作為對(duì)象初始化的一部分創(chuàng)建的資源。 它排除了這些資源,因?yàn)榧虞d它們是一個(gè)內(nèi)聯(lián)過(guò)程,并且不會(huì)阻止應(yīng)用程序的初始顯示。
完全顯示的時(shí)間
你可以使用 reportFullyDrawn( ) 方法來(lái)測(cè)量應(yīng)用程序啟動(dòng)和完成顯示所有資源和查看層次結(jié)構(gòu)之間的經(jīng)過(guò)時(shí)間。 這在應(yīng)用程序執(zhí)行延遲加載的情況下可能是有價(jià)值的。 在延遲加載中,應(yīng)用程序不會(huì)阻止窗口的初始繪制,而是異步加載資源并更新視圖層次結(jié)構(gòu)。
如果由于延遲加載,應(yīng)用程序的初始顯示不包括所有資源,那么你可能會(huì)將所有資源和視圖的完成加載和顯示視為單獨(dú)的指標(biāo):例如,你的UI可能已完全加載, 但尚未顯示應(yīng)用程序必須從網(wǎng)絡(luò)獲取的圖片。
為了解決這個(gè)問(wèn)題,你可以手動(dòng)調(diào)用 reportFullyDrawn( ) ,讓系統(tǒng)知道你的活動(dòng)已完成延遲加載。 當(dāng)你使用此方法時(shí),logcat顯示的值是自應(yīng)用程序?qū)ο髣?chuàng)建以來(lái)所經(jīng)過(guò)的時(shí)間,以及調(diào)用 reportFullyDrawn( ) 的時(shí)刻。
如果你發(fā)現(xiàn)你的顯示時(shí)間比你想要的慢,你可以繼續(xù)嘗試找出啟動(dòng)過(guò)程中的瓶頸
找到瓶頸
找到瓶頸的兩個(gè)好方法是Android Studio的Method Tracer工具和內(nèi)聯(lián)跟蹤。要了解Method Tracer,請(qǐng)參閱該工具的 documentation 。
如果你無(wú)法訪問(wèn)Method Tracer工具,或無(wú)法在正確的時(shí)間啟動(dòng)該工具以獲取日志信息,則可以通過(guò)在應(yīng)用程序和活動(dòng)的 onCreate( ) 方法中內(nèi)聯(lián)跟蹤獲得類(lèi)似的洞察。 要了解內(nèi)聯(lián)跟蹤,請(qǐng)參閱 Trace 函數(shù)的參考文檔以及 Systrace 工具。
常見(jiàn)問(wèn)題本節(jié)討論了經(jīng)常影響應(yīng)用程序啟動(dòng)性能的幾個(gè)問(wèn)題。這些問(wèn)題主要涉及初始化應(yīng)用程序和activity對(duì)象,以及加載屏幕。
繁重的應(yīng)用程序初始化
當(dāng)代碼覆蓋 Application 對(duì)象時(shí),啟動(dòng)性能可能會(huì)受到影響,并且在初始化該對(duì)象時(shí)執(zhí)行繁重的工作或復(fù)雜的邏輯。 如果你的應(yīng)用程序子類(lèi)執(zhí)行不需要完成的初始化,你的應(yīng)用程序可能會(huì)在啟動(dòng)期間浪費(fèi)時(shí)間。 一些初始化可能是完全不必要的:例如,當(dāng)應(yīng)用實(shí)際上響應(yīng)于intent而啟動(dòng)時(shí),初始化主activity的狀態(tài)信息。 根據(jù)intent,應(yīng)用程序僅使用之前初始化的狀態(tài)數(shù)據(jù)的子集。
應(yīng)用程序初始化期間的其他挑戰(zhàn)包括影響大或數(shù)量眾多的垃圾收集事件,或磁盤(pán)I / O與初始化同時(shí)發(fā)生,進(jìn)一步阻止初始化過(guò)程。 垃圾收集是Dalvik運(yùn)行時(shí)特別考慮的問(wèn)題; Art運(yùn)行時(shí)同時(shí)執(zhí)行垃圾回收,最小化操作的影響。
診斷問(wèn)題
你可以使用方法跟蹤或內(nèi)聯(lián)跟蹤來(lái)嘗試診斷問(wèn)題。
方法跟蹤
運(yùn)行Method Tracer工具會(huì)顯示 callApplicationOnCreate( ) 方法最終調(diào)用 com.example.customApplication.onCreate 方法。 如果工具顯示這些方法需要很長(zhǎng)時(shí)間才能完成執(zhí)行,那么您應(yīng)該進(jìn)一步探索以了解正在發(fā)生的工作。
內(nèi)聯(lián)跟蹤
使用內(nèi)聯(lián)跟蹤來(lái)調(diào)查可能的罪魁禍?zhǔn)祝ǎ?/p>你應(yīng)用程序的初始 onCreate( ) 函數(shù)。你的應(yīng)用程序初始化的任何全局單例對(duì)象。任何磁盤(pán)I / O,反序列化或在瓶頸期間可能發(fā)生的緊密循環(huán)。
解決問(wèn)題
無(wú)論問(wèn)題是不必要的初始化還是磁盤(pán)I / O,解決方案都需要延遲初始化對(duì)象:只初始化那些立即需要的對(duì)象。 例如,而不是創(chuàng)建全局靜態(tài)對(duì)象,而是移動(dòng)到單例模式,應(yīng)用程序只在首次訪問(wèn)它們時(shí)才會(huì)初始化對(duì)象。
繁重的Activity初始化
Activity創(chuàng)建通常需要很多高開(kāi)銷(xiāo)的工作。通常,有機(jī)會(huì)優(yōu)化這項(xiàng)工作以實(shí)現(xiàn)性能提高。這些常見(jiàn)問(wèn)題包括:
填充大的或復(fù)雜的布局。阻止在磁盤(pán)或網(wǎng)絡(luò)I / O上繪制屏幕。加載和解碼位圖。柵格化 VectorDrawable 對(duì)象。初始化activity的其他子系統(tǒng)。診斷問(wèn)題
在這種情況下,同樣,方法跟蹤和內(nèi)聯(lián)跟蹤都可以證明是有用的。
方法跟蹤
當(dāng)運(yùn)行Method Tracer工具時(shí),特定區(qū)域?qū)?zhuān)注于你的應(yīng)用程序的 Application 子類(lèi)構(gòu)造函數(shù)和 com.example.customApplication.onCreate() 方法。
如果工具顯示這些方法需要很長(zhǎng)時(shí)間才能完成執(zhí)行,那么你應(yīng)該進(jìn)一步探索以了解正在發(fā)生的工作。
內(nèi)聯(lián)跟蹤
使用內(nèi)聯(lián)跟蹤來(lái)調(diào)查可能的罪魁禍?zhǔn)祝ǎ?/p>你應(yīng)用程式的初始 onCreate( ) 函數(shù)。它初始化的任何全局單例對(duì)象。任何磁盤(pán)I / O,反序列化或在瓶頸期間可能發(fā)生的緊密循環(huán)。
解決問(wèn)題
有許多潛在的瓶頸,但兩個(gè)常見(jiàn)的問(wèn)題和補(bǔ)救措施如下:
你的視圖層次結(jié)構(gòu)越大,應(yīng)用程序填充的時(shí)間就越長(zhǎng)。 你可以采取兩個(gè)步驟來(lái)解決此問(wèn)題:
通過(guò)減少冗余或嵌套布局來(lái)展開(kāi)視圖層次結(jié)構(gòu)。
不要對(duì)UI的不需要在啟動(dòng)過(guò)程中可見(jiàn)的部分進(jìn)行填充。相反,使用 ViewStub 對(duì)象作為子層次結(jié)構(gòu)的占位符,應(yīng)用程序可以在更適當(dāng)?shù)臅r(shí)間填充。將所有的資源初始化在主線程上也可以減慢啟動(dòng)速度。 你可以解決這個(gè)問(wèn)題,如下所示:
移動(dòng)所有資源初始化,以便應(yīng)用程序可以在不同的線程上懶惰地執(zhí)行它。允許應(yīng)用加載和顯示你的視圖,然后更新依賴(lài)于位圖和其他資源的可視屬性。主題化啟動(dòng)屏幕
你可能希望為應(yīng)用程序的加載體驗(yàn)設(shè)計(jì)主題,以便應(yīng)用程序的啟動(dòng)屏幕與應(yīng)用程序的其他部分保持一致,而不是與系統(tǒng)主題一致。這樣做可以隱藏緩慢的活動(dòng)啟動(dòng)。
實(shí)現(xiàn)主題啟動(dòng)屏幕的常見(jiàn)方法是使用 windowDisablePreview 屬性關(guān)閉系統(tǒng)進(jìn)程在啟動(dòng)應(yīng)用程序時(shí)繪制的初始空白屏幕。 但是,此方法可能會(huì)導(dǎo)致啟動(dòng)時(shí)間比不抑制預(yù)覽窗口的應(yīng)用程序更長(zhǎng)。 此外,它強(qiáng)制用戶(hù)在activity啟動(dòng)時(shí)等待而不反饋,會(huì)使他們懷疑應(yīng)用程序是否正常工作。
診斷問(wèn)題
你可以通過(guò)觀察用戶(hù)啟動(dòng)應(yīng)用時(shí)的慢響應(yīng)來(lái)診斷此問(wèn)題。在這種情況下,屏幕可能看起來(lái)已凍結(jié),或已停止響應(yīng)輸入。
解決問(wèn)題
我們建議你不要禁用預(yù)覽窗口,而是遵循常見(jiàn)的 Material Design 。你可以使用activity的 windowBackground 主題屬性為起始activity提供一個(gè)簡(jiǎn)單的自定義drawable。
例如,你可以創(chuàng)建一個(gè)新的可繪制文件,并從布局XML和應(yīng)用程序清單文件中引用它,如下所示:
Layout XML 文件:
<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.lshqa.cn/bcjs/@drawable/product_logo_144dp' android:gravity='center'/> </item></layer-list>
Manifest 文件:
<activity ...android:theme='@style/AppTheme.Launcher' />
轉(zhuǎn)換回正常主題的最簡(jiǎn)單方法是在調(diào)用 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.jianshu.com/p/42aea2335019
