最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

Android 快速定位耗时方法

热点资讯 loading 1381浏览 0评论

一、启动耗时检测

1、查看Logcat

在Android Studio Logcat中过滤关键字“Displayed”,可以看到对应的冷启动耗时日志。

2、adb shell

使用adb shell获取应用的启动时间

adb shell am start -W [packageName]/[AppstartActivity全路径]

执行后会得到三个时间:ThisTime、TotalTime和WaitTime,详情如下:

  • ThisTime 最后一个Activity启动耗时。
  • TotalTime 所有Activity启动耗时。
  • WaitTime AMS启动Activity的总耗时。

一般查看得到的TotalTime,即应用的启动时间,包括创建进程 + Application初始化 + Activity初始化到界面显示的过程。

特点:
  • 线下使用方便,不能带到线上。
  • 非严谨、精确时间。
3、AOP(Aspect Oriented Programming)打点

面向切面编程,通过预编译和运行期动态代理实现程序功能统一维护的一种技术。

作用

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性,同时大大提高了开发效率。

AOP核心概念
1)、横切关注点

对哪些方法进行拦截,拦截后怎么处理。

2)、切面(Aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象。

3)、连接点(JoinPoint)

被拦截到的点(方法、字段、构造器)。

4)、切入点(PointCut)

对JoinPoint进行拦截的定义。

5)、通知(Advice)

拦截到JoinPoint后要执行的代码,分为前置、后置、环绕三种类型。

准备

首先,为了在Android使用AOP埋点需要引入AspectJ,在项目根目录的build.gradle下加入:

classpath 'com.hujiang.aspectjx:gradle-android-plugin- aspectjx:2.0.0'

然后,在app目录下的build.gradle下加入:

apply plugin: 'android-aspectjx'
implement 'org.aspectj:aspectjrt:1.8.+'
AOP埋点实战

JoinPoint一般定位在如下位置:

  • 函数调用
  • 获取、设置变量
  • 类初始化
  • 使用PointCut对我们指定的连接点进行拦截,通过Advice,就可以拦截到JoinPoint后要执行的代码。
  • Advice通常有以下几种类型:
  • Before:PointCut之前执行
  • After:PointCut之后执行
  • Around:PointCut之前、之后分别执行

首先,我们举一个小栗子:

@Before("execution(* android.app.Activity.on**(..))")
public void onActivityCalled(JoinPoint joinPoint) throws Throwable {
...
}

在execution中的是一个匹配规则,第一个*代表匹配任意的方法返回值,后面的语法代码匹配所有Activity中on开头的方法。

处理Join Point的类型:

  • call:插入在函数体里面
  • execution:插入在函数体外面

如何统计Application中的所有方法耗时?

@Aspect
public class ApplicationAop {
    @Around("call (* com.json.chao.application.BaseApplication.**(..))")
    public void getTime(ProceedingJoinPoint joinPoint) {
    Signature signature = joinPoint.getSignature();
    String name = signature.toShortString();
    long time = System.currentTimeMillis();
    try {
        joinPoint.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    Log.i(TAG, name + " cost" +     (System.currentTimeMillis() - time));
    }
}
注意

当Action为Before、After时,方法入参为JoinPoint。

当Action为Around时,方法入参为ProceedingPoint。

Around和Before、After的最大区别:

ProceedingPoint不同于JoinPoint,其提供了proceed方法执行目标方法。

总结AOP特性:
  • 无侵入性
  • 修改方便

强烈推荐结合第三节讲解的Systrace工具使用,可以非常快速地定位到耗时方法,上线的时候,可以考虑屏蔽掉AOP功能。

//apply plugin: 'android-aspectjx'

二、启动速度分析工具 — TraceView

使用方式
方式1

检测开始代码处添加:

Debug.startMethodTracing();

检测结束代码处添加:

Debug.stopMethodTracing();

使用adb pull将生成的**.trace文件导出到电脑,然后使用Android Studio的Profiler加载

方式2

打开Profiler -> CPU -> 点击 Record -> 点击 Stop -> 查看Profiler下方Top Down/Bottom Up 区域找出耗时的热点方法。

Profile CPU
1、Trace types
Trace Java Methods

会记录每个方法的时间、CPU信息。对运行时性能影响较大。

Sample Java Methods

相比于Trace Java Methods会记录每个方法的时间、CPU信息,它会在应用的Java代码执行期间频繁捕获应用的调用堆栈,对运行时性能的影响比较小,能够记录更大的数据区域。

Sample C/C++ Functions

需部署到Android 8.0及以上设备,内部使用simpleperf跟踪应用的native代码,也可以命令行使用simpleperf。

Trace System Calls

检查应用与系统资源的交互情况。
查看所有核心的CPU瓶。
内部采用systrace,也可以使用systrace命令。

2、Event timeline

显示应用程序中在其生命周期中转换不同状态的活动,如用户交互、屏幕旋转事件等。

3、CPU timeline

显示应用程序实时CPU使用率、其它进程实时CPU使用率、应用程序使用的线程总数。

4、Thread activity timeline

列出应用程序进程中的每个线程,并使用了不同的颜色在其时间轴上指示其活动。

  • 绿色:线程处于活动状态或准备好使用CPU。
  • 黄色:线程正等待IO操作。(重要)
  • 灰色:线程正在睡眠,不消耗CPU时间。
Profile提供的检查跟踪数据窗口有四种
1、Call Chart

提供函数跟踪数据的图形表示形式。

  • 水平轴:表示调用的时间段和时间。
  • 垂直轴:显示被调用方。
  • 橙色:系统API。
  • 绿色:应用自有方法
  • 蓝色:第三方API(包括Java API)

提示:右键点击Jump to source跳转至指定函数。

2、Flame Chart

将具有相同调用方顺序的完全相同的方法收集起来。

  • 水平轴:执行每个方法的相对时间量。
  • 垂直轴:显示被调用方。

注意:看顶层的哪个函数占据的宽度最大(平顶),可能存在性能问题。

3、Top Down

递归调用列表,提供self、children、total时间和比率来表示被调用的函数信息。

Flame Chart是Top Down列表数据的图形化。

4、Bottom Up

展开函数会显示其调用方。

按照消耗CPU时间由多到少的顺序对函数排序。

注意点:
  • Wall Clock Time:程序执行时间。
  • Thread Time:CPU执行的时间。
TraceView小结
特点
  • 图形的形式展示执行时间、调用栈等。
  • 信息全面,包含所有线程。
  • 运行时开销严重,整体都会变慢,得出的结果并不真实。
  • 找到最耗费时间的路径:Flame Chart、Top Down。
  • 找到最耗费时间的节点:Bottom Up。
作用

主要做热点分析,得到两种数据:

  • 单次执行最耗时的方法。
  • 执行次数最多的方法。

三、启动速度分析工具 — Systrace

使用方式:代码插桩

定义Trace静态工厂类,将Trace.begainSection(),Trace.endSection()封装成i、o方法,然后再在想要分析的方法前后进行插桩即可。

在命令行下执行systrace.py脚本:

python /Users/quchao/Library/Android/sdk/platform-tools/systrace/systrace.py -t 20 sched gfx view wm am app webview -a "com.wanandroid.json.chao" -o ~/Documents/open-project/systrace_data/wanandroid_start_1.html
具体参数含义如下:
  • -t:指定统计时间为20s。
  • shced:cpu调度信息。
  • gfx:图形信息。
  • view:视图。
  • wm:窗口管理。
  • am:活动管理。
  • app:应用信息。
  • webview:webview信息。
  • -a:指定目标应用程序的包名。
  • -o:生成的systrace.html文件。
如何查看数据?

在UIThread一栏可以看到核心的系统方法时间区域和我们自己使用代码插桩捕获的方法时间区域。

Systrace原理

在系统的一些关键链路(如SystemServcie、虚拟机、Binder驱动)插入一些信息(Label);

通过Label的开始和结束来确定某个核心过程的执行时间;

把这些Label信息收集起来得到系统关键路径的运行时间信息,最后得到整个系统的运行性能信息;

Android Framework里面一些重要的模块都插入了label信息,用户App中可以添加自定义的Lable。

Systrace小结
特性

结合Android内核的数据,生成Html报告。

系统版本越高,Android Framework中添加的系统可用Label就越多,能够支持和分析的系统模块也就越多。

必须手动缩小范围,会帮助你加速收敛问题的分析过程,进而快速地定位和解决问题。

作用

主要用于分析绘制性能方面的问题。

分析系统关键方法和应用方法耗时。

结合AOP,可以在方法的前后,非常方便地批量插入以下代码。最后从运行生成Html报告后,可以快速查找出耗时的方法。

Trace.begainSection();
Trace.endSection();

作者:gerrard0898
链接:https://juejin.im/post/5e0b06ab5188253a82107b32

转载请注明:Android开发中文站 » Android 快速定位耗时方法

您必须 登录 才能发表评论!