Android函数调用顺序库——AppMethodOrder使用介绍

来源:互联网 发布:ubuntu 网络映射 编辑:程序博客网 时间:2024/06/05 06:27

背景:当项目代码量很大的时候,或者你作为一名新人要快速掌握代码的时候,给函数打上log,来了解代码执行逻辑,这种方式会显然成本太大,要改动项目编译运行,NO!太耗时;或者你想debug的方式来给你想关注的几个函数,来了解代码执行逻辑,NO!因为你肯定会漏掉函数;也许你可以固执的给你写的项目打满log说这样也行,但是你要知道你方法所调用的jdk的函数或者第三方aar或者jar再或者android sdk中的函数调用顺序你怎么办,还能打log吗?显然不行吧,来~这个项目给让可以让你以包名为过滤点过滤你想要知道所有函数调用顺序。

注:本文重点介绍AppMethodOrder的使用方法,想探究其原理的同学可以去此项目的github主页https://github.com/zjw-swun/AppMethodOrder查看。

一、认识AppMethodOrder

AppMethodOrder是一个android第三方库,github地址https://github.com/zjw-swun/AppMethodOrder,它无需侵入式代码即可让你了解所有函数的调用顺序以及函数耗时。

二、如何使用

1. 新建工程

工程很简单,要实现的功能使在activity上点击button,然后弹出toast。
layout.xml内容如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.bjut.appmethod.MainActivity">    <Button        android:id="@+id/btn"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="点我"/></LinearLayout>

MainActivity如下:

public class MainActivity extends AppCompatActivity {    private Button mButton;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mButton = (Button) findViewById(R.id.btn);        mButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Toast.makeText(MainActivity.this, "点击了", Toast.LENGTH_SHORT).show();            }        });    }}

2. 在工程根目录下新建utils.gradle文件

File->New->File->utils.gradle,将以下代码复制到utils.gradle中,* 特别注意: 第71行 “def filterPackageName = “com.bjut.appmethod””这一行,要将”com.bjut.appmethod”改成你自己的包名。 *

//核心任务:在captures文件目录下输出 基于最新.trace文件的函数调用信息的txt版本//说明:dmtracedump 为 android sdk自带工具,要执行dmtracedump命令则需要先添加环境变量task AppOutPutMethodOrder() {    doLast {        def capturesDirPath = project.getProjectDir().getParentFile().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()                }            }        }    }}/** * read the sdk dir from local.properties * eg : *  sdk.dir = /home/env/sdk *  so: *   dmtracedump.dir = /home/env/sdk/platform-tools * * @return the dir which dmtracedump tools exists */def getDmtraceDumpDir() {    def rootDir = project.rootDir    def localProperties = new File(rootDir, "local.properties")    def sdkDir = null;    if (localProperties.exists()) {        Properties properties = new Properties()        localProperties.withInputStream { instr ->            properties.load(instr)        }        sdkDir = properties.getProperty('sdk.dir')    }    if (sdkDir == null || !(new File(sdkDir).exists())) {        sdkDir = android.getSdkDirectory().getAbsolutePath()    }    if (sdkDir == null || !(new File(sdkDir).exists())) {        sdkDir = android.plugin.getSdkFolder().getAbsolutePath()    }    def dmtraceDumpToolDir = sdkDir + File.separator + "platform-tools" + File.separator    if (new File(dmtraceDumpToolDir).exists()) {        return dmtraceDumpToolDir;    }    return ""}//这里AppFilterMethodOrder 任务其实也不需要 执行找到 \captures 目录找到 base_order.txt//用Notepad++ 使用正则 先过滤 带 xit 的行 (我们只关注ent 行就行,ent代表进入执行函数 xit代表退出函数)再过滤掉你不关心的包名// Notepad++ 中过滤将会使用到的命令行如下//^.*xit.*$ //去除掉 含有 xit 字符串的行  然后替换为空// ^((?!XXX).)*$  //去除不包含XXX的行  然后替换为空//^\s+   //合并空行  然后替换为空task AppFilterMethodOrder() {    doLast {        //TODO 替换为你想要过滤的包名        def filterPackageName = "com.bjut.appmethod"        if (project.hasProperty("package_name")) {            filterPackageName = project.getProperty("package_name")        }        //处理包名        def filterSignature = filterPackageName.replaceAll("[.]", "/")        def capturesDirPath = project.getProjectDir().getParentFile().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 orderTimeName = it.name.replace(".txt", "--timefilter.txt")                def orderFile = new File(capturesDirPath, orderName)                orderFile.write("")                def orderTimeFile = new File(capturesDirPath, orderTimeName)                orderTimeFile.write("")                it.eachLine { line ->                    if (line.contains(" ent ")                            //兼容不同版本traceview 有的是方法包名有的是方法签名                            && (line.contains(filterPackageName)                            || line.contains(filterSignature))                    ) {                        orderFile.append(line + "\n")                    }                    //生成带xit 和 ent 的trace行 函数耗时计算方式: xit字符后 数值 减去 ent字符后的 数字 (差值就是耗时 单位:微妙)                    //注意:好像函数体中含Thread.sleep的计算不准确                    if (line.contains(filterPackageName)                            || line.contains(filterSignature)){                        orderTimeFile.append(line + "\n")                    }                }            }        }    }}

3. 修改根目录下build.gradle(Project AppMethod),如下图所示:这里写图片描述

4. 运行项目,点击CPU右侧的时钟按钮,如图:

这里写图片描述
点击button,弹出toast后再次点击时钟按钮。然后android studio为我们生成一个后缀名为.trace的文件。如图:
这里写图片描述

5. 在IDE右侧找到Gradle面板,

这里写图片描述
找到AppFilterMethodOrder和AppOutPutMethodOrder两个方法。
这里写图片描述
双击AppOutPutMethodOrder方法,可以看到在captures目录下生成了以时间命名的txt文件,如图:
这里写图片描述
这个文件记录的是未经过过滤的所有函数的调用顺序。
双击AppFilterMethodOrder方法,生成如图文件:
这里写图片描述
分别是按你地包名过滤后的方法调用文件以及函数耗时文件。
接下来就可以在txt文件中查看方法调用顺序了。

1 0