bytex-referCheck原理解析

本文详细阐述了ByteX插件如何通过gradletransformapi和ASM技术,实现对工程构建产物的遍历、类图构建以及 ReferCheckPlugin 的合法验证过程,解决升级SDK后可能出现的 NoSuchMethodError 等问题。

一、背景及收益

升级sdk或更新依赖库后,可能因为库之间依赖的版本号不同,API有变动时会报:NoSuchMethodError 等错误

二、ByteX实现原理

ByteX是一个基于gradle transform api和ASM的字节码而实现的

三、bytex-referCheck 检测插件的整体实现思路:

  1. 将所有的子插件注册到宿主插件中,并给每个子插件绑定一个TransformFlow【默认为全局MainTransformFlow】
  2. 宿主插件的Transform方法中遍历执行每个子插件的TransformFlow执行run方法对.class进行处理:
    首先遍历工程中所有的构建产物(.class)和 遍历android.jar里的所有class文件生成类结构图【map:key(className),value(类/接口node)】;
    然后再遍历一次工程中所有的构建产物,将每个class文件传给bytex-referCheck子插件的transform方法处理
  3. 子插件ReferCheckPlugin的transform方法中结合第一次遍历的生成类图,与 ASM ClassVisitor 进行合法性验证

四、名词解释

  1. traverse过程:遍历一次工程中所有的构建产物(一般来说是class文件),单纯做遍历分析,不对输入文件做修改;
  2. traverseAndroidJar过程:遍历android.jar里的所有class文件 (哪个版本的android.jar由工程中的target api决定),主要是为了形成完整的类图.
  3. transform:再遍历一次工程中所有的构建产物,并对class文件做处理后输出(可能是直接回写到本地,也可能作为下一个处理过程的输入)
  4. 类图Graph:Graph里维护一个map集合 key为类名,value 是类或者接口节点

五、关键过程及代码

5-1:将子插件注册到bytex宿主插件中

首先在子插件的apply()方法中,将子插件注册到bytex宿主插件扩展类ByteXExtension中。

AbsPlugin.java

public final void apply(@NotNull Project project) {
   
   
   ...
   ByteXExtension byteX = project.getExtensions().getByType(ByteXExtension.class);
   // 向宿主插件 byteX  注册子插件,那么 宿主插件会持有 所有 注册的子插件的引用,通过 ByteXExtension 获取
   byteX.registerPlugin(this);
   ...

在bytex宿主插件中,获取宿主插件扩展ByteXExtension,并将扩展传入ByteXTransform

public class ByteXPlugin implements Plugin<Project> {
   
   
    @Override
    public void apply(@NotNull Project project) {
   
   
        // 获取 Android 为 App 提供的扩展属性 AppExtension 实例
        AppExtension android = project.getExtensions().getByType(AppExtension.class);
        // 获取 ByteX 自身创建的扩展属性 ByteXExtension 实例
        ByteXExtension extension = project.getExtensions().create("ByteX", ByteXExtension.class);
        // 注册 ByteXTransform 实例。ByteXTransform 继承了抽象类 CommonTransform,其实现了关键的 transform 方法
        android.registerTransform(new ByteXTransform(new Context(project, android, extension)));
    }
}

5-2:为每个子插件注册TransformFlow并执行run方法

从扩展中拿到所有子插件

   /**
     * 从扩展中获取 所有子插件的引用
     * @return
     */
    @Override
    protected List<IPlugin> getPlugins() {
   
   
        if (!context.extension.isEnable()) {
   
   
            return Collections.emptyList();
        }
        return context.extension.getPlugins();
    }

遍历插件注册TransformFlow

CommonTransform.java

private void transformInternal(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
   
   
          ...
          
plugins.forEach(plugin -> {
   
   
           manager.bind(new FlowBinder() {
   
   
               @Override
               public TransformFlow bind(TransformFlowerManager manager) {
   
   
                   // 调用子插件的registerTransformFlow 方法 传入全局公共的transform流MainTransformFlow
                   return plugin.registerTransformFlow(manager.getCommonFlow(), transformContext);
                }
           });
         }
);
         ...
}

5-3: 公共 MainTransformFlow.runTransform()遍历产物构建类图,以 遍历工程构建产物为例

遍历工程产物

traverseArtifactOnly(getProcessors(Process.TRAVERSE, new ClassFileAnalyzer(context, Process.TRAVERSE, graphBuilder, new ArrayList<>(handlers))));

getProcessors
@param process:Process.TRAVERSE遍历
@param fileHandler ClassFileAnalyzer类文件解析的
返回的是FileProcessor[]

 /**
     *  根据传入的状态  返回不同的文件处理器【4 种自定义的】 数组
     * @param process       不同的处理状态
     * @param fileHandler   根据不同处理状态  传入 不同的文件分析器 【FileHandler 子类: 1 ClassFileAnalyzer 类文件分析器  2 ClassFileTransformer 类文件转换器】
     * @return
     */
    private FileProcessor[] getProcessors(Process process, FileHandler fileHandler) {
   
   
        // 在 traverse(遍历) 和 transform(转换) 的过程中,加入自定义的 FileProcessor,提供更大的灵活性
        List<FileProcessor> processors = handlers.stream()
                .flatMap((Function<MainProcessHandler, Stream<FileProcessor>>) handler -> handler.process(process).stream())
                .collect(Collectors.toList());
        switch (process) {
   
   
            // 增量遍历状态
            case TRAVERSE_INCREMENTAL:
                // FilterFileProcessor:按照指定的条件过滤掉不需要的 FileData,此时的过滤条件是文件的状态不是 Status.NOTCHANGED
                processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED));
                // IncrementalFileProcessor 增量文件处理器,其中包含一个 ClassFileProcessor(FileHandler) 参数,用于对文件解析使用
                processors.add(new IncrementalFileProcessor(new ArrayList<>(handlers), ClassFileProcessor.newInstance(fileHandler)));
                break;
            case TRAVERSE:
            case TRAVERSE_ANDROID:
            case TRANSFORM:
                // ClassFileProcessor 类文件处理器,其中包含一个 ClassFileProcessor(FileHandler) 参数,用于对文件解析使用。
                processors.add(ClassFileProcessor.newInstance(fileHandler));
                // FilterFileProcessor 按照指定的条件过滤掉不需要的 FileData,此时的过滤条件是文件的状态不是 Status.NOTCHANGED 和 Status.REMOVED
                processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED && fileData.getStatus() != Status.REMOVED));
                break;
            default:
                throw new RuntimeException("Unknow Process:" + process);
        }
        return processors.toArray(new FileProcessor[0]);
    }

类型为:Process.TRAVERSE 则返回的是FileProcessor[]
index= 0-> FilterFileProcessor
else ->ClassFileProcessor

然后执行的TransformEngine,traverseOnly()方法

    /**
     *  cpu密集型 线程池 执行 遍历 任务
     * @param processors
     */
    public void traverseOnly(FileProcessor... processors) {
   
   
        Schedulers.FORKJOINPOOL().invoke(new PerformTraverseTask(context.allFiles(), getProcessorList(processors)));
    }

其中 传入的

执行的PerformTraverseTask的compute()方法

    @Override
    protected void compute() {
   
   
        List<FileTraverseTask> tasks = source.map(cache -> new FileTraverseTask(cache, processors)).collect(Collectors.toList());
        // 回调所有的 FileTraverseTask 实例的 compute 方法
        invokeAll(tasks);
    }

执行的是 FileTraverseTask.compute -> TraverseTask.compute-> ProcessorChain 链式任务
ProcessorChain.proceed方法

    @Override
    public Output proceed(Input input) throws IOException {
   
   
        if (index >= processors.size()) throw new AssertionError();
        // 1 index = 0 时 会从 processors 处理器列表中获取第一个处理器—FilterFileProcessor,并调用它的 process 方法
        // 2 index = 1 -> processors.size()-1 之前 时  是 ClassFileProcessor 则会执行  ClassFileProcessor.process()
        // 3 index = processors.size()-1  时 为 BackupFileProcessor 则执行 BackupFileProcessor.process()
        FileProcessor next = processors.get(index);
        return next.process(new ProcessorChain(processors, input, index + 1));
    }

遍历任务的话主要在ClassFileProcessor.process() 中
ClassFileProcessor.process

    @Override
    public Output process(Chain chain) throws IOException {
   
   
        Input input = chain.input();
        FileData fileData = input.getFileData();
        if (fileData.getRelativePath().endsWith(".class")) {
   
   
            // case 1、MainTransformFlow 里执行的是遍历任务 traverseArtifactOnly、traverseAndroidJarOnly的话 传入的 FileHandler 为 ClassFileAnalyzer 那么遍历任务会执行 ClassFileAnalyzer里的 handle方法处理
            // case 2、MainTransformFlow 里执行的是transform 任务: 传入的FileHandler 为 ClassFileTransformer 并且 如果 fileData 是.class 文件 ,则调用 ClassFileTransformer 的 handle 方法进行处理。
            handler.handle(fileData);
        }
        // 2、非 类 使用 FileProcessor 列表 最后一个 BackupFileProcessor  的 proceed  则 结束链式调用
        return chain.proceed(input);
    }

遍历任务看ClassFileAnalyzer的 handle方法

@Override
    public void handle(FileData fileData) {
   
   
        try {
   
   
            ...
            ...
            ...
            byte[] raw = fileData.getBytes();
            String relativePath = fileData.getRelativePath();
            // 构建 ClassReader 对象,解析文件
            ClassReader cr = new ClassReader(raw);
            int flag = getFlag(handlers);
            // 创建 ClassVisitorChain 对象,本质是 ClassWriter
            ClassVisitorChain chain = getClassVisitorChain(relativePath);
            // 如果类图构建器为空,则创建 GenerateGraphClassVisitor 类读取类文件,并构建类图
            if (this.mGraphBuilder != 
<think>好的,用户想了解ByteX的学习资料和使用教程。首先,我需要回顾提供的引用内容,找到相关的信息。引用1提到ByteX是字节跳动开发的字节码插件平台,基于GradleTransform API和ASM,适合Android开发。引用3和4有详细的开发者指南,包括环境搭建、插件开发步骤和调试方法。引用5提到有源码解和面试资料,可能对深入学习有帮助。用户可能需要入门教程、开发示例以及常见问题解决方法。接下来,我应该整理这些信息,分步骤回答。首先介绍ByteX是什么,然后提供官方文档和GitHub链接作为主要学习资料。接着,分点说明如何快速上手,比如环境搭建、插件开发流程,并引用相关引用中的内容。还要包括常见问题排查,比如插件不生效的情况,引用4提到了开启enable和添加日志。最后,推荐进阶学习资源,如源码分和社区讨论,引用5的源码解可能有帮助。需要确保回答结构清晰,使用中文,并且正确引用对应的引用编号。同时,生成相关问题,可能涉及开发环境配置、插件优化、与其他工具对比等。检查是否遗漏了用户可能关心的点,比如实际应用案例或性能优化技巧,但根据现有引用,可能没有更多信息。确保所有技术术语正确,比如ASM、GradleTransform API等,必要时进行简单解释。</think>### ByteX学习资料与使用教程 ByteX是字节跳动开源的**Android字节码插件平台**,基于Gradle Transform API和ASM实现,支持开发者通过插件化方式操作字节码。以下是系统化的学习路径: --- #### 一、核心学习资源 1. **官方文档与GitHub仓库** - GitHub地址:[bytedance/ByteX](https://github.com/bytedance/ByteX) 包含项目结构、基础API说明及插件开发示例[^1][^3]。 - 开发者指南:[ByteX Developer API](https://github.com/bytedance/ByteX/blob/master/wiki/ByteX-Developer-API-zh.md) 详细说明Transform流程、增量编译、日志调试等高级功能[^3]。 2. **快速上手步骤** - **环境搭建**:依赖Android Studio + Gradle,添加`com.bytedance.android.bytex`插件[^3]。 - **自定义插件**:继承`Plugin`类,实现`transform`方法操作字节码[^3]。 ```groovy class MyPlugin extends Plugin { @Override void apply() { android.registerTransform(new MyTransform()) } } ``` - **调试技巧**:启用`enableInDebug=true`并添加日志辅助排查问题[^4]。 --- #### 二、实战案例与进阶 1. **基础插件开发示例** - 实现方法调用替换、代码注入等(参考`method_call_opt`模块源码)[^2]。 - 示例:为方法添加Try-Catch块([AddTryCatchPlugin](https://github.com/bytedance/ByteX/tree/master/sample-plugin))[^4]。 2. **高级功能** - **增量编译**:通过`isIncremental()`感知代码变更,提升构建速度[^3]。 - **Graph API**:分类与方法依赖关系,实现精准代码修改[^3]。 --- #### 三、常见问题与排查 1. **插件不生效** - 检查`enable`和`enableInDebug`是否开启[^4]。 - 使用`FileProcessor`输出处理后的字节码进行对比[^3]。 2. **性能优化** - 避免在Transform中执行耗时操作,合理利用缓存[^5]。 --- #### 四、扩展学习 - **源码解**:参考社区文章《字节码插件平台ByteX源码解》[^5]。 - **ASM基础**:掌握`ClassVisitor`、`MethodVisitor`等API([ASM手册](https://asm.ow2.io/))。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值