Android App優(yōu)化之提升你的App啟動速度之實例挑戰(zhàn)

系列文:

  1. 背景:Android App優(yōu)化, 要怎么做?
  2. Android App優(yōu)化之性能分析工具
  3. Android App優(yōu)化之提升你的App啟動速度之理論基礎
  4. Android App優(yōu)化之提升你的App啟動速度之實例挑戰(zhàn)
  5. Android App優(yōu)化之Layout怎么擺
  6. Android App優(yōu)化之ANR詳解
  7. Android App優(yōu)化之消除卡頓
  8. Android App優(yōu)化之內存優(yōu)化
  9. Android App優(yōu)化之持久電量
  10. Android App優(yōu)化之如何高效網絡請求

1, 代碼分析

之前寫的Github App為例.

因為這個App集成了Bugly, Push, Feedback等服務, 所以Application的onCreate有很多第三方平臺的初始化工作...

public class GithubApplication extends MultiDexApplication {

    @Override
    public void onCreate() {
        super.onCreate();

        // init logger.
        AppLog.init();

        // init crash helper
        CrashHelper.init(this);

        // init Push
        PushPlatform.init(this);

        // init Feedback
        FeedbackPlatform.init(this);

        // init Share
        SharePlatform.init(this);

        // init Drawer image loader
        DrawerImageLoader.init(new AbstractDrawerImageLoader() {
            @Override
            public void set(ImageView imageView, Uri uri, Drawable placeholder) {
                ImageLoader.loadWithCircle(GithubApplication.this, uri, imageView);
            }
        });
    }
}

當前冷啟動效果:


code_start_before_optimize

可以看到啟動時白屏了很長時間.

2, Traceview上場

接下來我們結合我們上文的理論知識, 和介紹的Traceview工具, 來分析下Application的onCreate耗時.

在onCreate開始和結尾打上trace.

Debug.startMethodTracing("GithubApp");
...
Debug.stopMethodTracing();

運行程序, 會在sdcard上生成一個"GithubApp.trace"的文件.

注意: 需要給程序加上寫存儲的權限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

通過adb pull將其導出到本地

adb pull /sdcard/GithubApp.trace ~/temp

廣告: adb的眾多用法, 可以參考我的另一篇文

打開DDMS分析trace文件

ddms_open_trace

分析trace文件

traceview_ui
  1. 在下方的方法區(qū)點擊"Real Time/Call", 按照方法每次調用耗時降序排.
  2. 耗時超過500ms都是值得注意的.
  3. 看左邊的方法名, 可以看到耗時大戶就是我們用的幾大平臺的初始化方法, 特別是Bugly, 還加載native的lib, 用ZipFile操作等.
  4. 點擊每個方法, 可以看到其父方法(調用它的)和它的所有子方法(它調用的).
  5. 點擊方法時, 上方的該方法執(zhí)行時間軸會閃動, 可以看該方法的執(zhí)行線程及相對時長.

3, 調整Application onCreate再試

既然已經知道了哪些地方耗時長, 我們不妨調整下Application的onCreate實現(xiàn), 一般來說我們可以將這些初始化放在一個單獨的線程中處理, 為了方便今后管理, 這里我用了一個InitializeService的IntentService來做初始化工作.

明確一點, IntentService不同于Service, 它是工作在后臺線程的.

InitializeService.java代碼如下:

package com.anly.githubapp.compz.service;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.ImageView;

import com.anly.githubapp.common.wrapper.AppLog;
import com.anly.githubapp.common.wrapper.CrashHelper;
import com.anly.githubapp.common.wrapper.FeedbackPlatform;
import com.anly.githubapp.common.wrapper.ImageLoader;
import com.anly.githubapp.common.wrapper.PushPlatform;
import com.anly.githubapp.common.wrapper.SharePlatform;
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader;
import com.mikepenz.materialdrawer.util.DrawerImageLoader;

/**
 * Created by mingjun on 16/8/25.
 */
public class InitializeService extends IntentService {

    private static final String ACTION_INIT_WHEN_APP_CREATE = "com.anly.githubapp.service.action.INIT";

    public InitializeService() {
        super("InitializeService");
    }

    public static void start(Context context) {
        Intent intent = new Intent(context, InitializeService.class);
        intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
                performInit();
            }
        }
    }

    private void performInit() {
        AppLog.d("performInit begin:" + System.currentTimeMillis());

        // init Drawer image loader
        DrawerImageLoader.init(new AbstractDrawerImageLoader() {
            @Override
            public void set(ImageView imageView, Uri uri, Drawable placeholder) {
                ImageLoader.loadWithCircle(getApplicationContext(), uri, imageView);
            }
        });

        // init crash helper
        CrashHelper.init(this.getApplicationContext());

        // init Push
        PushPlatform.init(this.getApplicationContext());

        // init Feedback
        FeedbackPlatform.init(this.getApplication());

        // init Share
        SharePlatform.init(this.getApplicationContext());

        AppLog.d("performInit end:" + System.currentTimeMillis());
    }
}

GithubApplication的onCreate改成:

public class GithubApplication extends MultiDexApplication {

    @Override
    public void onCreate() {
        super.onCreate();

        // init logger.
        AppLog.init();

        InitializeService.start(this);
    }
}

看看現(xiàn)在的效果:

improved-1.gif

可以看到提升了很多, 然后還有一點瑕疵, 就是起來的時候會有一個白屏, 如果手機較慢的話, 這個白屏就會持續(xù)一段時間, 不太友好.

那么還有沒有什么辦法優(yōu)化呢?

4, 給我們的應用窗口弄一個PlaceHolder

Android最新的Material Design有這么個建議的. 建議我們使用一個placeholder UI來展示給用戶直至App加載完畢.

怎么做呢?

給Window加上背景

如第3節(jié)所言, 當App沒有完全起來時, 屏幕會一直顯示一塊空白的窗口(一般來說是黑屏或者白屏, 根據App主題).

前文理論基礎有說到, 這個空白的窗口展示跟主題相關, 那么我們是不是可以從首屏的主題入手呢? 恰好有一個windowBackground的主題屬性, 我們來給Splash界面加上一個主題, 帶上我們想要展示的背景.

做一個logo_splash的背景:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 底層白色 -->
    <item android:drawable="@color/white" />
    
    <!-- 頂層Logo居中 -->
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/ic_github" />
    </item>
</layer-list>

弄一個主題:

<style name="SplashTheme" parent="AppTheme">
    <item name="android:windowBackground">@drawable/logo_splash</item>
</style>

將一個什么不渲染布局的Activity作為啟動屏

寫一個什么都不做的LogoSplashActivity.

public class LogoSplashActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 注意, 這里并沒有setContentView, 單純只是用來跳轉到相應的Activity.
        // 目的是減少首屏渲染
        
        if (AppPref.isFirstRunning(this)) {
            IntroduceActivity.launch(this);
        }
        else {
            MainActivity.launch(this);
        }
        finish();
    }
}

在AndroidManifest.xml中設置其為啟動屏, 并加上主題:

<activity
  android:name=".ui.module.main.LogoSplashActivity"
  android:screenOrientation="portrait"
  android:theme="@style/SplashTheme">
  <intent-filter>
      <action android:name="android.intent.action.MAIN"/>
      <category android:name="android.intent.category.LAUNCHER"/>
  </intent-filter>
</activity>

5, 最終的效果

讓我們來看下最終的效果:

improved-2.gif

相比之前, 呈現(xiàn)給用戶的不再是一個白屏了, 帶上了logo, 當然這個背景要顯示什么, 我們可以根據實際情況來自定義.

這種優(yōu)化, 對于有些Application內的初始化工作不能移到子線程做的情況, 是非常友好的. 可以避免我們的App長時間的呈現(xiàn)給用戶一個空白的窗口.

6, 結語

照例, 總結下.
這次關于App啟動時間的優(yōu)化, 寫了兩篇. 寫這么多, 還是想傳達下個人做技術的思想, 也算是個人的經驗回顧, 拋磚引玉.

實際場景可能遠比這個復雜吉执,在此更多的提供一種分析思路~歡迎擴展

矯情了, 還是總結下本文相關的吧:

  1. Application的onCreate中不要做太多事情.
  2. 首屏Activity盡量簡化.
  3. 善用工具分析.
  4. 多閱讀官方文檔, 很多地方貌似無關, 實際有關聯(lián), 例如這次就用了Material Design文檔中的解決方案.

本文完整源碼, 請移步Github

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市穆碎,隨后出現(xiàn)的幾起案子先蒋,更是在濱河造成了極大的恐慌,老刑警劉巖辩撑,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件界斜,死亡現(xiàn)場離奇詭異,居然都是意外死亡合冀,警方通過查閱死者的電腦和手機各薇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來君躺,“玉大人峭判,你說我怎么就攤上這事∽亟校” “怎么了林螃?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俺泣。 經常有香客問我疗认,道長,這世上最難降的妖魔是什么伏钠? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任横漏,我火速辦了婚禮,結果婚禮上贝润,老公的妹妹穿的比我還像新娘绊茧。我一直安慰自己,他們只是感情好打掘,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布华畏。 她就那樣靜靜地躺著鹏秋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亡笑。 梳的紋絲不亂的頭發(fā)上侣夷,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音仑乌,去河邊找鬼百拓。 笑死,一個胖子當著我的面吹牛晰甚,可吹牛的內容都是我干的衙传。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼厕九,長吁一口氣:“原來是場噩夢啊……” “哼蓖捶!你這毒婦竟也來了?” 一聲冷哼從身側響起扁远,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤俊鱼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后畅买,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體并闲,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年谷羞,在試婚紗的時候發(fā)現(xiàn)自己被綠了帝火。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡洒宝,死狀恐怖购公,靈堂內的尸體忽然破棺而出萌京,到底是詐尸還是另有隱情雁歌,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布知残,位于F島的核電站靠瞎,受9級特大地震影響,放射性物質發(fā)生泄漏求妹。R本人自食惡果不足惜乏盐,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望制恍。 院中可真熱鬧父能,春花似錦、人聲如沸净神。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至爱榕,卻和暖如春瓣喊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背黔酥。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工藻三, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人跪者。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓棵帽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渣玲。 傳聞我的和親對象是個殘疾皇子岖寞,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容