淺談App的啟動最佳化

xuexiangjys發表於2022-11-22

1. 應用啟動的方式

在Android中,應用啟動一般可分為三種:冷啟動溫啟動熱啟動

那麼什麼是冷啟動溫啟動熱啟動呢?下面我們來簡單看一下它們的定義:

  • 冷啟動:當啟動應用時,後臺沒有該應用的程式。這時系統會又一次建立一個新的程式分配給該應用,這個啟動方式就是冷啟動。
  • 溫啟動:當啟動應用時,後臺已有該應用的程式,但是Activity可能因為記憶體不足被回收。這樣系統會從已有的程式中來啟動這個Activity,這個啟動方式叫溫啟動。
  • 熱啟動:當啟動應用時,後臺已有該應用的程式,且Activity仍然存在記憶體中沒有被回收。這樣系統直接把這個Activity拉到前臺即可,這個啟動方式叫熱啟動。

由於冷啟動相對於其他啟動方式多了程式的建立(Zygote程式fork建立程式)以及應用的資源載入和初始化(Application的建立及初始化),所以相對來說會比較耗時,所以我們一般說的App啟動最佳化一般指的都是App的冷啟動最佳化。

2. 最佳化方案

App啟動最佳化的本質就是:啟動速度和體驗的最佳化

這就好比早些年你去飯店吃飯,你想要點餐但等了半天都沒有服務人員過來,可能就等得不耐煩直接走開了。同樣的,對於APP來說,如果使用者點選App後長時間都打不開,使用者就很可能失去耐心而解除安裝應用。

所以啟動速度是使用者對我們App的第一體驗。如果啟動速度過慢,使用者第一印象就會很差,這樣即使你功能做出花來,使用者也不會願意去使用。

其實要想最佳化App的啟動體驗,關鍵就是要讓使用者更快地獲取到應用的內容(流暢,不卡頓、不等待),那麼我們應該怎麼做呢?

2.1 案例分析

這裡我們還是先以之前說的去飯店吃飯為例來展開討論。現在的飯店競爭是尤為得激烈,為了能夠提高顧客的體驗、留住顧客,真的是使出了渾身解數,除了口味之外,服務品質也是被擺在了越來越重要的位置。

就比方說:

  • 為了能夠更快地給使用者提供點餐服務,現在的飯店每個座子上基本都貼上了點餐的二維碼。
  • 一些熱門需要排隊的餐館,在前臺張貼了二維碼,提供排隊取號和提前點餐服務。
  • 一些常年爆滿需要排隊的餐館,還提供了零食、茶水、棋牌以及美甲等服務。
  • 有些餐廳為了提高上菜的速度和體驗,設定了倒數計時免單的服務。

以上的改進措施,都是這些年餐飲行業在競爭下,不斷提高服務質量的產物。可以說誰的服務質量落下了,誰就有可能被淘汰。

其實我們仔細分析一下上面所列舉的,不難看出有很多是可以讓我們借鑑的。

(1)案例1

分析:飯店提供了點餐的二維碼,本質上是將一件被動等待轉變為主動請求的一種過程,客觀上減少了等待的時間,從而提高了速度。

類比:這對應我們的應用程式,就像原先一些耗時不必要的三方庫需要被動等待其初始化完畢程式才會繼續進行,轉變為先不初始化這部分耗時的三方庫,等真正用到時再進行初始化;又類似我們應用程式的遊客模式,無需被迫進行一堆複雜的使用者註冊過程,就可以直接進入程式使用,待涉及一些使用者資訊功能的時候再提示使用者註冊。

(2)案例2

分析:熱門餐館同時提供排隊取號和提前點餐服務,本質上是將原先無法同時進行的操作(需要先排上隊等到座位,才能掃描座位號點餐)變成了可同時進行的操作,將序列任務轉化為並行任務,從而節省了時間。

類比:這對應我們的應用程式,就是將一些原本在主執行緒序列執行的耗時資源/資料載入,改為在子執行緒中併發執行。這在幾個耗時任務耗時差距不大的時候最佳化尤為明顯。

(3)案例3

分析:在顧客排隊等待的時候,提供零食、茶水、棋牌以及美甲等服務,本質上就是讓顧客提前享受本餐館的服務,從而緩解顧客等待的焦慮。

類比:這對應我們的應用程式,就是開屏啟動頁。在Android12上,Google強制增加了這個開屏頁,就是為了讓使用者提前看到你的應用頁面,讓使用者產生應用啟動很流暢的假象,從而提高使用者的啟動體驗。

(4)案例4

分析:設定倒數計時免單服務,本質上就是給等待加上了進度條上限以及超時賠償。俗話說最讓人害怕的是等待,比等待更讓人害怕的是看不見盡頭的等待。而為等待設定上限,可以極大地緩解顧客等待的焦慮,畢竟等待超時了也是會有補償的。

類比:這對應我們的應用程式,就是一些應用(比如遊戲)初次啟動會非常耗時,所以它們通常會在啟動頁增加一個初始化/載入進度條頁面,來告訴使用者啥時候能載入完,而不是無止境未知的等待。

透過分析,我們可以看到(1)、(2)兩種操作是從技術的角度來實現的最佳化,而(3)、(4)兩種操作則更多的是從業務的角度去實現的最佳化。

2.2 最佳化策略

從上面的案例分析,我們可以得出,應用啟動最佳化,我們可以分別從技術和業務的角度來進行決策。

2.2.1 技術最佳化

(1)針對啟動流程任務進行梳理。

  • 無耗時任務 -> 主執行緒
  • 耗時且需要同步任務 -> 非同步執行緒 + 同步鎖
  • 耗時且無需同步任務 -> 非同步執行緒

(2)非必要不執行。

  • 非必要資料 -> 懶載入
  • 非必要任務 -> 延遲/空閒執行
  • 非必要介面/佈局 -> 延遲載入
  • 非必要功能 -> 刪除/外掛化

(3)資料結構最佳化,減小初始化時間。

  • 資料結構儘可能複用,避免記憶體抖動
  • 資料結構申請的空間要恰到好處,不能過大(佔用空間,建立慢)也不能過小(頻繁擴充,效率低)
  • 對於必要且量大的資料,可採取分段載入。
  • 重要的資源本地快取

2.2.2 業務最佳化

(1)業務流程整合。

  • 多個相關的序列業務整合為統一的一個業務。
  • 不相關的序列業務整合為並行的業務。

(2)業務流程拆分調整。

  • 對業務進行拆分,拆分出主要(必要)業務和次要(非必要)業務。
  • 分別對主要業務和次要業務進行優先順序評估,業務執行按優先順序從高到底依次執行。

2.3 最佳化方向

在最佳化之前,讓我們先來分析一下冷啟動的過程:

Zygote建立應用程式 -> AMS請求ApplicationThread -> Application建立 -> attachBaseContext > onCreate -> ActivityThread啟動Activity -> Activity生命週期(建立、佈局載入、螢幕佈置、首幀繪製)

以上過程,只有Application和Activity的生命週期這兩個階段對我們來說是可控的,所以這就是我們的最佳化方向。

3. 最佳化措施

3.1 啟動流程最佳化

1.依據之前我們列舉的技術最佳化策略,首先需要對啟動的所有任務流程進行梳理,然後對其執行方式進行最佳化。

  • 只有必要且非耗時的任務在主執行緒執行。
  • 耗時且需要同步的任務,使用非同步執行緒 + 同步鎖的方式執行。
  • 耗時且無需同步的任務在非同步執行緒執行。

2.第三方SDK初始化最佳化。

  • 對於那些在啟動時非必要的第三方SDK,可以延遲初始化。
  • 對於初始化耗時的第三方SDK,可以開啟一個後臺服務/非同步執行緒進行初始化。

3.使用任務執行框架。

這裡我們還可以使用一些第三方的任務啟動框架,對啟動流程進行最佳化。下面我就拿我開源的XTask 簡單介紹一下:

這裡我們模擬了三種型別的任務:

  • 1.優先順序最重要的任務,執行時間50ms;
  • 2.單獨的任務,沒有執行上的先後順序,但是需要同步,每個執行200ms;
  • 3.耗時、最不重要的任務,等所有任務執行完畢後執行,每個執行需要1000ms。

最佳化前

/**
 * 最佳化前的寫法, 這裡僅是演示模擬,實際的可能更復雜
 */
private void doJobBeforeImprove(long startTime) {
    new TopPriorityJob(logger).doJob();
    for (int i = 0; i < 4; i++) {
        new SingleJob((i + 1), logger).doJob();
    }
    new LongTimeJob(logger).doJob();
    log("任務執行完畢,總共耗時:" + (System.currentTimeMillis() - startTime) + "ms");
}

執行結果:

由於所有的任務都是執行在主執行緒,序列執行,所以花了大約1865ms。

最佳化後

/**
 * 最佳化後的寫法, 這裡僅是演示模擬,實際的可能更復雜
 */
private void doJobAfterImprove(final long startTime) {
    ConcurrentGroupTaskStep groupTaskStep = XTask.getConcurrentGroupTask();
    for (int i = 0; i < 4; i++) {
        groupTaskStep.addTask(buildSingleTask(i));
    }
    XTask.getTaskChain()
            .addTask(new MainInitTask(logger))
            .addTask(groupTaskStep)
            .addTask(new AsyncInitTask(logger))
            .setTaskChainCallback(new TaskChainCallbackAdapter() {
                @Override
                public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
                    log("任務完全執行完畢,總共耗時:" + (System.currentTimeMillis() - startTime) + "ms");
                }
            }).start();
    log("主執行緒任務執行完畢,總共耗時:" + (System.currentTimeMillis() - startTime) + "ms");
}

@NonNull
private XTaskStep buildSingleTask(int finalI) {
    return XTask.getTask(new TaskCommand() {
        @Override
        public void run() throws Exception {
            new SingleJob((finalI + 1), logger).doJob();
        }
    });
}    

執行結果:

由於只有優先順序最重要的任務在主執行緒執行,其他任務都是非同步執行,所以主執行緒任務執行消耗57ms,所有任務執行消耗1267ms。

3.2 IO最佳化

3.網路請求最佳化。

  • 啟動過程中避免不必要的網路請求。對於那些在啟動時非必要執行的網路請求,可以延時請求或者使用快取。
  • 對於需要進行多次序列網路請求的介面進行最佳化整合,控制好請求介面的粒度。比如後臺有獲取使用者資訊的介面、獲取使用者推薦資訊的介面、獲取使用者賬戶資訊的介面。這三個介面都是必要的介面,且存在先後關係。如果依次進行三次請求,那麼時間基本上都花在網路傳輸上,尤其是在網路不穩定的情況下耗時尤為明顯。但如果將這三個介面整合為獲取使用者的啟動(初始化)資訊,這樣資料在網路中傳輸的時間就會大大節省,同時也能提高介面的穩定性。

4.磁碟IO最佳化

  • 啟動過程中避免不必要的磁碟IO操作。這裡的磁碟IO包括:檔案讀寫、資料庫(sqlite)讀寫和SharePreference等。
  • 對於啟動過程中所必須的資料載入,選擇合適的資料結構。可以選擇支援隨機讀寫、延時解析的資料儲存結構以替代SharePreference。
  • 啟動過程中避免大量的序列化和反序列化。

3.3 執行緒最佳化

我們在開發應用的過程中,都或多或少會使用到執行緒。當我們建立一個執行緒時,需要向系統申請資源,分配記憶體空間,這是一筆不小的開銷,所以我們平時開發的過程中都不會直接操作執行緒,而是選擇使用執行緒池來執行任務。

但問題就在於如果執行緒池設定不對的話,很容易被人濫用,引發記憶體溢位的問題。而且通常一個應用會有多個執行緒池,不同功能、不同模組乃至是不同三方庫都會有自己的執行緒池,這樣大家各用各的,就很難做到資源的協調統一,勁不往一處使。

3.3.1 執行緒最佳化分析

1.執行緒池耗時分析。

要想進行執行緒最佳化,首先我們就需要了解執行緒池在使用過程中,哪些地方比較耗時。

  • 首先,從常規上來講,執行緒池在使用過程中,執行緒建立、執行緒切換和CPU排程比較耗時。
  • 其次,需要從業務的角度去分析當前應用的執行緒使用情況,到底是哪些任務導致執行緒池大量的建立和執行耗時。這裡我們可以使用Hook的方式,去Hook執行緒的建立(ThreadFactory的newThread方法)和執行進行統計。
  • 最後,我們需要結合業務的重要性(優先順序)以及其相應的執行緒執行開銷,進行綜合考量,調整執行緒池的執行策略。

2.執行緒池執行分析。

然後再讓我們看看執行緒池的執行邏輯:

我們知道,一個執行緒池通常由一個核心執行緒池和一個阻塞佇列組成。那麼當我們呼叫執行緒池去執行一個任務的時候,執行緒池是如何執行的呢?

核心執行緒池 -> 阻塞佇列 -> 最大執行緒數(新建執行緒) -> RejectedExecutionHandler(拒絕策略)
  • 當執行緒池中的核心執行緒池執行緒飽和後,任務會被塞進阻塞佇列中等待。
  • 如果阻塞佇列也滿了,執行緒池會建立新的執行緒。
  • 但是如果目前執行緒池中的執行緒總數已經達到了最大執行緒數,這個時候會呼叫執行緒池的拒絕策略(預設是直接中斷,丟擲異常)。

其中核心執行緒池的最大執行緒數並不是設定的越大越好,為什麼這麼說?因為CPU的處理能力是有限的,四核的CPU一次也只能同時執行四個任務,如果核心執行緒池數設定過大,那麼各任務之間就會互相競爭CPU資源,加大CPU的排程消耗,這樣反而會降低執行緒池的執行效率。

一般來說,核心執行緒池的最大執行緒數滿足下面的公式:

最佳核心執行緒數目 = ((執行緒等待時間 + 執行緒CPU時間)/ 執行緒CPU時間 )* CPU數目

(1)執行緒等待時間所佔比例越高,需要越多執行緒。
(2)執行緒CPU時間所佔比例越高,需要越少執行緒。

這裡我們考慮兩種最常見的情況:

  • CPU密集型任務:這種任務絕大多數時間都在進行CPU計算,執行緒等待時間幾乎為0,因此這時最佳核心執行緒數為 n + 1(或者為n),這裡n為CPU核心數。
  • IO密集型任務:這種任務,CPU通常需要等待I/O(讀/寫)操作,這樣CPU的處理時間和等待時間幾乎差不多,因此這時最佳核心執行緒數為 2n + 1(或者為2n),這裡n為CPU核心數。
3.3.2 執行緒最佳化目標

透過上面對執行緒池的分析,我們可以知道:

  • 執行緒池設定過大,會侵佔記憶體,互相競爭CPU資源,增加CPU的排程耗時。
  • 執行緒池設定過小,任務會阻塞序列,降低執行緒池執行效率。

因此,我們執行緒最佳化的目標是:

(1)擁有可以統籌全域性的統一的執行緒池。
(2)能根據機器的效能來控制數量,合理分配執行緒池大小。
(3)能夠根據業務的優先順序進行排程,優先順序高的先執行。

3.3.3 執行緒最佳化具體措施

1.建立主執行緒池+副執行緒池的組合執行緒池,由執行緒池管理者統一協調管理。主執行緒池負責優先順序較高的任務,副執行緒池負責優先順序不高以及被主執行緒池拒絕降級下來的任務。

這裡執行的任務都需要設定優先順序,任務優先順序的排程透過PriorityBlockingQueue佇列實現,以下是主副執行緒池的設定,僅供參考:

  • 主執行緒池:核心執行緒數和最大執行緒數:2n(n為CPU核心數),60s keepTime,PriorityBlockingQueue(128)。
  • 副執行緒池:核心執行緒數和最大執行緒數:n(n為CPU核心數),60s keepTime,PriorityBlockingQueue(64)。

2.使用Hook的方式,收集應用內所以使用newThread方法的地方,改為由執行緒池管理者統一協調管理。

3.將所有提供了設定執行緒池介面的第三方庫,透過其開放的介面,設定為執行緒池管理者管理。沒有提供設定介面的,考慮替換庫或者插樁的方式,替換執行緒池的使用。

3.4 閃屏最佳化

閃屏最佳化屬於啟動使用者體驗的最佳化。畢竟誰也不想使用頁面一閃一閃的應用。

1.設定自定義閃屏頁。

設定自定義的閃屏頁可以提高我們啟動的"視覺速度"。通常會設定一個背景,然後把logo居中顯示,可以使用xml檔案來佈局(注意,該圖片不可展示動畫,並且展示時間也不可控)。這種方式可以給使用者一種啟動非常快的感覺,不僅解決了啟動白屏的問題,並且展示了品牌logo也有助於提升品牌認知。

(1) 使用xml自定義一張帶有logo的圖片。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque">
    <item android:drawable="?attr/xui_config_color_splash_bg"/>

    <item android:bottom="?attr/xui_config_app_logo_bottom">
        <bitmap
            android:gravity="center"
            android:src="?attr/xui_config_splash_app_logo"/>
    </item>

    <item android:bottom="?attr/xui_config_company_logo_bottom">
        <bitmap
            android:gravity="bottom"
            android:src="?attr/xui_config_splash_company_logo"/>
    </item>
</layer>

(2) 把這張圖片透過設定主題的android:windowBackground屬性方式顯示為啟動閃屏。

<style name="XUITheme.Launch.Demo">
    <item name="android:windowBackground">@drawable/xui_config_bg_splash</item>
    <item name="xui_config_splash_app_logo">@drawable/ic_splash_app_logo_xui</item>
    <item name="xui_config_splash_company_logo">@drawable/ic_splash_company_logo_xuexiang</item>
</style>

(3) 在manifest中將主頁的主題設定為剛才帶啟動圖片的Launch主題。

<activity
    android:name=".activity.MainActivity"
    android:theme="@style/XUITheme.Launch.Demo">
</activity>

(4) 程式碼執行到主頁面的onCreate的時候設定為程式正常的主題,這樣就切回到正常主題背景了。

public class BaseActivity extends XPageActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
    }
}

2.使用Android12提供的啟動畫面Splash Screen。

https://developer.android.google.cn/guide/topics/ui/splash-screen

3.如果是因為某些不可抗拒的原因(例如一些大型遊戲的首次載入),導致第一次啟動非常慢的話,可以在主頁面進入之前,增加一個進度條載入頁面。這樣的好處就是要告訴使用者,到底需要多久才能載入完畢,給使用者一個明確的資訊,緩解使用者的等待焦慮。

4.減少首頁的跳轉層次。除非某些不可抗拒的原因(比如廣告作為一項重要收入來源),儘量不要設定啟動頁(SplashActivity),因為開啟和關閉任何一個Activity都是需要消耗時間的,每多一個Activity的跳轉就意味著我們主頁面的開啟時間會被延長。

3.5 主頁面最佳化

主頁面的啟動和顯示也是app啟動非常重要的一部分。

3.5.1 佈局最佳化

佈局最佳化的核心就是:提高頁面渲染的速度,防止頁面過度渲染導致耗時。
  • 降低檢視層級。減少冗餘或者巢狀佈局,防止頁面過度渲染。合理使用merge標籤和約束佈局ConstraintLayout
  • 不必要的佈局延遲載入。用ViewStub替代在啟動過程中不需要顯示的UI控制元件。
  • 首頁懶載入。首頁不需要立即顯示的頁面,可以使用懶載入。
  • 使用自定義View替代複雜的View疊加。

3.5.2 頁面資料預載入

一般來說,我們喜歡在每個頁面內部才開始載入和顯示資料,因為這樣寫可能更容易讓人看懂,利於以後的維護。但是如果等頁面UI佈局初始化完畢後,我們才去載入資料的話,勢必會增加頁面啟動顯示的時間。

因為每個頁面(Activity)的啟動本身就是比較耗時的過程,我們可以將需要顯示的資料進行預載入(即頁面啟動和資料載入同時進行,序列->並行),這樣等頁面UI佈局初始化完畢後,我們就可以拿著預載入的資料直接渲染顯示了,這樣可以減少資料載入的等待,從而達到加快頁面顯示的目的。

這裡我們可以參考開源預載入庫:PreLoader

3.6 系統排程最佳化

  • 1.啟動階段不啟動子程式,只在主程式執行Application的onCreate方法。因為子程式會共享CPU的資源,導致主程式CPU緊張。
  • 2.啟動過程中減少系統呼叫,避免與AMSWMS競爭鎖。因為AMSWMS在應用啟動的過程中承擔了很多工作,且這些方法很多都是帶鎖的,這時應用應當避免與它們進行通訊,避免出現大量的鎖等待,阻塞關鍵操作。
  • 3.啟動過程中除了Activity之外的元件啟動要謹慎。因為四大元件的啟動都是透過主執行緒Handler進行驅動的,如果在應用啟動的同時他們也啟動,Handler中的Message勢必會增加,從而影響應用的啟動速度。
  • 4.啟動過程中減少主執行緒Handler的使用。原因同上面一樣,Activity的啟動都是由主執行緒Handler進行驅動的,應用啟動期間減少主執行緒Handler的使用,可以減小對主頁面啟動的影響。對於那些量大且頻繁的任務排程,可以使用HandlerThread中的Looper建立屬於子執行緒的handler來代替。
  • 5.使用IdleHandler。利用IdleHandler特性,在訊息佇列空閒時,對延遲任務進行分批初始化。

3.7 GC 最佳化

啟動過程中應當減少GC的次數。因為GC會暫停程式的執行,從而會帶來延遲的代價。那麼我們應當如何避免頻繁的GC呢?

  • 1.避免進行大量的字串操作,特別是序列化和反序列化。不要使用+(加號)進行字串拼接。
  • 2.避免臨時物件的頻繁建立,頻繁建立的物件需要考慮複用。
  • 3.避免大量bitmap的繪製。
  • 4.避免在自定義View的onMeasureonLayoutonDraw中建立物件。

3.8 Webview啟動最佳化

如果你的應用使用到了Webview,可以按需對Webview進行最佳化。
  • 1.由於WebView首次建立比較耗時,需要預先建立WebView,提前將其核心初始化。
  • 2.使用WebView快取池,用到WebView的時候都從快取池中拿。
  • 3.應用內建本地離線包,如一些字型和js指令碼,即預置靜態頁面資源。

3.9 應用瘦身

對應用瘦身,可以最直接地加快資源載入的速度,從而提高應用啟動的效率。
  • 1.刪除沒有引用的資源。我們可以使用Inspect Code或者開啟資源壓縮,自動刪除無用的資源。
  • 2.能用xml寫Drawable的,就不要使用UI切圖。
  • 3.重用資源。同一影像的著色不同,我們可以用android:tint和tintMode屬性調整使用。
  • 4.壓縮png和jpeg檔案。這裡推薦一個非常好用的圖片壓縮外掛:img-optimizer
  • 5.使用webp檔案格式。Android Studio可以直接將現有的bmp,jpg,png或靜態gif影像轉換為webp格式。
  • 6.使用向量圖形,尤其是那些與解析度無關,且可伸縮的小圖示儘可能使用向量圖形。
  • 7.開啟程式碼混淆。使用proGuard程式碼混淆器工具,包括壓縮、最佳化、混淆等功能。
  • 8.對於一些獨立也非必須的功能模組,採用外掛化,按需載入。

3.10 資源重排

利用Linux的IO讀取策略,PageCache和ReadAhead機制,按照讀取順序重新排列,減少磁碟IO次數。具體可參見《支付寶 App 構建最佳化解析:透過安裝包重排布最佳化 Android 端啟動效能》 這篇文章。這種技術門檻較高,一般應用都不會用到。

3.11 類重排

類重排的實現透過ReDex的Interdex調整類在Dex中的排列順序。調整Dex中類的順序,把啟動時需要載入的類按順序放到主dex裡。具體實現可以參考《Redex初探與Interdex:Andorid冷啟動最佳化》 這篇文章。

4. 如何進行最佳化

上面講了那麼多應用啟動最佳化的策略和措施,可能有些人就會問了:那麼具體到我們每個不同的專案上,我們應該如何進行最佳化呢?

以下是我個人的最佳化步驟,僅供參考:

  • 1.明確最佳化的內容和目標。首先,做任何最佳化一定是需要帶著問題(目的)去最佳化的。任何不帶目的進行的最佳化都是耍流氓。
  • 2.分析現狀、確認問題。當我們目前需要最佳化的內容後,接下來就是需要進行大量的埋點統計、比較與分析,確認到底是因為什麼原因導致的應用啟動過慢,找到需要最佳化的部位。
  • 3.進行針對性的最佳化。找到導致應用啟動過慢的問題之後,就是按照本篇講述的最佳化策略和措施,進行針對性的最佳化。
  • 4.對最佳化結果進行總結並進行持續跟進。對最佳化前後的資料進行統計和比較,總結最佳化的經驗並在組內進行分享,並在後續的版本中進行持續跟進。有條件的可以結合CI,增加線上的啟動效能監控。

最後

講了這麼多,還是希望大家在平時開發的過程中,多重視一些應用啟動最佳化的相關技巧,這樣等別人讓你最佳化應用啟動的時候,也就不會那麼手足無措了。

我是xuexiangjys,一枚熱愛學習,愛好程式設計,勤于思考,致力於Android架構研究以及開源專案經驗分享的技術up主。獲取更多資訊,歡迎微信搜尋公眾號:【我的Android開源之旅】

本文由mdnice多平臺釋出

相關文章