6360eb29321452fbd9f6e78ee28818ad
Android App调试一个奇巧淫技

前言

不知道同学们有没有遇到这些时候:

1.需要在某个时刻,获取某个本地数据,而重新走流程debug又比较麻烦;
2.你需要临时清理一个数据,但app当前流程,并不提供这样的操作;
3.想在程序加段代码,代码要依赖app当前状态,但不知道代码跑不跑得通,于是在某处加入代码,编译运行,走流程...如果代码失败,还得重复上述步骤;
4.等等...

简单地说,就是想在app运行的某个时刻,运行一小段代码,于是不得不重新编译、运行、走流程。当然场景还有很多很多,不一一而足。

本篇笔者介绍一个方法,可以让你遇到这些情况,有更轻松、灵活地调试app。


一个场景

调试需求:

1.浏览一个app页面,先显示本地缓存,再请求接口更新数据,并显示;
2.当有bug发生,需要重新走一遍这个流程debug;
3.必须先清空本地缓存,再重新走流程。

问题:

如何清空本地缓存?

方案:

1.在系统清除整个app数据,并重新登录、走流程;
2.在代码中加入清空缓存逻辑,重新编译运行,并在某个事件下触发这段逻辑,并且判断BuildConfig.DEBUG==true才能执行(或者上线前注释掉);
3.写一段清空缓存代码,能立即在app上执行,并不影响原来代码。

探讨方案:

1.方案一,最笨拙的方法,好处是不需要写任何代码。坏处是每次需要重新登陆、走流程。如果流程太长,花费的时间很多。

2.方案二,好处是不用重新登陆、走流程,写一次代码,以后调试都能用,多次调试效率相对高。坏处一,第一次调试时,比方案一多花时间,并且重新编译运行app,这里也花费时间,并且不知道调试代码是否有bug,如果有bug还得改代码、编译、运行。坏处二,调试代码入侵到原代码,必须小心对待,以防在上线时有影响。

3.方案三,好处是调试代码不入侵原代码,不需要重新编译运行app(当然编译调试代码也要耗几秒钟)。不足,需要做一点准备工作。

方案三就是笔者今天介绍的“奇巧淫技”。


实现思路

我们的需求是:

临时在app上跑一段代码,并且不入侵原代码,不需要重新编译运行app。

实现思路:

1.调试代码写成单元测试(或者java main函数);
2.编译、打包成dex文件;
3.发送dex给app;
4.app执行代码。

相信聪明的同学,看到思路已经廓然开朗了;同时,笔者也相信很多同学直接滚到下面点demo链接.....下面给大家讲讲代码。

代码

1.写调试代码

.../test/com/example/dex,写DexTask类,继承Runnable

package com.example.dex;

public class DexTask implements Runnable {
    @Override
    public void run() {
        System.out.println("DexTask running...");
    }
}

run()会被app执行。

2.编译、打包dex

例如,工程包名com.example.dex。单元测试代码在src/main/test/java目录。

单元测试目录

单元测试目录

那么,编译后的单元测试class文件,在build/intermediates/classes/test/debug目录。

单元测试class文件目录

单元测试class文件目录

class打包jar

用shell命令,将build/intermediates/classes/test/debug/目录打包成myjar.jar

String dir = new File("build/intermediates/classes/test/debug").getAbsolutePath();

Bash bash = new Bash();
bash.cd(dir);
bash.exec("jar -cvf myjar.jar .");

(Bash是笔者写的一个工具类)

jar编译成dex

使用android sdk的Dx工具命令,将myjar.jar编译成dex.jar

> $ANDROID_HOME/build-tools/27.0.1/dx --dex --output=dex.jar myjar.jar

注意,更改目录为sdk存在的build-tools版本dx路径。笔者最新到27.0.1,读者可能是其他版本(demo中会自动获取本地最新build-tools版本)。

java代码:

Dx     dx      = new Dx();
String dexPath = dx.dx(dir + "/myjar.jar", "dex.jar");

Dx是笔者封装的dx工具类。

编译jar、dex后,build/intermediates/classes/test/debug存在这两个文件:

(demo中,每次执行完就删掉myjar.jar和dex.jar)

3.发送dex到app

app监听端口

app启动一个Service,用ServerSocket监听某端口(demo用10086端口做例子):

top Created with Sketch.