Android 啟動優(yōu)化

隨著項(xiàng)目版本的迭代占婉,App的性能問題會逐漸暴露出來泡嘴,啟動慢,啟動白屏黑屏逆济,包越來越大酌予,App耗電量高,而好的用戶體驗(yàn)與性能表現(xiàn)緊密相關(guān)奖慌。今天就來分析關(guān)于Android啟動優(yōu)化要怎么做抛虫?

為什么出現(xiàn)白屏或者黑屏

冷啟動白屏持續(xù)時間可能會很長瘟斜,這是個很不好的體驗(yàn)谣殊,一般由以下原因造成:
1、Application的onCreate流程荒叼,對于大型的APP來說涎劈,通常會在這里做大量的通用組件的初始化操作;
建議:很多第三方SDK都放在Application初始化阅茶,我們可以放到用到的地方才進(jìn)行初始化操作蛛枚。
2、Activity的onCreate流程脸哀,特別是UI的布局與渲染操作蹦浦,如果布局過于復(fù)雜很可能導(dǎo)致嚴(yán)重的啟動性能問題;
優(yōu)化APP啟動速度意義重大撞蜂,啟動時間過長盲镶,可能會使用戶直接卸載APP侥袜。

白屏或黑屏,具體是哪一個溉贿,取決于app的Theme使用的是dark還是light主題

一枫吧、啟動的三種方式

App啟動方式 冷啟動 熱啟動 溫啟動

冷啟動(Cold start)

冷啟動是指APP在手機(jī)啟動后第一次運(yùn)行,或者APP進(jìn)程被kill掉后在再次啟動宇色。
可見冷啟動的必要條件是該APP進(jìn)程不存在九杂,這就意味著系統(tǒng)需要創(chuàng)建進(jìn)程,APP需要初始化宣蠕。在這三種啟動方式中例隆,冷啟動耗時最長,對于冷啟動的優(yōu)化也是最具挑戰(zhàn)的抢蚀。因此本文重點(diǎn)談?wù)摰氖菍鋯酉嚓P(guān)的優(yōu)化镀层。

溫啟動(Warm start)

App進(jìn)程存在,當(dāng)時Activity可能因?yàn)閮?nèi)存不足被回收皿曲。這時候啟動App不需要重新創(chuàng)建進(jìn)程唱逢,但是Activity的onCrate還是需要重新執(zhí)行的。場景類似打開淘寶逛了一圈然后切到微信去聊天去了谷饿,過了半小時再次回到淘寶惶我。這時候淘寶的進(jìn)程存在,但是Activity可能被回收博投,這時候只需要重新加載Activity即可绸贡。

熱啟動(Hot start)

App進(jìn)程存在,并且Activity對象仍然存在內(nèi)存中沒有被回收毅哗√拢可以重復(fù)避免對象初始化,布局解析繪制虑绵。
場景就類似你打開微信聊了一會天這時候出去看了下日歷 在打開微信 微信這時候啟動就屬于熱啟動尿瞭。

二、啟動優(yōu)化時間統(tǒng)計

3.1adb命令 : adb shell am start -S -W 包名/啟動類的全限定名 翅睛, -S 表示重啟當(dāng)前應(yīng)用

E:\code\kfz-android>adb shell am start -S -W com.xxx.app/com.xxx.app.xxx.SplashActivity
Stopping: com.xxx.app
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.app/.business.SplashActivity }
Status: ok
Activity: com.xxx.app/.xxx.SplashActivity
ThisTime: 819
TotalTime: 819
WaitTime: 834
Complete

ThisTime : 最后一個 Activity 的啟動耗時(例如從 LaunchActivity - >MainActivity「adb命令輸入的Activity」 , 只統(tǒng)計 MainActivity 的啟動耗時)
TotalTime : 啟動一連串的 Activity 總耗時.(有幾個Activity 就統(tǒng)計幾個)
WaitTime : 應(yīng)用進(jìn)程的創(chuàng)建過程 + TotalTime .

3.2可以通過在代碼中增加log來計算啟動時間

long startTime = System.currentTimeMillis();    //獲取開始時間
doSomething();    //測試的代碼段
long endTime = System.currentTimeMillis();    //獲取結(jié)束時間
System.out.println("程序運(yùn)行時間:" + (endTime - startTime) + "ms");    //輸出程序運(yùn)行時

3.3 使用systrace

可能在源代碼經(jīng)成椋看到這種Debug.startMethodTracing(file.getAbsolutePath());統(tǒng)計的代碼,是官方提供捕发,分析trace文件

  //把分析結(jié)果存在一個文件
File file = new File(Environment.getExternalStorageDirectory(), "kongfz.trace");
Log.e(TAG, "onCreate: " + file.getAbsolutePath());
Debug.startMethodTracing(file.getAbsolutePath());
需要統(tǒng)計的方法
Debug.stopMethodTracing();
獲取到的trace文件
獲取到的trace文件

可以看到具體哪個方法特別耗時疏旨。

3.4 使用profiler來查看方法耗時

profiler面板截圖

關(guān)于3、4方案查看trace文件可以通過Hdmss或者直接通過AndroidStudio分析扎酷。

3.5 使用依賴庫 例如AOP 無侵入埋點(diǎn)插樁方式進(jìn)行時間統(tǒng)計檐涝,或者APT注解編譯統(tǒng)計,這個暫不舉例,可以自行百度使用方法谁榜。

3.6根據(jù)系統(tǒng)日志來統(tǒng)計啟動耗時幅聘,在Android Studio中查找已用時間,必須在logcat視圖中禁用過濾器(No Filters)窃植。因?yàn)檫@個是系統(tǒng)的日志輸出帝蒿,而不是應(yīng)用程序的。

比如我們可以通過過濾displayed輸出的啟動日志. 示例如下:


系統(tǒng)過濾

三撕瞧、原因分析

1.高耗時任務(wù)
數(shù)據(jù)庫初始化陵叽、某些第三方框架初始化、大文件讀取丛版、MultiDex加載等巩掺,導(dǎo)致CPU阻塞
2.復(fù)雜的View層級
使用的嵌套Layout過多,層級加深页畦,導(dǎo)致View在渲染過程中胖替,遞歸加深,占用CPU資源豫缨,影響Measure独令、Layout等方法的速度
3.類過于復(fù)雜
Java對象的創(chuàng)建也是需要一定時間的,如果一個類中結(jié)構(gòu)特別復(fù)雜好芭,new一個對象將消耗較高的資源燃箭,特別是一些單例的初始化,需要特別注意其中的結(jié)構(gòu)
4.主題及Activity配置
有一些App是帶有Splash頁的舍败,有的則直接進(jìn)入主界面招狸,由于主題切換,可能會導(dǎo)致白屏邻薯,或者點(diǎn)了Icon裙戏,過一會兒才出現(xiàn)主界面

四、優(yōu)化方案

冷啟動優(yōu)化分為可控階段和不可控階段

不可控階段

點(diǎn)擊app以后到初始化Application之間這段時間厕诡,系統(tǒng)接管累榜,從Zygote進(jìn)程中fork創(chuàng)建新進(jìn)程,GC回收等等一系列操作灵嫌,和我們app無關(guān)

可控階段

作為普通應(yīng)用壹罚,App進(jìn)程的創(chuàng)建等環(huán)節(jié)我們是無法主動控制的,可以優(yōu)化的也就是Application寿羞、Activity創(chuàng)建以及回調(diào)等過程猖凛。
初始化Application開始,如下圖:


啟動流程

從上圖可以看到稠曼,整個冷啟動流程中至少有兩處onCreate形病,分別是Application和Activity,整個流程都是可控的霞幅。所以漠吻,onCreate方法做的事情越多,冷啟動消耗的時間越長司恳。

關(guān)于啟動加速方案,Google給出的建議是:

1.利用提前展示出來的Window途乃,快速展示出來一個界面,給用戶快速反饋的體驗(yàn)扔傅;
2.避免在啟動時做密集沉重的初始化(Heavy app initialization)耍共;
3.定位問題:避免I/O操作、反序列化猎塞、網(wǎng)絡(luò)操作试读、布局嵌套等。

官方文檔給出的建議中,方向1屬于治標(biāo)不治本荠耽,只是表面上快钩骇;方向2、3可以真實(shí)的加快啟動速度铝量。接下來我們就在項(xiàng)目中實(shí)際應(yīng)用一下倘屹。

4.1透明主題優(yōu)化

為了解決啟動窗口白屏問題,許多開發(fā)者使用透明主題來解決這個問題慢叨,但是治標(biāo)不治本纽匙。
會出現(xiàn)點(diǎn)擊圖標(biāo)幾秒鐘沒有反應(yīng)情況,雖然解決了上面這個問題拍谐,但是仍然有些不足烛缔。

<!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>

4.2設(shè)置閃屏圖片主題

為了更順滑無縫銜接我們的閃屏頁,可以在啟動 Activity 的 Theme中設(shè)置閃屏頁圖片赠尾,這樣啟動窗口的圖片就會是閃屏頁圖片力穗,而不是白屏。
其實(shí)只是良好體驗(yàn)气嫁,沒有做到真正的優(yōu)化当窗。大部分App都采用這種方式。

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@drawable/lunch</item>  //閃屏頁圖片
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">false</item><!--顯示虛擬按鍵寸宵,并騰出空間-->
    </style>

4.3 優(yōu)化Application

從用戶點(diǎn)擊launcher圖標(biāo)到看到界面第一幀為應(yīng)用啟動過程崖面,主要會經(jīng)過以下這些方法:

main()->Application:attachBaseContext()->onCreate()->Activity:onCreate()->onStart()->onResume()

main->Activity創(chuàng)建的這個過程會經(jīng)過一系列framework層的操作,對于系統(tǒng)自動執(zhí)行的操作我們不易進(jìn)行優(yōu)化梯影,但是巫员,如果我們繼承Application自定義了自己的Application,可以做如下優(yōu)化:

1.盡量不將一些業(yè)務(wù)邏輯放于Application中甲棍;
2.不以靜態(tài)變量的方式在Application中保存應(yīng)用數(shù)據(jù)简识;
3.不要把文件、數(shù)據(jù)庫的操作放在Application
我們知道有很多第三方組件(包括App應(yīng)用本身)都在 Application 中完成初始化操作。但是在 Application 中完成繁重的初始化操作和復(fù)雜的邏輯就會影響到應(yīng)用的啟動性能七扰。

分析后發(fā)現(xiàn)影響冷啟動時間的常見問題如下:

復(fù)雜繁瑣的布局初始化
阻塞主線程 UI 繪制的操作奢赂,如 I/O 讀寫或者是網(wǎng)絡(luò)訪問.
其它占用主線程的操作

我們可以根據(jù)這些組件的輕重緩急之分,對初始化做一下分類 :

必要的組件一定要在主線程中立即初始化(入口 Activity 可能立即會用到)
組件一定要在主線程中初始化颈走,但是可以延遲初始化膳灶。
組件可以在子線程中初始化。

在進(jìn)行優(yōu)化的時候立由,需要注意以下幾種情況:

放在子線程的組件初始化建議延遲初始化轧钓,這樣就可以了解是否會對項(xiàng)目造成影響!
將需要在主線程中初始化但是可以不用立即完成的動作延遲加載(初始化放在 Application 中統(tǒng)一管理為妙锐膜,不建議放在Activity里面)

  handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 設(shè)置數(shù)據(jù)庫的上下文
                DBOpenHelper.setContext(context);
                // 初始化GreenDao
                initGreenDao();
                // 加載分類數(shù)據(jù)
                loadClassifyData();

                //百度地圖初始化毕箍,百度地圖需要在Application初始化
                GamActivityMap.init(Kongfz.this);
            }
        }, 3000);

可以嘗試將常見的組件庫,例如 Bugly道盏,x5內(nèi)核初始化霉晕,SP的讀寫,友盟等組件放到子線程中初始化捞奕。(子線程初始化不能影響到組件的使用)

 new Thread() {
            @Override
            public void run() {
                super.run();
                //設(shè)置線程的優(yōu)先級牺堰,不與主線程搶資源
                setPriority(1);
                //子線程初始化第三方組件
                try {
                    Thread.sleep(5000);//建議延遲初始化,可以發(fā)現(xiàn)是否影響其它功能颅围,或者是崩潰伟葫!
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //配置umeng、百度統(tǒng)計信息
                configAnalytic();
                //ali 實(shí)人認(rèn)證sdk初始化
                RPSDK.initialize(getContext());
                //手機(jī)內(nèi)存初始化
                Util.getMemoryLevel(getContext());
                //第三方分享
                ShareUtil.shareConfig(getContext());
                //聽云
                TingYuntils.init(Kongfz.this);


                LogUtil.e("app", "onCreate Application");
                LogUtil.e("app", "width=" + Util.getScreenWidth(getContext()) + ";height=" + Util.getScreenHeight(getContext()) + ";statusBarHeight=" + Util.getStatusBarHeight(getContext()) + ";dpi=" + Util.getDensityDpi(getContext()));

                //推送初始化
                Config.setMiniTest();

                //推動初始化
                PushMainUtils.initMergePushSDk(true, Kongfz.this);
            }
        }.start();

在優(yōu)化好啟動時間后院促,我們就可以在針對閃屏頁的時間筏养,進(jìn)行調(diào)整優(yōu)化,具體公式為:

閃屏頁展示總時間 = 組件初始化時間 + 剩余展示時間

4.4 優(yōu)化啟動頁Activity

啟動頁盡量不要網(wǎng)絡(luò)請求等耗時操作常拓。如果使用了請求網(wǎng)絡(luò)等操作在適當(dāng)?shù)臅r候應(yīng)該及時取消的耗時操作渐溶。例如,某些時候弄抬,當(dāng)用戶點(diǎn)擊了launcher圖標(biāo)茎辐,但馬上又想退出點(diǎn)擊了返回鍵,過了幾秒鐘用戶在使用其他APP掂恕,突然跳轉(zhuǎn)到我們的APP那就用戶體驗(yàn)感很不好了拖陆。所以可以在返回事件中取消掉耗時操作。
減少歡迎頁復(fù)雜邏輯懊亡,復(fù)雜布局依啰,onCreate()中過多邏輯。

4.5 開啟服務(wù)做處理

可以在后臺處理的店枣,放在后臺處理速警,而不用立馬處理叹誉。

4.6 MultiDex.install(this);優(yōu)化 當(dāng)項(xiàng)目方法超過65535,需要分包加載闷旧,Android5.0默認(rèn)實(shí)現(xiàn)桂对,Android5.0沒有實(shí)現(xiàn),可能出現(xiàn)崩潰鸠匀,或者第一次加載緩慢的情況。具體可以參考下面文章逾柿, 有詳細(xì)原理分析缀棍,以及解決

https://juejin.im/post/5d95f4a4f265da5b8f10714b

五、啟動頁適配

啟動頁面適配對于Android手機(jī)來說是一個很麻煩的問題机错,劉海屏爬范、底部虛擬導(dǎo)航欄、各種尺寸的手機(jī)屏幕弱匪,導(dǎo)致啟動頁被拉伸變形等出現(xiàn)青瀑,那么怎么進(jìn)行啟動頁適配呢?下面說兩種比較通用的適配方案:
5.1 切目前主流手機(jī)尺寸放入不同資源文件夾中進(jìn)行適配:


資源示例

此種方式問題:
1.適配不能完全適配只能適配主流的機(jī)型萧诫。
2.文件資源過多斥难,導(dǎo)致包變大。
5.2 切部分圖進(jìn)行適配帘饶。

六哑诊、優(yōu)化效果

OPPO優(yōu)化前:

E:\code\kfz-android>adb shell am start -S -W com.xxx.app/com.xxx.app.xxx.SplashActivity
Stopping: com.xxx.app
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.app/.xxx.SplashActivity }
Status: ok
Activity: com.xxx.app/.xxx.SplashActivity
ThisTime: 1723
TotalTime: 1723
WaitTime: 1761
Complete

OPPO優(yōu)化后:

E:\code\kfz-android>adb shell am start -S -W com.xxx.app/com.xxx.app.xxx.SplashActivity
Stopping: com.xxx.app
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.app/.xxx.SplashActivity }
Status: ok
Activity: com.xxx.app/.xxx.SplashActivity
ThisTime: 717
TotalTime: 717
WaitTime: 733
Complete

華為優(yōu)化前:

E:\code\KongfzCode2\kfz-android>adb shell am start -S -W com.xxx.app/com.xxx.app.xxx.SplashActivity
Stopping: com.xxx.app
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.app/.xxx.SplashActivity }
Status: ok
Activity: com.xxx.app/.business.SplashActivity
ThisTime: 2613
TotalTime: 2613
WaitTime: 2644
Complete

華為優(yōu)化后:

E:\code\kfz-android>adb shell am start -S -W com.xxx.app/com.xxx.app.xxx.SplashActivity
Stopping: com.kongfz.app
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.app/.xxx.SplashActivity }
Status: ok
Activity: com.xxx.app/.xxx.SplashActivity
ThisTime: 1632
TotalTime: 1632
WaitTime: 1660
Complete

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市及刻,隨后出現(xiàn)的幾起案子镀裤,更是在濱河造成了極大的恐慌,老刑警劉巖缴饭,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暑劝,死亡現(xiàn)場離奇詭異,居然都是意外死亡颗搂,警方通過查閱死者的電腦和手機(jī)担猛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丢氢,“玉大人毁习,你說我怎么就攤上這事÷敉瑁” “怎么了纺且?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稍浆。 經(jīng)常有香客問我载碌,道長猜嘱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任嫁艇,我火速辦了婚禮朗伶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘步咪。我一直安慰自己论皆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布猾漫。 她就那樣靜靜地躺著点晴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悯周。 梳的紋絲不亂的頭發(fā)上粒督,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機(jī)與錄音禽翼,去河邊找鬼屠橄。 笑死,一個胖子當(dāng)著我的面吹牛闰挡,可吹牛的內(nèi)容都是我干的锐墙。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼长酗,長吁一口氣:“原來是場噩夢啊……” “哼贮匕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起花枫,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤刻盐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后劳翰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敦锌,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年佳簸,在試婚紗的時候發(fā)現(xiàn)自己被綠了乙墙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡生均,死狀恐怖听想,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情马胧,我是刑警寧澤汉买,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站佩脊,受9級特大地震影響蛙粘,放射性物質(zhì)發(fā)生泄漏垫卤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一出牧、第九天 我趴在偏房一處隱蔽的房頂上張望穴肘。 院中可真熱鬧,春花似錦舔痕、人聲如沸评抚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慨代。三九已至,卻和暖如春边翼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸣剪。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工组底, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筐骇。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓债鸡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铛纬。 傳聞我的和親對象是個殘疾皇子厌均,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容