在Android開發過程中APP啓動速度一直是困擾着很多開發者的一個問題,今天就跟大家聊一天關於App啓動速度如何優化的話題,本文主要分爲:1.代碼分析、2.Traceview上場、3.調整Application onCreate、4.PlaceHolder四個部分來跟大家一起探討。
以之前寫的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); } }); } }
當前冷啓動效果:
可以看到啓動時白屏了很長時間.
接下來我們結合我們上文的理論知識, 和介紹的Traceview工具, 來分析下Application的onCreate耗時.
Debug.startMethodTracing("GithubApp"); ... Debug.stopMethodTracing();
運行程序, 會在sdcard上生成一個"GithubApp.trace"的文件.
注意: 需要給程序加上寫存儲的權限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
adb pull /sdcard/GithubApp.trace ~/temp
traceview_ui
既然已經知道了哪些地方耗時長, 我們不妨調整下Application的onCreate實現, 一般來說我們可以將這些初始化放在一個單獨的線程中處理, 爲了方便今後管理, 這裏我用了一個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); } }
看看現在的效果:
可以看到提升了很多, 然後還有一點瑕疵, 就是起來的時候會有一個白屏, 如果手機較慢的話, 這個白屏就會持續一段時間, 不太友好.
那麼還有沒有什麼辦法優化呢?
Android最新的Material Design有這麼個建議的. 建議我們使用一個placeholder UI來展示給用戶直至App加載完畢.
怎麼做呢?
如第3節所言, 當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>
寫一個什麼都不做的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>
讓我們來看下最終的效果:
相比之前, 呈現給用戶的不再是一個白屏了, 帶上了logo, 當然這個背景要顯示什麼, 我們可以根據實際情況來自定義.
這種優化, 對於有些Application內的初始化工作不能移到子線程做的情況, 是非常友好的. 可以避免我們的App長時間的呈現給用戶一個空白的窗口.
照例, 總結下.
這次關於App啓動時間的優化, 寫了兩篇. 寫這麼多, 還是想傳達下個人做技術的思想, 也算是個人的經驗回顧, 拋磚引玉.實際場景可能遠比這個複雜,在此更多的提供一種分析思路~歡迎擴展
矯情了, 還是總結下本文相關的吧:
- Application的onCreate中不要做太多事情.
- 首屏Activity儘量簡化.
- 善用工具分析.
- 多閱讀官方文檔, 很多地方貌似無關, 實際有關聯, 例如這次就用了Material Design文檔中的解決方案.