App优化之提升你的App启动速度之实例挑战
2017-02-18 18:18
513 查看
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); } }); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
当前冷启动效果:
可以看到启动时白屏了很长时间.
2, Traceview上场
接下来我们结合我们上文的理论知识, 和介绍的Traceview工具, 来分析下Application的onCreate耗时.
在onCreate开始和结尾打上trace.
Debug.startMethodTracing("GithubApp"); ... Debug.stopMethodTracing();1
2
3
1
2
3
运行程序, 会在sdcard上生成一个”GithubApp.trace”的文件.
注意: 需要给程序加上写存储的权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>1
1
通过adb pull将其导出到本地
adb pull /sdcard/GithubApp.trace ~/temp1
1
广告: adb的众多用法, 可以参考我的另一篇文
打开DDMS分析trace文件
分析trace文件
在下方的方法区点击”Real Time/Call”, 按照方法每次调用耗时降序排.
耗时超过500ms都是值得注意的.
看左边的方法名, 可以看到耗时大户就是我们用的几大平台的初始化方法, 特别是Bugly, 还加载native的lib, 用ZipFile操作
131a0
等.
点击每个方法, 可以看到其父方法(调用它的)和它的所有子方法(它调用的).
点击方法时, 上方的该方法执行时间轴会闪动, 可以看该方法的执行线程及相对时长.
3, 调整Application onCreate再试
既然已经知道了哪些地方耗时长, 我们不妨调整下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()); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
GithubApplication的onCreate改成:
public class GithubApplication extends MultiDexApplication { @Override public void onCreate() { super.onCreate(); // init logger. AppLog.init(); InitializeService.start(this); } }1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
看看现在的效果:
可以看到提升了很多, 然后还有一点瑕疵, 就是起来的时候会有一个白屏, 如果手机较慢的话, 这个白屏就会持续一段时间, 不太友好.
那么还有没有什么办法优化呢?
4, 给我们的应用窗口弄一个PlaceHolder
Android最新的Material Design有这么个建议的.建议我们使用一个placeholder UI来展示给用户直至App加载完毕.
怎么做呢?
给Window加上背景
如第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>1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
弄一个主题:
<style name="SplashTheme" parent="AppTheme"> <item name="android:windowBackground">@drawable/logo_splash</item> </style>1
2
3
1
2
3
将一个什么不渲染布局的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(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在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>1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
5, 最终的效果
让我们来看下最终的效果:相比之前, 呈现给用户的不再是一个白屏了, 带上了logo, 当然这个背景要显示什么, 我们可以根据实际情况来自定义.
这种优化, 对于有些Application内的初始化工作不能移到子线程做的情况, 是非常友好的. 可以避免我们的App长时间的呈现给用户一个空白的窗口.
6, 结语
照例, 总结下. 这次关于App启动时间的优化, 写了两篇. 写这么多, 还是想传达下个人做技术的思想, 也算是个人的经验回顾, 抛砖引玉.
实际场景可能远比这个复杂,在此更多的提供一种分析思路~欢迎扩展
矫情了, 还是总结下本文相关的吧:
Application的onCreate中不要做太多事情.
首屏Activity尽量简化.
善用工具分析.
多阅读官方文档, 很多地方貌似无关, 实际有关联, 例如这次就用了Material Design文档中的解决方案.
相关文章推荐
- Android App优化之提升你的App启动速度之实例挑战
- Android App优化之提升你的App启动速度之实例挑战
- App优化之提升你的App启动速度之实例挑战
- App优化之提升你的App启动速度之实例挑战
- Android App优化之提升你的App启动速度之实例挑战
- Android App优化之提升你的App启动速度之实例挑战
- Android App优化之提升App启动速度实例
- Android App优化之提升你的App启动速度
- Android App优化之提升你的App启动速度之理论基础
- Android App优化之提升你的App启动速度之理论基础
- App优化之提升你的App启动速度之理论基础
- App优化之提升你的App启动速度
- 优化 App 的启动速度
- Android性能优化之提高App启动速度和Splash页面设计
- Android开机启动速度优化 && app启动速度优化
- WP7 App性能优化(4):加快应用程序启动速度(上)
- 安卓开发类似QQ、微信优化APP启动速度,记录返回桌面前的任务栈状态
- 提升html5的性能体验系列之五webview启动速度优化及事件顺序解析]
- windows7系统运行速度提升之服务优化(修改服务的启动类型)
- WP7 App性能优化(5):加快应用程序启动速度(下)