美文网首页
实战系列——听说可以做到APP秒开?

实战系列——听说可以做到APP秒开?

作者: 设计失 | 来源:发表于2021-06-24 18:21 被阅读0次

    不知道哪天下午,老板走到你面前;拿着手机看着你的编辑器:小王啊,你这个App启动有点慢啊,能不能优化一下呀?

    小王:老板你可别乱说啊,我启动的时候可不慢啊,说着拿着手机打开App,老板你看,1、2、3、4、5、6、7s过去了...手机上的APP还停留在启动界面 (内心os,糟糕了),一阵虚汗袭来,对着老板:老板,启动界面请求接口太多啦,应该是网络慢的原因。。。

    老板:我是用户,我不管你多少接口,我要的是快,你看别人家的APP,都是秒开、秒进;你怎么不行?

    小王:.......

    当然,上面的故事只是一种被老板加需求的情景;真实用户启动你APP非常慢的时候,多多少少会有抱怨,你可能会像我一样,打开浏览器一顿搜索:

    启动优化搜索.png

    然后一个个点进去查看后发现,一大堆理论:

    1、主题页面(无法解决实际卡顿问题)
    2、使用多线程异步初始化(实际项目多方SDK逻辑复杂)
    3、使用 AppStartUp 初始化(实际上会遇到未初始化或初始化过慢的问题)
    

    从来没有一个有解决自己项目中的优化点的文章,下面我从公司APP出发,从0开始优化,以肉眼能够看到的优化速度提供优化思路:

    #从Debug.startMethodTracing()说起

    做过性能优化的朋友都不会对Debug.startMethodTracing()方法陌生,这个方法可以生成对应的Trace文件,使用Profile可以用来分析CPU的方法运行耗时(在这里就不在演示生成和使用方法了);

    但是现在当你搜索使用startMethodTracing生成TraceView文件的方法来检验耗时操作时;会发现谷歌提示你说已经过时了:

    弃用Traceview.png
    歇逼了,原来使用的方法不得行了,那按照谷歌推荐的方式来搞吧?但是之前使用Profile监视器可以看到一段时间内对应的CPU方法耗时,但是无法统计从点击应用图标开始的CPU耗时;

    后面进入官网细心查看,我们可以看到Profile监视器其实是有这样的功能的:传送门

    profile启动监视.png

    ## 使用Profile监视启动方法耗时

    从上面的实践我们得知,可以通过设置Android Studio的启动来获取到Profile trace文件,具体的上图:

    步骤
    步骤一.png
    步骤二.png
    步骤三.png
    步骤四.png

    通过上面的步骤,Profile会自动生成启动的文件并开始分析,大概几十秒后,你会得到如下的界面:

    启动后的界面.png

    从左到右三个红框分别是:
    1:得到的设备及文件
    2:所有的线程运行耗时
    3:分析后的方法或者时间图

    ### 分析

    读者可能要发火了,废话说了一大堆,正事你是一件没干

    稍安勿躁,前面一大堆铺垫都是为了分析启动后得到的文件,又因为每个应用的耗时和方法都不一样,我只是提供一种分析思路,而不是真正为你秒开(那你是标题党咯?)哈哈哈,能不能秒开当然得看各位看官的造化了...

    首先,我们看下上面第三个红框里面分析的到底是什么:

    Top Down
    top_down.png

    Top Down很明显是将所有的耗时从高到低排序,第一个native thread我们默认是没有trace的,因为是使用的 Trace Java Methods(细心的你不知道有没有注意到),如果没注意到,可以重新点进去Debug Configurations配置;

    Flame Chart
    flame_chart.png

    这个图相信做过性能优化、内存优化的朋友都是非常熟悉的,我们经常查看CPU运行时间来查看方法耗时,看看对应的方法是否有优化空间;

    Bottom Up
    bottom_up.png

    该方法是自底而上,我分析的过少,了解的可以留言;

    从Flame Chart开始着手

    打开页面,选中其中一个方格点击(不选中可能引起后面操作无法放大缩小),然后使用 WS缩放方格;

    了解过应用启动过程的朋友可能知道,应用启动能够优化的过程仅在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启动的时候设置的;

    applyConfigurationToResourcesLocked.png

    createAppContext()方法中调用了LoadedApk.crreateOrUpdateClassLoaderLocked,通过方法栈可以看到主要是获取到DexFile,而加载Dex文件也是一个耗时的过程,在了解到启动优化的时候,我们经常看到大厂有说到Dex优化,其实也是从这一过程着手

    createAppContext.png

    installContentProviders()该方法,使用过JetPack AppStartUp的都知道,其原理也是将初始化内容放到ContentProvider中,利用的就是ContentProvider初始化会比Application#onCnreate()早的原因,但是治标不治本,内容初始化还是在里面,耗时还是没减少;相反的,还可能引起未初始化就使用、或者初始化失败的情况;

    installContentProviders.png
    在这里就发现了问题,因为项目中使用了Mob Share SDK,而这个第三方库使用了一个ShareSDKFileProviderContentProvider,使用Mob也无需手动调用任何代码,原因是Mob插件帮我们完成了这一系列的操作;包括添加QQWechat依赖库还有MobSDK的初始化(初始化在MobProvider类中,这个MobProvider也在后面会初始化),看到Mob插件里面的功能,才知道插件原来功能这么强大;

    当然,最新版本的Mob Share SDK我已经联系已经将Provider去除了,查看代码其实是使用的SSDKLog,对于耗时成本,日志对我们接入项目是完全没用的,所以后期版本考虑要去除调Mob(手动滑稽)

    mob客服.png

    callApplicationOnCreate()方法,调用了我们自己的Application,其中就是自己的业务耗时了;我们项目中用到了阿里ARouter友盟统计等第三方的初始化,可以尝试将ARouter异步初始化或者去除掉😹;

    callApplicatoinOnCreate.png

    ####启动业务流程

    一般App启动都会有一个启动页面、或者有一个广告页面,然后从启动页面到主页都会有一个;当我们从点击图标开始进入主页都会有自己的业务逻辑:

    可能是调用服务器同步信息
    可能是初始化第三方服务
    也有可能是加载闪屏图片

    我们项目从点击到主页的逻辑也非常多,包含了初始化第三方服务、调用接口、获取闪屏等等,所以从用户点击图标到进入主页的过程都需要各位自己去分析;
    上面分析的步骤是从应用启动到Applicatoin#onCreate()方法的优化,可以看到哪些第三方耗时,进入onCreate()之后,就会调用ActivityThread#performLaunchActivity()启动我们的闪屏页面,当然也要分析这个过程的耗时;

    缩小方法耗时栈,可以看到,在Application#onCreate()后调用了我们项目的StartActivity#oncreate()

    startActivity.png

    再次将焦点放大到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操作必定是耗时的,应该避免这些操作,界面如有必要可以使用代码的方式创建;

    相关文章

      网友评论

          本文标题:实战系列——听说可以做到APP秒开?

          本文链接:https://www.haomeiwen.com/subject/skpwyltx.html