引言
TraceView 是 Android SDK 提供的一个性能分析工具官网介绍 ,一般用来检查 UI 卡顿、分析 app 耗时操作。但是对于大多说 Android 开发来说,TraceView 的图形化页面只能让人感觉一脸懵逼!!!
本文重点
昨天在干货集中营 发现这一篇文章 一个能让你了解所有函数调用顺序以及函数耗时的Android库(无需侵入式代码) 文里面提到可以使用 dmtracedump 命令把 trace 文件转为 txt ,然后再从 txt 里面提取我们需要的信息。
以上为本文全部主要内容,下面的啰嗦都是对以上内容的具体阐释。
获得 trace 文件
通过点击 DDMS start Method Profiling (第一次点击开始,第二次点击结束)
在代码中通过使用
android.os.Debug.startMethodTracing()
android.os.Debug.stopMethodTracing()
生成 traceView ,然后通过下面的 adb pull 命令导出 traceView
在 AndroidStudio 中获取,这个是本文重点,上面两种方式纯属为了凑数
点击 monitors cpu 模块的小闹钟(第一次点击开始,第二次点击结束)
然后 trace 文件会被存放在项目根目录的 captures 目录。
在项目根目录的 gradle 文件中增加一个 task
task AppOutPutMethodOrder() {
doLast {
def capturesDirPath = project.getProjectDir().path + File.separator + "captures";
def capturesDir = new File(capturesDirPath);
capturesDir.traverse {
if (it.isFile() && it.name.endsWith(".trace")) {
def orderName = it.name.replace("trace", "txt")
def orderFile = new File(capturesDirPath, orderName)
orderFile.write("")
def dmtracedumpDir = getDmtraceDumpDir();
//说明:dmtracedump 为 android sdk自带工具,要执行dmtracedump命令则需要先添加环境变量
def baseComand = dmtracedumpDir + "dmtracedump -ho " + it.absolutePath + " >> " + orderFile.absolutePath
println baseComand
String osNameMatch = System.getProperty("os.name").toLowerCase();
if (osNameMatch.contains("windows")) {
("cmd /c start /b " + baseComand).execute()
} else {
["bash", "-c", baseComand].execute()
}
}
}
}
}
以上代码来源一个能让你了解所有函数调用顺序以及函数耗时的Android库(无需侵入式代码)
执行命令
gradlew AppOutPutMethodOrder
利用 dmtracedump 命令把 trace 文件转为 txt 文件
txt 文件比较大,AndroidStudio 自带的文本阅读器打不开,需要用其他工具打开(这里使用的是 sublime Text)下面是截取的一部分 txt 内容
这个文件有下面几个重要信息
1. VERSION ~ Trace (1~29行)是线程信息,前面是线程 id 后面是 线程名称。
2. Trace 以后是方法信息。
第一部分是线程 id
第二部分 ent 代码方法开始执行,xit 表示方法执行完毕
第三部分表示当时的时间戳,单位是微秒(μs)
第四部分表示方法名,参数类名
提取需要的信息
在 gradle 再增加一个 task
task AppFilterMethodOrder() {
doLast {
//TODO 替换为你想要过滤的包名
def filterPackageName = "com.xxx.xxx"
if (project.hasProperty("package_name")) {
filterPackageName = project.getProperty("package_name")
}
//处理包名
def filterSignature = filterPackageName.replaceAll("[.]", "/")
def capturesDirPath = project.getProjectDir().path + File.separator + "captures";
def capturesDir = new File(capturesDirPath);
capturesDir.traverse {
if (it.isFile() && it.name.endsWith(".txt") && !it.name.contains("--filter")) {
def orderName = it.name.replace(".txt", "--filter.txt")
def orderFile = new File(capturesDirPath, orderName)
orderFile.write("")
def map = new HashMap();
it.eachLine { line ->
if (line.contains(filterPackageName) || line.contains(filterSignature)) {
if (line.contains("ent")) {
line = line.replaceAll("[.]{2,}+", "")
line = line.replaceAll("[ ]{2,}+", " ")
def item = line.split(" ");
map.put(item[3], item[2])
} else if (line.contains("xit")) {
line = line.replaceAll("[.]{2,}+", "")
line = line.replaceAll("[ ]{2,}+", " ")
def item = line.split(" ");
// for (int i = 0; i < item.length; i++) {
// println "--" + i + ":" + item[i]
// }
println map.size()
// println map.containsKey(item[3])
if (map.containsKey(item[3])) {
println item[3] + " 耗时:" + (item[2].toInteger() - map.get(item[3]).toInteger())/1000 + " 毫秒"
orderFile.append("耗时:\t\t" + (item[2].toInteger() - map.get(item[3]).toInteger())/1000 + " 毫秒" + "\t\t" + item[3])
orderFile.append("\n")
map.remove(item[3])
}
}
}
}
}
}
}
}
执行代码
gradlew AppFilterMethodOrder -P package_name=com.xxx.xxx
这个 task 中,我们根据包名提取需要的信息得到