系列文:
- 背景:Android App優(yōu)化, 要怎么做?
- Android App優(yōu)化之性能分析工具
- Android App優(yōu)化之提升你的App啟動速度之理論基礎
- Android App優(yōu)化之提升你的App啟動速度之實例挑戰(zhàn)
- Android App優(yōu)化之Layout怎么擺
- Android App優(yōu)化之ANR詳解
- Android App優(yōu)化之消除卡頓
- Android App優(yōu)化之內存優(yōu)化
- Android App優(yōu)化之持久電量
- 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);
}
});
}
}
當前冷啟動效果:
可以看到啟動時白屏了很長時間.
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文件
分析trace文件
- 在下方的方法區(qū)點擊"Real Time/Call", 按照方法每次調用耗時降序排.
- 耗時超過500ms都是值得注意的.
- 看左邊的方法名, 可以看到耗時大戶就是我們用的幾大平臺的初始化方法, 特別是Bugly, 還加載native的lib, 用ZipFile操作等.
- 點擊每個方法, 可以看到其父方法(調用它的)和它的所有子方法(它調用的).
- 點擊方法時, 上方的該方法執(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)在的效果:
可以看到提升了很多, 然后還有一點瑕疵, 就是起來的時候會有一個白屏, 如果手機較慢的話, 這個白屏就會持續(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, 最終的效果
讓我們來看下最終的效果:
相比之前, 呈現(xiàn)給用戶的不再是一個白屏了, 帶上了logo, 當然這個背景要顯示什么, 我們可以根據實際情況來自定義.
這種優(yōu)化, 對于有些Application內的初始化工作不能移到子線程做的情況, 是非常友好的. 可以避免我們的App長時間的呈現(xiàn)給用戶一個空白的窗口.
6, 結語
照例, 總結下.
這次關于App啟動時間的優(yōu)化, 寫了兩篇. 寫這么多, 還是想傳達下個人做技術的思想, 也算是個人的經驗回顧, 拋磚引玉.
實際場景可能遠比這個復雜吉执,在此更多的提供一種分析思路~歡迎擴展
矯情了, 還是總結下本文相關的吧:
- Application的onCreate中不要做太多事情.
- 首屏Activity盡量簡化.
- 善用工具分析.
- 多閱讀官方文檔, 很多地方貌似無關, 實際有關聯(lián), 例如這次就用了Material Design文檔中的解決方案.
本文完整源碼, 請移步Github