不知道哪天下午,老板走到你面前;拿着手机看着你的编辑器:小王啊,你这个App启动有点慢啊,能不能优化一下呀?
小王:老板你可别乱说啊,我启动的时候可不慢啊,说着拿着手机打开App,老板你看,1、2、3、4、5、6、7s过去了...手机上的APP还停留在启动界面 (内心os,糟糕了),一阵虚汗袭来,对着老板:老板,启动界面请求接口太多啦,应该是网络慢的原因。。。
老板:我是用户,我不管你多少接口,我要的是快,你看别人家的APP,都是秒开、秒进;你怎么不行?
小王:.......
当然,上面的故事只是一种被老板加需求的情景;真实用户启动你APP
非常慢的时候,多多少少会有抱怨,你可能会像我一样,打开浏览器一顿搜索:
然后一个个点进去查看后发现,一大堆理论:
1、主题页面(无法解决实际卡顿问题)
2、使用多线程异步初始化(实际项目多方SDK逻辑复杂)
3、使用 AppStartUp 初始化(实际上会遇到未初始化或初始化过慢的问题)
从来没有一个有解决自己项目中的优化点的文章,下面我从公司APP
出发,从0开始优化,以肉眼能够看到的优化速度提供优化思路:
#从Debug.startMethodTracing()说起
做过性能优化的朋友都不会对Debug.startMethodTracing()
方法陌生,这个方法可以生成对应的Trace
文件,使用Profile
可以用来分析CPU
的方法运行耗时(在这里就不在演示生成和使用方法了);
但是现在当你搜索使用startMethodTracing
生成TraceView
文件的方法来检验耗时操作时;会发现谷歌提示你说已经过时了:
歇逼了,原来使用的方法不得行了,那按照谷歌推荐的方式来搞吧?但是之前使用
Profile
监视器可以看到一段时间内对应的CPU
方法耗时,但是无法统计从点击应用图标开始的CPU
耗时;
后面进入官网细心查看,我们可以看到Profile
监视器其实是有这样的功能的:传送门
## 使用Profile
监视启动方法耗时
从上面的实践我们得知,可以通过设置Android Studio
的启动来获取到Profile trace
文件,具体的上图:
步骤 | |
---|---|
一 | 步骤一.png |
二 | 步骤二.png |
三 | 步骤三.png |
四 | 步骤四.png |
通过上面的步骤,Profile
会自动生成启动的文件并开始分析,大概几十秒后,你会得到如下的界面:
从左到右三个红框分别是:
1:得到的设备及文件
2:所有的线程运行耗时
3:分析后的方法或者时间图
### 分析
读者可能要发火了,废话说了一大堆,正事你是一件没干
稍安勿躁,前面一大堆铺垫都是为了分析启动后得到的文件,又因为每个应用的耗时和方法都不一样,我只是提供一种分析思路,而不是真正为你秒开(那你是标题党咯?)哈哈哈,能不能秒开当然得看各位看官的造化了...
首先,我们看下上面第三个红框里面分析的到底是什么:
Top Down
top_down.pngTop Down
很明显是将所有的耗时从高到低排序,第一个native thread
我们默认是没有trace
的,因为是使用的 Trace Java Methods
(细心的你不知道有没有注意到),如果没注意到,可以重新点进去Debug Configurations
配置;
Flame Chart
flame_chart.png这个图相信做过性能优化、内存优化的朋友都是非常熟悉的,我们经常查看CPU
运行时间来查看方法耗时,看看对应的方法是否有优化空间;
Bottom Up
bottom_up.png该方法是自底而上,我分析的过少,了解的可以留言;
从Flame Chart开始着手
打开页面,选中其中一个方格点击(不选中可能引起后面操作无法放大缩小),然后使用 W
、S
缩放方格;
了解过应用启动过程的朋友可能知道,应用启动能够优化的过程仅在handleBindApplication
之后,而第三方ContentProvider
或者自己声明的Provider
都会在Application#onCreate()
方法之前运行,所以我们只需要看这一过程中运行的方法和耗时即可;
main函数耗时.png
从上面的方格可以看到有不同颜色的方法渲染,红色一般是指系统Room
的方法,白色和黄色指自己
的方法,这里自己是指厂商的room和APP业务代码
;
* 可以看 ① ② 处方法块中有白色块,但是看名字是`miui`厂商自己的代码,这一块方法我们是无法修改的
* 在调用handleBindApplication()方法里面,包括了:
public void handleBindApplication() {
applyConfigurationToResourcesLocked(); // ①
...
ContextImpl.createAppContext(this, data.info); // ②
...
installContentProviders(app, data.providers); // ③
...
mInstrumentation.callApplicationOnCreate(app); // ④
}
以上的方法在ActivityThread#handleBindApplication()
方法中调用,当然我也是看到方法栈后省略了其中许多代码,原来的流程读者可以点到源码中细细品读;
① 按W
放大方法栈,可以看到我当前项目中的MiuiResourcesImpl.updateConfiguration()
方法栈无法优化,这是主题样式获取,系统在APP启动的时候设置的;
② createAppContext()
方法中调用了LoadedApk.crreateOrUpdateClassLoaderLocked
,通过方法栈可以看到主要是获取到DexFile
,而加载Dex
文件也是一个耗时的过程,在了解到启动优化的时候,我们经常看到大厂有说到Dex优化
,其实也是从这一过程着手
③ installContentProviders()
该方法,使用过JetPack AppStartUp
的都知道,其原理也是将初始化内容放到ContentProvider中
,利用的就是ContentProvider
初始化会比Application#onCnreate()
早的原因,但是治标不治本,内容初始化还是在里面,耗时还是没减少;相反的,还可能引起未初始化就使用、或者初始化失败的情况;
在这里就发现了问题,因为项目中使用了Mob Share SDK
,而这个第三方库使用了一个ShareSDKFileProvider
的ContentProvider
,使用Mob
也无需手动调用任何代码,原因是Mob插件
帮我们完成了这一系列的操作;包括添加QQ
、Wechat
依赖库还有MobSDK
的初始化(初始化在MobProvider
类中,这个MobProvider
也在后面会初始化),看到Mob插件
里面的功能,才知道插件原来功能这么强大;
当然,最新版本的
mob客服.pngMob Share SDK
我已经联系已经将Provider
去除了,查看代码其实是使用的SSDKLog
,对于耗时成本,日志对我们接入项目是完全没用的,所以后期版本考虑要去除调Mob
(手动滑稽)
④ callApplicationOnCreate()
方法,调用了我们自己的Application
,其中就是自己的业务耗时了;我们项目中用到了阿里ARouter
、友盟统计
等第三方的初始化,可以尝试将ARouter
异步初始化或者去除掉😹;
####启动业务流程
一般
App
启动都会有一个启动页面、或者有一个广告页面,然后从启动页面到主页都会有一个;当我们从点击图标开始进入主页都会有自己的业务逻辑:可能是调用服务器同步信息
可能是初始化第三方服务
也有可能是加载闪屏图片
我们项目从点击到主页的逻辑也非常多,包含了初始化第三方服务、调用接口、获取闪屏等等,所以从用户点击图标到进入主页的过程都需要各位自己去分析;
上面分析的步骤是从应用启动到Applicatoin#onCreate()
方法的优化,可以看到哪些第三方耗时,进入onCreate()
之后,就会调用ActivityThread#performLaunchActivity()
启动我们的闪屏页面,当然也要分析这个过程的耗时;
缩小方法耗时栈,可以看到,在Application#onCreate()
后调用了我们项目的StartActivity#oncreate()
再次将焦点放大到StartActivity
的耗时流程,看到各种熟悉的方法:
PhoneWindow.installDecor()
LayoutInflater.inflate()
loadXmlDrawable()
相信看到这些方法都不陌生,setContentView()
的流程浮现在脑海中了吧~~ 但是回想起我的项目启动页面啥也没有,你居然给我耗时近1s
时间,这肯定忍无可忍的;
既然io
读写操作,xml
的读写都是非常耗时的,那就直接使用代码创建界面吧,当然,越复杂的界面肯定优化效果越好,为了避免io
操作,还是需要花时间精力改写;
xml 写法 | 代码写法 |
---|---|
start_xml.png | start_java.png |
当然,这是我们项目中的耗时,各位需花时间精力处理自己项目中的业务逻辑,这里就不再细究了;
总结
1、了解了谷歌废弃了Debug
来生成Trace
文件的方法,使用Profile
来统计APP
点击图标到启动的CPU
耗时时间
2、知道了启动过程中哪些第三方库会导致应用启动慢,解决方法也简单,给第三方库提建议或者直接去掉那些耗时的第三方库
3、应用中的io
操作必定是耗时的,应该避免这些操作,界面如有必要可以使用代码的方式创建;
网友评论