您的位置:首页 > 移动开发 > Android开发

Android 性能优化 之 TraceView工具的使用

2016-11-15 15:14 537 查看
Traceview简介

TraceView 是 Android 平台特有的数据采集和分析工具,它主要用于分析 Android 中应用程序的 hotspot。TraceView 本身只是一个数据分析工具,而数据的采集则需要使用 Android SDK 中的 Debug 类或者利用 DDMS 工具。二者的用法如下:

开发者在一些关键代码段开始前调用 Android SDK 中 Debug 类的 startMethodTracing 函数,并在关键代码段结束前调用 stopMethodTracing 函数。这两个函数运行过程中将采集运行时间内该应用所有线程(注意,只能是 Java 线程)的函数执行情况,并将采集数据保存到 /mnt/sdcard/ 下的一个文件中。开发者然后需要利用 SDK 中的 TraceView 工具来分析这些数据。

借助 Android SDK 中的 DDMS 工具。DDMS 可采集系统中某个正在运行的进程的函数调用信息。对开发者而言,此方法适用于没有目标应用源代码的情况。

DDMS 中 TraceView 使用示意图如下,调试人员可以通过选择 Devices 中的

应用后点击 按钮 Start Method Profiling(开启方法分析)和点击

Stop Method Profiling(停止方法分析)



我这边用一个简单的demo学习一下这个工具的使用。

代码如下所示:

package com.mingrisoft;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity implements Runnable {
private ImageView iv; // 声明一个显示广告图片的ImageView对象
private Handler handler; // 声明一个Handler对象
private List<Integer> list1 = new ArrayList<Integer>();
private int[] path = new int[] { R.drawable.img01, R.drawable.img02,
R.drawable.img03, R.drawable.img04, R.drawable.img05,
R.drawable.img06 }; // 保存广告图片的数组
private String[] title = new String[] { "编程词典系列产品", "高效开发", "快乐分享", "用户人群",
"快速学习", "全方位查询" }; // 保存显示标题的数组
public void dynatest()
{
for(int i = 0; i < 500000; i++) {
//Log.i(TAG, "======== ");
list1.add(i);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
iv = (ImageView) findViewById(R.id.imageView1); // 获取显示广告图片的ImageView
Thread t = new Thread(this); // 创建新线程
t.start(); // 开启线程
// 实例化一个Handler对象
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 更新UI
TextView tv = (TextView) findViewById(R.id.textView1); // 获取TextView组件
if (msg.what == 0x101) {
dynatest();
tv.setText(msg.getData().getString("title")); // 设置标题
iv.setImageResource(path[msg.arg1]); // 设置要显示的图片
}
super.handleMessage(msg);
}

};

}

@Override
public void run() {
int index = 0;
while (!Thread.currentThread().isInterrupted()) {
index = new Random().nextInt(path.length); // 产生一个随机数
Message m = handler.obtainMessage(); // 获取一个Message
m.arg1 = index; // 保存要显示广告图片的索引值
Bundle bundle = new Bundle(); // 获取Bundle对象
m.what = 0x101; // 设置消息标识
bundle.putString("title", title[index]); // 保存标题
m.setData(bundle); // 将Bundle对象保存到Message中
handler.sendMessage(m); // 发送消息

try {
Thread.sleep(2000); // 线程休眠2秒钟
} catch (InterruptedException e) {
e.printStackTrace(); // 输出异常信息
}

}
}
}


这个demo主要功能是每两秒随机显示一张广告图片。首先将三张图片资源引用ID保存到 path 这个数组中。

private int[] path = new int[] { R.drawable.img01, R.drawable.img02,
R.drawable.img03, R.drawable.img04, R.drawable.img05,
R.drawable.img06 }; // 保存广告图片的数组


MainActivity 重写 Runnable的 Run函数,每隔两秒钟随机发送一个图片ID给hander然后显示。

TextView tv = (TextView) findViewById(R.id.textView1); // 获取TextView组件
if (msg.what == 0x101) {
//故意增加延迟
for(int i = 0; i < 10000; i++) {
list1.add(i);
}
tv.setText(msg.getData().getString("title")); // 设置标题
iv.setImageResource(path[msg.arg1]); // 设置要显示的图片
}


我们在显示广告图片中故意加一个循环用于延迟广告图片显示。

其UI划分为上下两个面板,即Timeline Panel(时间线面板)和Profile Panel(分析面板)。



左边Pane显示的是测试数据中所采集的线程信息。由图1-4可知,本次测试数据采集了main线程,两个Binder线程和其它系统辅助线程(例如GC线程等)的信息。

右边Pane所示为时间线,时间线上是每个线程测试时间段内所涉及的函数调用信息。这些信息包括函数名、函数执行时间等。由图可知,main线程对应行的的内容非常丰富,而其他线程在这段时间内干得工作则要少得多。

另外,开发者可以在时间线Pane中移动时间线纵轴。纵轴上边将显示当前时间点中某线程正在执行的函数信息。

同意颜色在实际轴越长说明该颜色代表的函数花费时间越长

Profile Panel 是 TraceView 的核心界面,其内涵非常丰富。它主要展示了某个线程(先在 Timeline Panel 中选择线程)中各个函数调用的情况,包括 CPU 使用时间、调用次数等信息。而这些信息正是查找 hotspot 的关键依据。所以,对开发者而言,一定要了解 Profile Panel 中各列的含义。下表列出了 Profile Panel 中比较重要的列名及其描述。



另外,每一个Time列还对应有一个用时间百分比来统计的列(如Incl Cpu Time列对应还有一个列名为Incl Cpu Time %的列,表示以时间百分比来统计的Incl Cpu Time)。

了解完Traceview的UI后,现在介绍如何利用Traceview来查找hotspot。

一般而言,hotspot包括两种类型的函数:

一类是调用次数不多,但每次调用却需要花费很长时间的函数。在示例代码中,它就是hotspot 1。

一类是那些自身占用时间不长,但调用却非常频繁的函数。在示例代码中,它就是hotspot 2。

在我们代码中handleMessage 函数属于 每次调用花费很长时间的函数。因为里面有个耗时的dynatest循环在那一直处理。



从上图中我们可以发现

标号 12 的 com.mingrisoft.MainActivity.dynatest这一行很特别。它的Incl Cpu Time % 是 32.1 % ,Excl Cpu Time 也是 32.1% ,并且 它的 Calls+Recur Calls/Total列显示其调用次数为1,即它仅仅被调用一次了。这个函数是应用程序实现的,所以极有可能是一个潜在的Hotspot。在这个函数里面做了很多耗时的操作。

比如标号 11 的 handleMessage 虽然 它的Incl Cpu Time % 是 40.2 %,但是它的Excl Cpu Time 是 0 % 。

说明dynatest可能是手机发烫、卡顿、高 CPU 占用率的原因所在。

我们现在把函数 dynatest() 注释掉,再看看结果:





说明去掉dynatest这个函数后。handleMessage 主要花费时间都是在 setImageResource 函数上。而且这个函数是系统函数。

第二种情况

就是虽然每次调用花费时间不是很多。但是调用次数很多的那种情况。

源码中函数dynatest() 注释掉。添加一下代码

public void handleMessage(Message msg) {
// 更新UI
TextView tv = (TextView) findViewById(R.id.textView1); // 获取TextView组件
if (msg.what == 0x101) {
//dynatest() ;
for(int i = 0; i < 500000; i++) {
//Log.i(TAG, "======== ");
list1.add(i);
}
tv.setText(msg.getData().getString("title")); // 设置标题
iv.setImageResource(path[msg.arg1]); // 设置要显示的图片
}


然后运行TraceView得到下面的图:





发现这三个类的Incl Cpu Time 很高分别是55.6% 10.6% 和7.6%,但是你有发现他们每次的Incl Real Time大约分部是



1535 282 和 198.每次占用cpu时间很短。但是占用整个cpu运行周期的比例确实很高。这说明只有一种情况。这几个函数执行频率非常高。通过查看Call+Recur Calls/Total 发现情侣确实如我所料。



这里发现执行次数达到49947.而且函数大部分都是一次。这说明这几个函数运行次数太多。可以在实际开发过程中优化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  性能优化 android