朝阳杨少爷(ID:CY_YANG_DA_YE),专一于Android领域的开发者、分享者。复制代码
咱们的APP新版本,从2017年4月份提交第一行代码开始,就如今已经有两年半的时间,在这两年多的时间里,APP的内容内容不断丰富,例如前后加入了求职招聘、问答、我的中心、二手机,小视频等等模块。同时对于之前的旧功能也在不断地完善,例如,丰富了发帖的内容、小视频详情页像抖音同样方便快捷、标签的聚合更加精准的吸引用户。java
在功能、内容丰富的同时,难免会引入不少第三方的工具,例如友盟、个推、神策、腾讯云的视频组件、IM及时通信组件,GreenDao数据库等等shell
随着代码量愈来愈多,第三方工具越来多的致使APP,首次启动的时候,时间较长,用户体验较差。数据库
Nimbledroid统计Google Play各种别APP冷启动平均耗时 (Nexus 5 + Android 4.4)
[图片上传失败...(image-c82b9a-1565334335822)]
而Google Play排名前100的非游戏类的应用的冷启动时间统计为缓存
39个冷启动时间在2秒之内
73个冷启动时间在3秒之内ruby
而咱们APP的冷启动时间为:bash
执行命令行网络
adb shell am start -W -n 包名/包名.activity.MainEntryActivity
复制代码
ThisTime:最后一个启动的Activity的启动耗时;
TotalTime:本身的全部Activity的启动耗时;
WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和本身Activity的启动)。app
也就是说,在启动的时候,须要2.4s。异步
可见,还有很大的优化空间。ide
接下来,就经过本篇内容,深刻的剖析一下,启动慢的缘由,并给出合理的解决方案。
开始,优化启动的过程,那么就要先了解一下,Android系统,在启动一个APP的过程是什么样的?都作了什么事儿?
冷启动:当启动应用时,后台没有该应用的进程,这时系统会从新建立一个新的进程分配给该应用。
热启动:当启动应用时,后台存在该应用的进程(back键,home键,应用退出,可是没有销毁),从已有的进程中启动。
由于热启动是后台进程已经存在了,因此启动速度比较快,这里提到的优化是指冷启动。
Android冷启动过程相对比较复杂,须要经历35步,简单来讲,须要5个过程
整个应用程序的启动过程要执行不少步骤,可是总体来看,主要分为如下五个阶段:
一. Step1 - Step 11:
Launcher经过Binder进程间通讯机制通知ActivityManagerService,
它要启动一个Activity;
二. Step 12 - Step 16:
ActivityManagerService经过Binder进程间通讯机制通知Launcher进入Paused状态;
三. Step 17 - Step 24:
Launcher经过Binder进程间通讯机制通知ActivityManagerService,它已经准备就绪进入Paused状态,
因而ActivityManagerService就建立一个新的进程,用来启动一个ActivityThread实例,
即将要启动的Activity就是在这个ActivityThread实例中运行;
四. Step 25 - Step 27:
ActivityThread经过Binder进程间通讯机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,
以便之后ActivityManagerService可以经过这个Binder对象和它进行通讯;
五. Step 28 - Step 35:
ActivityManagerService经过Binder进程间通讯机制通知ActivityThread,
如今一切准备就绪,它能够真正执行Activity的启动操做了。
复制代码
在更加简单一点,从用户点击桌面图标开始,大体会经历如下几个过程:
在这个启动过程中,可能会存在的几个问题:
1.点击图标好久都不响应。
2.首页显示太慢。
3.首页显示后没法操做。
而接下来,重点解决的问题就是,点击图标好久都不响应的问题。
优化工具
当,遇到一个问题的时候,到最后解决的步骤,无外乎有如下几个步骤。
Hugo是 jake Wharton大神制做的工具,能够在log当中打印每一个方法的执行时间,能够帮咱们定位到运行时间比较长的方法和函数,配置也相对简单。
接下来,就在代码当中实践一下。
本想观察一下 Application当中,初始化的时间
@DebugLog
@Override
public void onCreate() {
super.onCreate();
initHttpClient();
// //初始化配置
initGlobal();
// CrashCaptureHandler.getInstance().initCrashHandler();
initJPush();
initGetTuiPush();
initSource();
initSmallVideoRecord();
initIM();
if (BuildConfig.DEBUG) {
boolean hostUrlEnable = new HostUrlUtils().initHost(URLConstants.class.getDeclaredFields(), URLConstants.RELEASE_SERVER);
if (hostUrlEnable) {
Dispatcher.hostUrlClass = URLConstants.class;
}
} else {
//正式环境才开始crash防御
install();
initAliyunLog();
}
initHuoDongHeZi();
initSensor();
initUmeng();
initLinkMe();
initRN();
}
复制代码
观察打印的Log
2019-08-10 16:10:09.752 30634-30634/> V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:09.941 30678-30678/? V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:10.004 30692-30692/? V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:10.124 30737-30737/? V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:10.529 30678-30678/? V/MyApplication: ⇠ onCreate [587ms]
2019-08-10 16:10:10.569 30634-30634/? V/MyApplication: ⇠ onCreate [816ms]
2019-08-10 16:10:10.832 30737-30737/? V/MyApplication: ⇠ onCreate [707ms]
2019-08-10 16:10:10.881 30692-30692/? V/MyApplication: ⇠ onCreate [877ms]
2019-08-10 16:10:13.917 31109-31109/?:xg_service_v3 V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:14.441 31109-31109/?:xg_service_v3 V/MyApplication: ⇠ onCreate [524ms]
复制代码
能够发现,原来初始化onCreat被屡次调用!
正常状况下,一个应用会开启一个进程,那么application会被执行一次,说明在启动的过程中,不只如此,由于初始化的重复调用,致使初始化的时间就有 2391ms!这就极大的影响到APP的启动速度!给用于一种,点击完图标没有反应的感受。
既然,问题已经发现了,就要解决这些问题。
既然发现是进程的问题,那么就要看看,在启动的过程中,总共有哪些进程。
2019-08-10 16:40:07.881 3166-3166/? D/yzc: ?
2019-08-10 16:40:07.978 3225-3225/? D/yzc: ?:pushcore
2019-08-10 16:40:08.128 3298-3298/? D/yzc: ?:ipc
2019-08-10 16:40:08.172 3245-3245/? D/yzc: ?:pushservice
2019-08-10 16:40:11.754 3739-3739/?s:xg_service_v3 D/yzc: ?s:xg_service_v3
复制代码
从中,能够看到除了APP的进程以外,还有 信鸽推送的进程,ipc进程,个推动程,极光的进程。
以前可能某些业务须要,集成了三家的推送,而每一个推送都会在后台默默的开启一个进程来接收推送消息。天然就会致使APP的第一次初始化启动很慢。
和运营小伙伴确认,当前只有信鸽推送在使用,个推,极光就给去掉。那么去掉以后,再看一下启动时间。
同时也避免了屡次初始化的问题!
相比以前的2391ms 优化了800ms! 接下来,继续优化,看看哪里还有能够优化的空间。
在Application当中,能够看到,初始化的内容有
initGlobal();
初始化配置
initIM();
初始化及时通信
initSource();
初始化APP相关版本配置
initSmallVideoRecord();
初始化短视频
install();
初始化放在Crash
initAliyunLog();
初始化阿里云相关内容
initHuoDongHeZi();
初始化活动盒子
initSensor();
初始化神策
initUmeng();
初始化友盟
initLinkMe();
初始化深度链接
initRN();
初始化ReactNative
分别统计一下每一个初始化须要的的时间。
initBbsGlobal [73ms]
initIM [95ms]
initSource [0ms]
initSmallVideoRecord [11ms]
install [0ms]
initAliyunLog [19ms]
initHuoDongHeZi [69ms]
initSensor [191ms]
initUmeng [14ms]
initLinkMe [63ms]
initRN [0ms]
根据统计,能够看到耗时较长的方法有神策、IM聊天、活动盒子、深度链接、配置文件。
对于这些时间长的,在不影响功能的状况下,能够尝试放在子线程当中进行初始化,不占用主线程的资源。
new Thread(new Runnable() {
@Override
public void run() {
initIM();
initSource();
initSmallVideoRecord();
initLinkMe();
initUmeng();
if (!URLConstants.RELEASE_SERVER) {
boolean hostUrlEnable = new HostUrlUtils().initHost(URLConstants.class.getDeclaredFields(), URLConstants.RELEASE_SERVER);
if (!hostUrlEnable) {
new AlertDialog.Builder(getApplicationContext())
.setTitle("未检测到可用的URL")
.setMessage("请确诊URL地址可用")
.setPositiveButton("退出", null).show();
}
Dispatcher.hostUrlClass = URLConstants.class;
} else {
//正式环境才开始crash防御
install();
initAliyunLog();
}
initHuoDongHeZi();
}
}).start();
复制代码
通过改造,看一下启动时间。
相比以前又优化了 300ms!,相比没有优化以前优化了1132ms! 也就是快了 1秒 多的时间,能够说效果仍是十分明显的。
同时能够看到,Application的初始化时间当前仅需199ms
在 Application的优化告一段落,关注一下 MainEntryActivity 这个 Activity 是进入app的一个Activity,在这个 Activity的onCreate()当中 进行了一些请求操做,看看这里面有什么能够优化的地方。
在这个里,有两个操做形成耗时时间比较长,一个是
isCityInviteNetWork()
这个方法是当用户登陆的时候,根据用户获取到用户的地理位置信息并存储。
这个方法彻底能够放在子线程当中,不占用主线程的资源。
checkAdversiterment()
这个是在网络获取开屏广告的方法,这个须要请求接口,获取到开屏广告并展现的方法。
对于这个方法,也能够子线程当中异步操做,第一次请求下来缓存起来,从第二次进入APP,在进行展现,这样避免了主线程的耗时操做。
通过这些操做以后,能够看到启动时间,已经优化到了1s左右。
虽然,没有目前没有达到秒开的效果,可是比起优化以前已经有了很大的区别,优化效果。从此,还会继续优化,争取达到一个更好的效果。
以前我看有朋友说能够设置主题达到秒开的效果,我试了一下好像不太行,也须要大概500ms左右,若是你们有好的想法和建议,欢迎给我留言!咱们一块儿讨论!
参考:
www.jianshu.com/p/e69d22ec0…
www.jianshu.com/p/1e65aa223…
www.jianshu.com/p/496529bd1…
blog.csdn.net/u013263323/…