AddTryCatch gradle plugin 编译期给代码加try catch的插件

AddTryCatch插件可在编译期为代码添加tryCatch,有效处理第三方库引发的崩溃问题。支持自定义异常处理逻辑,适用于Android开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AddTryCatch插件的作用——编译期加tryCatch,支持向第三方库中加tryCatch

在平时开发过程中,无可避免的会遇到crash,如果是自己写的代码里抛出异常还好,把相关代码逻辑改好即可。但是遇到集成到项目中的第三方库抛出异常,又无法通过自己可修改的代码部分规避问题,就只能依赖第三方库开发者修复问题,再发布新版本了。
而现实中遇到的第三方库,能在提issue后快速修复问题并发布新版本的实在是少之又少。这种情况下,我们就十分被动了。

每当这时候,我就在想:如果能修改第三方库中的代码就好了! 不求能精准修改逻辑,仅仅是能加上try catch就不错了,如果能在catch后捕获到异常,能再调用自定义的代码处理该异常,就更好了!

而这个AddTryCatch,就是为此而生的。

这个插件,可以通过简单的配置,做到在编译期修改字节码的效果。
因为是在编译期在字节码的层面上修改,所以不管是自己写的代码,还是引用的第三方库中的代码,都可以修改

项目地址:https://github.com/xingchenxuanfeng/AddTryCatchPlugin

AddTryCatch插件的使用

该插件的使用方式很简单,只需要两部:
  1. AddTryCatch插件发布在了jitpack上,使用时需要先把jitpack加入到buildScript 的repositories中,并在dependencies 中加入classpath 'com.github.xingchenxuanfeng:AddTryCatchPlugin:1.0.1' 。代码如下:
buildscript {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
    dependencies {
        ...
        classpath 'com.github.xingchenxuanfeng:AddTryCatchPlugin:1.0.1'
    }
}
  1. 在app moudle级别的build.gradle中apply plugin: 'add-trycatch',然后按如下格式进行配置。
apply plugin: 'add-trycatch'

addTryCatch {
    hookPoint = [
            "com.addtrycatchplugin.TestCrash1" : [
                    "crashMethod1",
                    "crashMethod2"
            ],
            "com.addtrycatchplugin.TestCrash2": [
                    "crashMethod1",
                    "crashMethod2"
            ]
    ]
    exceptionHandler = ["com.addtrycatchplugin.ExceptionUtils": "uploadCatchedException"]
}

按照上面的写法配置好后,插件就可以正常工作了。

参数解释:

下面我来解释一下各个参数的具体意义。

hookPoint

hookPoint表示需要注入tryCatch的代码,hookPoint声明的数据结构是Map<String, List<String>>

上面的配置表示,在com.addtrycatchplugin.TestCrash1这个类的crashMethod1和crashMethod2两个方法,与com.addtrycatchplugin.TestCrash2这个类的crashMethod1和crashMethod2两个方法,共计四个方法中加入tryCatch。在方法的第一行加入try关键字,在方法最后一行加入catch关键字和catch代码块。

exceptionHandler

exceptionHandler表示在catch到异常后,会执行的异常处理方法(常见的处理策略是上报到统计平台以供分析)。com.addtrycatchplugin.ExceptionUtils是处理方法的类名,uploadCatchedException是方法名。(该参数可以为空,表示catch到异常后,不做任何处理,忽略该异常)

下面是插件效果:

原始代码: 三个类 异常处理类 ExceptionUtils。java,和要被修改的类,TestCrash1.java,TestCrash2.java

ExceptionUtils.java

package com.addtrycatchplugin;

import android.util.Log;

public class ExceptionUtils {
    public static void uploadCatchedException(Exception exception) {
        if (exception == null) {
            return;
        }
        //demo里没有接入异常上报平台,仅仅打了log来测试是否捕获成功
        Log.e("ExceptionUtilsTAG", "uploadCatchedException", exception);
    }
}

TestCrash1.java

package com.addtrycatchplugin;

public class TestCrash1 {
    public static void crashMethod1() {
        int a = 1 / 0;
    }

    public static void crashMethod2() {
        int a = 1 / 0;
    }
}

TestCrash2.java

package com.addtrycatchplugin;

class TestCrash2 {
    public static void crashMethod1() {
        int a = 1 / 0;
    }

    public static void crashMethod2() {
        int a = 1 / 0;
    }
}
生成后的代码:TestCrash1.class 和TestCrash2.class

TestCrash1.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.addtrycatchplugin;

public class TestCrash1 {
    public TestCrash1() {
    }

    public static void crashMethod1() {
        try {
            int var0 = 1 / 0;
        } catch (Exception var2) {
            ExceptionUtils.uploadCatchedException(var2);
        }
    }

    public static void crashMethod2() {
        try {
            int var0 = 1 / 0;
        } catch (Exception var2) {
            ExceptionUtils.uploadCatchedException(var2);
        }
    }
}

TestCrash2.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.addtrycatchplugin;

public class TestCrash2 {
    public TestCrash2() {
    }

    public static void crashMethod1() {
        try {
            int var0 = 1 / 0;
        } catch (Exception var2) {
            ExceptionUtils.uploadCatchedException(var2);
        }
    }

    public static void crashMethod2() {
        try {
            int var0 = 1 / 0;
        } catch (Exception var2) {
            ExceptionUtils.uploadCatchedException(var2);
        }
    }
}

插件原理:

利用gralde transform api在编译流程中加入自定义的transform任务,然后在自定义的transform任务中,使用asm修改字节码来达到注入try catch代码的目的。同时使用Hunter框架来优化transform任务运行效率,简化代码逻辑。

asm

ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建自定义复杂转换和代码分析工具。ASM提供与其他Java字节码框架类似的功能,但专注于 性能。因为它的设计和实现尽可能小而且快,所以它非常适合在动态系统中使用(当然也可以以静态方式使用,例如在编译器中)。
目前已广泛应用于众多著名项目,如OpenJDK,Groovy编译器,Kotlin编译器Gradle等。

Gradle transform api

Gradle Transform是Android官方提供给开发者在项目构建阶段中由class到dex转换之前修改class文件的一套api。目前比较经典的应用是字节码插桩、代码注入技术。

Hunter

Hunter: 一个插件框架,在它的基础上可以快速开发一个并发、增量的字节码编译插件,帮助开发人员隐藏了Transform和ASM的绝大部分逻辑,开发者只需写少量的ASM code,就可以开发一款编译插件,修改Android项目的字节码。

插件实现:

具体实现见下期:
一步步实现AddTryCatch插件 —— gradle transform和ASM实践

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值