zoukankan      html  css  js  c++  java
  • <转>Android App性能评测分析-启动时间篇

    1、前言

    随着项目版本的迭代,App的性能问题会逐渐暴露出来,而好的用户体验与性能表现紧密相关,性能问题从应用的启动优化开始,下面会根据实际app性能测试案例,进行app性能评测之启动时间的分析和总结。

    2、App启动方式了解

    通常来说, 一个App启动也会分如下三中不同的状态:

    • 冷启动
      当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动,也就是先实例化Application

      冷启动的流程即为App启动流程的全过程, 需要创建App进程, 加载相关资源, 启动Main Thread, 初始化首屏Activity等.

      在这个过程中, 屏幕会显示一个空白的窗口(颜色基于主题), 直至首屏Activity完全启动.

      下图展示了冷启动的时间线:


       
      冷启动流程.png
    • 热启动
      当启动应用时,后台已有该应用的进程,这时会从已有的进程来启动Activity(不需要重新创建Application)

      类同与冷启动, 在这个过程中, 屏幕会显示一个空白的窗口(颜色基于主题), 直至activity渲染完毕.

    • 温启动
      介于冷启动和热启动之间, 一般来说在以下两种情况下发生:

      • 用户back退出了App, 然后又启动. App进程可能还在运行, 但是activity需要重建.

      • 用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启, 但是可以在onCreate中将被动杀死锁保存的状态(saved instance state)恢复.

    通过三种启动状态的相关描述, 可以看出我们要做的启动优化其实就是针对冷启动. 热启动和温启动都相对较快.

    3、性能评测结果案例分析

    3.1 XX银行 APP启动时间评测结果分析

    3.1.1 启动时间测试结果

    统计的时间为APP的冷启动时间,4G网络情况下,平均冷启动时间为14.1S,平均热启动时间为6.7S,无网络时平均启动时间为5.2S。由于APP的启动时间耗时远远超过行业标准水平3S,当app启动时,任何一个地方有耗时操作都会拖慢我们应用的启动速度,所以针对APP启动时间做了具体分析

    3.1.2评测结果分析

    首先来看App从点击桌面图标到我们看到App的主界面整个过程中经过了哪些步骤:
    启动虚拟机—>启动AMS —>通过Zygote创建ApplicationProcess进程–>Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上。

    1)初始化Application
    • 【初始化Application】应用程序初始化总共耗费了3.26S,耗时已超过行业APP启动标准基线时间3S。具体看下耗时详情分析:
      (1)应用Application.attach的时间较长。应用存在多个dex文件,请控制应用方法数量。
      (2)onCreate方法耗时长,主要体现BaseApplication创建上。

    长耗时方法排名如下:
    1.com.pingan.fstandard.framework.base.BaseApplication.onCreate(BaseApplication.java)

    1. com.pingan.fstandard.common.c.a(InitManager.java)
    2. com.pafinancialtech.pafs.kitchendaddy.f.b(PAFFToast.java:83
      4.com.pingan.fstandard.HomeApplication.d(HomeApplication.java:51

    综上分析,BaseApplication创建上耗费较长时间,需要开发进行更详细的分析优化。另外,初始化Application时,也包含了任意门相关等非核心功能的程序,建议启动时不创建该类程序。

    2)初始化Activity
    • 【初始化Activity】耗时也过长,耗时约1s左右
      主要耗时的地方是在闪屏界面创建oncreate时com.pingan.fstandard.activity.SplashActivity.onCreate,建议开发优化
    3)加载请求及响应

    首先来一张流程图了解一下我们APP首页加载的一个流程

     
    页面加载流程.png
     
    冷启动资源加载.png
    • 【网络请求及响应】总体来看,首页加载耗时占比最多,需要6s左右,对应的网络消耗时间占比45%左右。

    根据抓包及代码段分析显示APP启动到首页加载完成是一个预加载和异步加载的过程。
    (1) 项目中大部分第三方组件都抢占先机,在Application主线程初始化。这样的初始化方式肯定是过重的,建议考虑异步初始化三方组件,不阻塞主线程;延迟部分三方组件(比如埋点统计、任意门、ImageLoader的初始化)。
    (2) 启动到首页加载完成网络请求密集,请求内容丰富,部分资源重复请求。
    请求了相关配置信息、启动页广告、个推、磁贴配置信息、商城理财产品列表,运营广告Banner、F后端接口,广告BANNER位、插件信息、任意门、百度地图SDK(talkingdata、灰度升级)等林林总总加起来就有40+个网络请求,加载的数据+广告页一共有1.7M左右,这说明了我们的APP首页设计的内容比较丰富,部分资源重复请求,部分内容需要调外部接口,所以耗时比较长,这是产品设计问题、信息未做缓存处理和部分外部关联方接口响应慢的原因导致,建议优化。
    (3)非核心功能资源过早请求加载
    另外其中类似百度地图SDK、任意门、微信sdk相关插件只有在生活页分享时使用到的非主要业务功能启动时也加载出来了,建议开发同学和产品同学调整APP启动时网络请求的加载策略,非核心/主要/高PV的功能相关的API,SDK、插件等没有必要在启动时做加载缓存,这是非常耗时且得不偿失的。

    3.1.3启动时间优化建议

    (1)应用Application.attach的时间较长。应用存在多个dex文件,请控制应用方法数量。
    (2)onCreate方法耗时长,主要体现BaseApplication创建上,需要开发进行更详细的分析优化,建议考虑异步初始化三方组件,不阻塞主线程;延迟或者和产品一起梳理需要的去除的部分三方组件(比如统计、任意门、ImageLoader的初始化)。
    (3)com.pingan.fstandard.activity.MainActivity存在过度绘制导致卡顿,首页UI布局层次过多,建议开发进一步分析优化
    (4)建议产品调整优化app启动时网络请求的加载策略,非核心/主要/高PV的功能相关的API,SDK、插件等没有必要在启动时做加载缓存
    (5)部分资源重复请求,建议信息缓存,常用信息只在第一次获取,之后从缓存中取
    (6)建议开发梳理无用但被执行的老代码,去掉无用但被执行的老代码

    综上,建议开发童鞋尽快投入做APP启动时性能改善和优化,以提升行业相比竞品的竞争力。

    4、App端启动耗时排查方法及思路

    4.1 排查方法:使用Traceview分析

    TraceView 是 Android SDK 中内置的1个工具,它可以加载 trace 文件,用图形的情势展现代码的履行时间、次数及调用栈,便于我们分析。

    (1)使用Android Studio工具DDMS

    生成Traceview 进行分析查看具体Trace期间各方法调用关系,调用次数以及耗时比例

     
    ddms.png

    (2)使用代码生成 trace 文件

      Debug.startMethodTracing("shixintrace");   
     //开始 trace,保存文件到 "/sdcard/shixintrace.trace"
        // ...
        Debug.stopMethodTracing();    //结束
    

    代码很简单,当你调用开始代码的时候,系统会生产 trace 文件,并且产生追踪数据,当你调用结束代码时,会将追踪数据写入到 trace 文件中。
    下1步使用 adb 命令将 trace 文件导出到电脑:

    adb pull /sdcard/shixintrace.trace /tmp
    

    使用代码生成 trace 方式的好处是容易控制追踪的开始和结束,缺点就是步骤略微多了一点。

    (3)直接使用android studio生成trace文件

    Android Studio 内置的 Android Monitor 可以很方便的生成 trace 文件到电脑。
    注意:此方法仅仅适用于下拉代码到本地生成DEBUG测试包到手机进行调试

     
    monitor1.png
     
    monitor2.png

    分析指标:

    不应在Application以及Activity的生命周期回调中做任何费时操作,具体指标大概是你在onCreate,onResume,onStart等回调中所花费的总时间最好不要超过400ms,否则用户在桌面点击你的应用图标后,将感觉到明显的卡顿。

    4.2 开启严苛模式(StrictMode)

    检查是否有主线程做了耗时操作: 开启 严苛模式(StrictMode)

    (1)StrictMode可以用于捕捉发生在应用程序 主线程中耗时的磁盘、网络访问或函数调用,使主线程处理UI和动画在磁盘读写和网络操作时变得更平滑,避免主线程被阻塞,导致ANR窗口的发生。

    下面是启用StrictMode的实例,可以在Application的OnCreate中添加,这样就能在程序启动的最初一刻进行监控了。

    private void setStrictMode() {
            if (Integer.valueOf(Build.VERSION.SDK) > 3) {
                Log.d(LOG_TAG, "Enabling StrictMode policy over Sample application");
                StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                        .detectAll()    // 这里可以替换为.detectDiskReads().detectDiskWrites().detectNetwork()。
                                        // detectAll() 包括了磁盘读写和网络I/O
                        .penaltyLog()   //打印logcat,当然也可以定位到dropbox,通过文件保存相应的log
                        .penaltyDeath()
                        .build());
                StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                        .detectAll()
                        .penaltyLog()
                        .build());
            }
        }
    

    (2)除了通过日志查看之外,我们也可以在开发者选项中开启严格模式,开启之后,如果主线程中有执行时间长的操作,屏幕则会闪烁,这是一个更加直接的方法。

    4.3、通过抓包工具分析

    可以通过抓包工具Charels、Fiddler等查看网络请求加载了哪些资源,加载资源的顺序、哪些资源重复加载、信息是否缓存

    4.4、查看Logcat

    通过adb输出log信息,过滤日志查看APP启动时每个方法的大致耗时。

    4.5、启动时间排查思路

    对于App来说, 我们可以控制的启动时间线的点无外乎:

    Application的onCreate
    首屏Activity的渲染

    而我们现在的App动不动集成了很多第三方服务, 启动时需要检查广告, 注册状态等等一系列接口都是在Application的onCreate或是首屏的onCreate中做的.

    5、启动时间耗时常见原因及优化建议

    5.1、 常见主要问题(持续补充ing)

    • 部分数据库及IO的操作发生在首屏Activity主线程;
    • Application中创建了线程池;
    • 启动时做密集沉重的初始化(Heavy app initialization);
    • Multidex的使用,也是拖慢启动速度的元凶;
    • UI存在过度绘制;
    • 首屏Activity网络请求密集;
    • 非核心功能资源过早请求加载
    • 工作线程使用未设置优先级;
    • 信息未缓存,重复获取同样信息;
    • 流程问题:例如闪屏图每次下载,当次使用;
    • 执行无用老代码;
    • 执行开发阶段使用的代码;
    • 执行重复逻辑;
    • 调用三方SDK里或者Demo里的多余代码;

    5.2、建议:

    • 去掉无用但被执行的老代码;
    • 去掉开发阶段使用但线上被执行的代码;
    • 去掉重复逻辑执行代码;
    • UI渲染优化,去除重复绘制,减少UI重复绘制时间
    • 去掉调用三方SDK里或者Demo里的多余代码;
    • 信息缓存,常用信息只在第一次获取,之后从缓存中取;
    • 项目是多进程架构,只在主进程执行Application的onCreate();
    • 根据优先级的划分,一些初始化工作能否将任务优先级划分成3个等级,任务优先级为2,3的,通过懒加载的方式在首页渲染完成后进行加载
    • 避免I/O操作、反序列化、网络操作、布局嵌套等。

    参考文献:
    学习地址:
    https://www.cnblogs.com/sunzn/p/3192231.html
    http://www.wfuyu.com/technology/27625.html
    http://blog.csdn.net/qq_16131393/article/details/51172488

    作者:萧竹 出处:https://www.jianshu.com/u/88caeb7696f5 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 相关阅读:
    《Spring in action 4》(十一)SSM + Redis
    git 上传时报文件已存在的错
    JQuery--find与filter
    初识mysql
    并发--模块整理
    守护进程与守护线程
    用string模块生成随机验证码
    loggin 模块及其单例模式
    TCP协议--黏包现象
    网络编程---tcp/udp协议
  • 原文地址:https://www.cnblogs.com/1737623253zhang/p/11547350.html
Copyright © 2011-2022 走看看