AGP4 Transform 小结

基于AGP的Android组件化编译插件实践 作者:nebulae-pan 概述 组件化算是大型项目的一种较好的组织方案,可以很好解藕逻辑,单独编译需求模块方便测试复用,减少维护成本,甚至良好组件化的项目可以无痛过度到插件化。但是其也有相应的缺点,比如模块间需要额外通信手段导致编写复杂效率降低,不同模块需根据版本依赖并作额外管理。 为了开发编译插件帮助项目达成解藕、复用、单独编译的目的,一般情况下要提供以下几种功能: 单独运行(支持module作为App运行) 不同module代码资源隔离(或者仅仅暴露指定接口) 资源增加前缀以减少冲突 Appl. 阅读详情

整理一下 Transform,仅供参考

注册 Transform

首先是注册 Transform appExtension.registerTransform

class PrimerPlugin implements Plugin<Project> {

	@Override
    void apply(Project project) {
		mPrimerTransform = new PrimerTransform()
		if(isAppExtension(project)){
			def appExtension = project.extensions.getByType(AppExtension)
        	appExtension.registerTransform(mPrimerTransform)
		}
	}

	//当前是应用程序构建扩展(应用程序-可运行模块项目),区别于 LibraryExtension 库项目
	boolean isAppExtension(Project project) {
        try {
            project.extensions.getByType(AppExtension)
        } catch (Exception e) {
            return false
        }
        return true
    }
}

确定 Transform

Transform 有几个重要方法需要重写

  • 明确构建过程处理的输入是什么
  • 确定处理范围是什么
  • 以及最重要的如何处理

其中关键是 transform 方法的重写

class PrimerTransform extends Transform {

    PrimerTransform() {
    }

	//给当前 Transform 起一个响亮的名字
    @Override
    String getName() {
        return PrimerTransform.class.getSimpleName()
    }

	/*
	* 处理文件的类型
	* 
	* CONTENT_CLASS: class 文件
	* CONTENT_JARS:jar 文件
	* CONTENT_RESOURCES:资源文件
	* CONTENT_NATIVE_LIBS:so 库文件
	* CONTENT_DEX:dex 文件
	*/
    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS
    }

	
	/***
	 * 作用范围
	 * 
	 * PROJECT: 主项目
	 * SUB_PROJECTS: 子项目
	 * EXTERNAL_LIBRARIES: 外部库
	 *
	 */
    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }

	//是否增量编译,true:只处理上一次构建更改的文件,一定程度提高构建速度
    @Override
    boolean isIncremental() {
        return false
    }

	//明确如何处理?处理什么?  一般使用 invocation.getInputs 获取所有参与构建的输入
    @Override
    void transform(TransformInvocation invocation) throws IOException {
        TransformOutputProvider outputProvider = invocation.getOutputProvider()

        //处理上一个 transform 的所有输入,包括目录和 jar 文件
        invocation.getInputs().forEach(new Consumer<TransformInput>() {
            @Override
            void accept(TransformInput transformInput) {
                //处理所有 jar
                transformInput.getJarInputs().forEach(new Consumer<JarInput>() {
                    @Override
                    void accept(JarInput jarInput) {
                    	//后去输出文件,对 jar 文件处理完毕之后需要把最终结果输出到该文件
                        File destFile = outputProvider.getContentLocation(jarInput.getName(),
                                jarInput.getContentTypes(), 
                                jarInput.getScopes(),
                                Format.JAR)
                        try {
                            processJarFile(jarInput, destFile)
                        } catch (Exception e) {
                            CmdColorPrintUtils.outputRed("check plugin transform getInputs error = " + e)
                        }
                    }
                })

                //处理所有目录(目录下包含 class 文件)
                transformInput.getDirectoryInputs().forEach(new Consumer<DirectoryInput>() {
                    @Override
                    void accept(DirectoryInput directoryInput) {
                        //后去输出文件,对 jar 文件处理完毕之后需要把最终结果输出到该文件
                        File destDir = outputProvider.getContentLocation(directoryInput.getName(),
                                directoryInput.getContentTypes(),
                                directoryInput.getScopes(), 
                                Format.DIRECTORY)
                                
                        //计算相对路径
                        Path sourcePath = directoryInput.getFile().toPath()
                        Path destPath = destDir.toPath()
                        
                        try {
                        	//遍历输入目录,
                            Files.walk(sourcePath)
                                    .sorted(Comparator.naturalOrder())//按照自然顺序,也可以倒序
                                    .forEach {
                                        Path relativePath = sourcePath.relativize(it)
                                        Path targetPath = destPath.resolve(relativePath)
                                        //是目录,在该 Transform 路径下创建同名目录,否则进一步处理文件
                                        if (Files.isDirectory(it)) {
                                            Files.createDirectories(targetPath)
                                        } else if (Files.isRegularFile(it)) {
                                            processDirClassFile(it.toFile(), targetPath.toFile())
                                        }
                                    }
                        } catch (IOException e) {
                            CmdColorPrintUtils.outputRed("check plugin transform getDirectoryInputs error = " + e)
                        }
                    }
                })
            }
        })
    }
}

处理目录下的 class:transformInput.getDirectoryInputs()

    /**
     *
     * @param rawClassFile 目录下的 class 文件
     * @param outputFile 下一个 transform 的输出文件
     */
    private void processDirClassFile(File rawClassFile, File outputFile) {
        //确保输出路径已创建,否则目录不存在导致下面文件创建失败
        def parentFile = outputFile.getParentFile()
        if (parentFile.isDirectory() && !parentFile.exists()) {
            parentFile.mkdirs()
        }

        def rawInputStream = new FileInputStream(rawClassFile)
        byte[] originBytes = IOUtils.toByteArray(rawInputStream)
        //只处理 class 字节码文件,非 class 文件不做处理原样输出
        if (!rawClassFile.getName().endsWith(".class")) {
            Files.write(Paths.get(outputFile.getPath()), 
            	originBytes,
                StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
            return
        }

        //byte[] -> asm -> byte[],通过 asm 操作最终获得新的字节数组,便是修改后的 classes 文件
        byte[] newBytes = processClassFileByAsm(originBytes, rawClassFile.getName())

        //使用 StandardOpenOption.CREATE:如果文件不存在则创建
        //使用 StandardOpenOption.TRUNCATE_EXISTING:如果文件存在则覆盖(不要使用错了导致奇怪的打包问题)
        Files.write(Paths.get(outputFile.getPath()), newBytes,
                StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
    }

处理 jar 文件:transformInput.getJarInputs()

	private void processJarFile(JarInput jarInput, File destFile) throws Exception {
        //只处理 jar 文件(假如有非 jar 文件也别忘了原样输出)
        def inFile = jarInput.getFile()
        if (!inFile.getName().endsWith(".jar")) {
            return
        }

        JarOutputStream outputStream = new JarOutputStream(new FileOutputStream(destFile))
        JarFile inJarFile = new JarFile(jarInput.getFile())
        def rawEntries = inJarFile.entries()

        //jar 本省就是一个压缩包,遍历他的每一个 class
        while (rawEntries.hasMoreElements()) {
            def element = rawEntries.nextElement()
            def elementName = element.getName()
            outputStream.putNextEntry(new JarEntry(element.getName()))

            def elementInStream = inJarFile.getInputStream(element)
            //转成 byte 数组输入到 asm
            byte[] originBytes = IOUtils.toByteArray(elementInStream)

            //byte[] -> asm -> byte[],通过 asm 操作最终获得新的字节数组便是修改后的 classes 文件
            //交给 asm 的,我们只处理 class 文件,别忘了 jar 里面什么都可能有
            //这里只是简单的通过文件后缀判断,其实真正应该是通过 class 文件魔数 (magic number) 判断
            if (elementName.endsWith(".class")) {
            	//这里面主要是实现自己的逻辑了,找到那个 class 文件是你需要处理的目标文件
                if (CommUtils.trimEquals(AsmConstant.CLASS_PATH_GAME_MANAGER, elementName)) {
                	//拿到目标类的字节数组进行 asm 操作
                    checkGameManagerMethod(originBytes)
                }
                int index = elementName.lastIndexOf(".class")
                if (index > 0) {
                    def className = elementName.substring(0, index).replaceAll("/", ".")
                    if (CommUtils.trimEquals(className, CheckPluginManager.sInstance.getGameActivityName())) {
                        CmdColorPrintUtils.outputGreen("找到 jar 里面需要处理的类:" + elementName)
                        //拿到目标类的字节数组进行 asm 操作
                        originBytes = asmProcess(originBytes)
                    }
                }
            }

            outputStream.write(originBytes)
            outputStream.closeEntry()
        }

        outputStream.close()
        inJarFile.close()
        Files.write(Paths.get(inFile.getPath()), IOUtils.toByteArray(new FileInputStream(destFile)),
                StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
    }

判断一个文件是否是 class 文件:

 public boolean isClassFile(String filename) {
        if (filename == null || filename.trim().isEmpty() || !filename.endsWith(".class")) {
            return false;
        }

        try (DataInputStream dis = new DataInputStream(new FileInputStream(filename))) {
            return dis.readInt() == 0xCAFEBABE;
        } catch (IOException e) {
            return false;
        }
    }

ASM 操作

Android 打包构建过程伴随着 class 字节码的转换,通常在其中引入字节码框架进一步处理字节码以满足需求,字节码框架有很多选择:

举一个简单的例子:

判断类里面是否有dispatchTouchEvent(MotionEvent ev) 方法

	private void checkGameManagerMethod(byte[] originBytes) {
        //拷贝数组,不影响原数组数据
        def copyOf = Arrays.copyOf(originBytes, originBytes.length)
        ClassReader classReader = new ClassReader(copyOf)
        int option = 0
        classReader.accept(new ClassVisitor(Opcodes.ASM9) {
            @Override
            MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                //不处理构造方法
                if (!name.equals("<init>")) {
                    boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0
                    boolean isNativeMethod = (access & ACC_NATIVE) != 0
                    //不处理 native 方法和抽象方法
                    if (!isAbstractMethod && !isNativeMethod) {
                    	//判断该类里面是否有方法 dispatchTouchEvent(MotionEvent ev)
                        if (CommUtils.trimEquals(name, "dispatchTouchEvent") && CommUtils.trimEquals("(Landroid/view/MotionEvent;)V", descriptor)) {
                            CmdColorPrintUtils.outputGreen("找到 dispatchTouchEvent 方法")
                        }
                    }
                }
                return super.visitMethod(access, name, descriptor, signature, exceptions)
            }
        }, option)
    }

修改类的静态代码块中某字符串常量

静态代码块的静态初始化方法<clinit>,看 java 代码是这样的:

在这里插入图片描述

使用 jadx 查看 smali 代码是这样的:

在这里插入图片描述

除此之外还有一个实例初始化方法<init>

/**
 * 访问静态代码块
 */
public class StaticBlockClassVisitor extends ClassVisitor {

    private String mMapping, mRawName;

    /**
     * @param classVisitor
     * @param rawName      映射前的值
     * @param mapping      映射值
     */
    public StaticBlockClassVisitor(ClassVisitor classVisitor, String rawName, String mapping) {
        super(Opcodes.ASM9, classVisitor);
        this.mMapping = mapping;
        this.mRawName = rawName;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
		//<clinit> 就是类的静态构造方法
        if ("<clinit>".equals(name)) {
            return new MethodVisitor(Opcodes.ASM9, mv) {
                @Override
                public void visitLdcInsn(Object value) {
                    if (value instanceof String
                            && value != null
                            && mMapping != null
                            && CommUtils.trimEquals((String) value, mRawName)) {
                        CmdColorPrintUtils.outputGreen("修改常量映射 :" + value + " -> " + mMapping);
                        //进行映射替换
                        super.visitLdcInsn(mMapping);
                    } else {
                        super.visitLdcInsn(value);
                    }
                }
            };
        }
        return mv;
    }
}
“终于懂了” 系列:组件化框架 ARouter 完全解析(三)AGP/Transform/ASM—动态代码注入 本篇内容较多,前半部分都是在介绍铺垫知识,只有完全理解了才能掌握ARouter框架的核心。其中Gradle、AGP是Android开发中必定使用到的技术点,希望通过对ARouter的学习能更加深刻的理解Android编译构建的过程,以及使用Gradle插件能完成哪些事情。Transform是官方Gradle 插件中提供的API,它能获取和处理被打进dex文件之前的class文件。不过Transform在最新的Gradle插件版本中被废弃了,不过这不影响我们我们对它的学习。 阅读详情

相关推荐

绝缘检测是什么? 绝缘检测原理 绝缘检测检测的是什么?那个量?电压?电流?电阻?

绝缘电阻是电气设备和电气线路最基本的绝缘指标。对于低压电气装置的交接试验,常温下电动机、配电设备和配电线路的绝缘电阻不应低于0.5MΩ(对于运行中的设备和线路,绝缘电阻不应低于1MΩ/kV)。 绝缘电阻的正确测量方法 现代生活日新月异,人们一刻也离不开电。在用电过程中就存在着用电安全问题,在电器设备中,例如电机、电缆、家用电器等。 影响因素 环境温湿度 一般材料的绝缘电阻值随环境温湿度的升高而减小。相对而言,表面电阻(率)对环境湿度比较敏感,而体电阻(率)则对温度较为敏感。 湿度增加,表面泄漏增大,导体电导

dianqicyuyan的博客 2万+

AGP Transform API 被废弃意味着什么?

前几天 AGP 7.0 正式发布,细心的同学可能已经发现 从 AGP 1.3 一直存在的 Transform API 被标记为废弃了,但从注释中并没有说明应该用哪个 API 来替代,发现了这个问题,有同学就不淡定了,连 AGP 中最稳定的 Transform API 都被废弃了,以后是不让用字节码插桩了吗? 初见端倪 从 AGP 4.2 开始,就出现了几个跟操作字节码相关的新 API ,比如:com.android.gradle.instrumentation包下的AsmClassVisitorFa..

chuyouyinghe的专栏 773

【多模态融合】TransFusion学习笔记(1)

工作上主要还是以纯lidar的算法开发,部署以及系统架构设计为主。对于多模态融合(这里主要是只指Lidar和Camer的融合)这方面研究甚少。最近借助和朋友们讨论论文的契机接触了一下这方面的知识,起步是晚了一点,但好歹是开了个头。下面就借助TransFusion论文及其开源代码来串一串这方面的知识。这个很好理解,参考FPointNet网络图示。就是利用现有的2D检测器在图像上提取2D初始框,根据Camera成像原理,捕获2D框视锥内的点云,再利用点云分类模型(如:PointNet)做进一步分类。

4964

刚学会Transform,你告诉我就要被移除了

已经2022年了,作为一名Android工程师,我相信大家都知道Transform是什么,如果有人不知道,也没关系,因为Transform已经快过时了,按照惯例,这里我还是要介绍一下TransformAGP包含了一个TransformAPI,它允许第三方插件在编译后的class文件转换为dex文件之前做处理操作.而使用TransformAPI,我们完全可以不用去关注相关task的生成与执行流程,我们可以只聚焦在如何对输入的class文件进行处理工作时机处理对象Transform链。.........

bugyinyin的博客 1969

2025.06.24【R语言】|clusterProfiler安装与常见报错FAQ全解

本文介绍了clusterProfiler工具的安装方法及常见报错解决方案。安装可通过Bioconductor或Conda完成,需注意安装配套注释包。文中列举了10种常见报错,包括依赖包缺失、ID类型不匹配、物种不支持等问题,并给出具体解决方法。最后总结了GO/KEGG富集分析的标准流程,包括ID转换、富集分析和可视化步骤,为生物信息学分析提供实用参考。建议使用R≥4.2版本,并确保正确调用BiocManager进行安装。

5996

IsaacGym四足机器人sim to real全过程详细解析(walk these ways方法)

本文使用强化学习的方法运行到Aliengo机器人上完成从训练到部署的全过程。该方法使用的是GO1机器人提供了完整的sim to real代码和遥控器控制代码,所以我们只需要重新训练和部署健康就可以使用在Aliengo上。该方法使用lcm进行通讯首先程序通过UDP和机器人控制板进行通讯获取机器人状态并下达指令。然后又通过lcm进行机器人状态信息的发布和模型命令的获取。

weixin_44061195的博客 2万+

AGP 8.0 适配 - transform适配

Google的AGP 8.0 已经出来一段时间了,基于8.0有许多新的特性,比如baseline profile,dexlayout等,这些特性可以大幅度的提升app的启动速度。该案例实现如下功能:收集线上代码类的使用情况,从而通过数据分析,针对线上未使用的类,进行移除,而从缩减包体积。第二种是针对需要先全局遍历所有的字节码,找到对应类,方法,相关的数据,然后二次遍历,进行插装。说明:从上图可以看出,针对具体的io流的遍历,官方已经帮我们覆盖掉了,我们只需要关注与要做的字节码插装的实现就可以了。

zhuzhumouse的专栏 2817

基于STM32L4XX、HAL库的MMA8452QR1姿态传感器驱动程序设计

/ HAL库需要左移1位。#define MMA8452Q_I2C_ADDRESS 0x1D // SA0=1时的地址。* @param range: 量程(MMA8452Q_RANGE_2G/4G/8G)* @param haccel: MMA8452Q句柄。* @param haccel: MMA8452Q句柄。* @param haccel: MMA8452Q句柄。* @param haccel: MMA8452Q句柄。* @param haccel: MMA8452Q句柄。

zhusl6688的博客 772

适配 AGP8.5 版本,Transform(四)

支持的组件输出、输入类型支持的转换类型:(列举几个常用的)

Primer5 716

华为的LTC(Lead to Cash,从线索到现金)流程是其销售管理体系的核心,覆盖了从发现销售线索到最终收回现金的全过程,强调端到端的业务贯通和以客户为中心的运营理念

华为LTC流程是覆盖销售全周期的管理体系,包含四大阶段:线索转机会(LTO)、机会转订单(OTO)、订单转交付(OTD)、交付转现金(DTC)。其核心采用"铁三角"团队模式(AR/SR/FR协作),通过多部门联合评审把控关键节点,实现端到端业务贯通。该流程与IPD、MTL、ITR等体系高效协同,结合iSales平台实现数字化管理,有效提升销售效率、客户满意度和资金回笼速度。

金牌架构师 1725

无需SLAM建图,直接生成Gazebo仿真环境的二维代价地图的功能包pgm_map_creator适配ROS Noetic编译报错及解决方法

在ROS Noetic中,使用`pgm_map_creator`功能包直接生成Gazebo仿真环境的二维代价地图时,可能会遇到各种编译和运行错误。本篇博客详细解析了在适配ROS Noetic过程中常见的报错及其解决方法,包括Boost文件结构更新、C++版本兼容性、Gazebo函数名变更、链接库缺失以及protobuf文件加载错误。通过逐步排查和修改,如更换头文件路径、调整C++标准、更新CMakeLists.txt文件等,成功解决了这些问题,使得`pgm_map_creator`功能包能够顺利编译和运行。

慕羽★的博客 2261

总听说AGP,它到底做了什么?

目录前言一、基础准备二、AGP源码的打开方式三、代码分析第一步 寻找AppPlugin第二步 AppPlugin第三步 配置Project第四步 确认扩展第五步 创建Task第六步 配置完成后创建Task第七步 TaskManager第一次创建多个Task第八步 TaskManager第二次创建多个Task总结 前言 故事的开始是这样的。 之前阅读《Android开发高手课》的时候,里面启动优化一栏有讲到 systrace + 函数插桩 是不错的卡顿排查方式。 主要方式就是通过 Transform + As

qq_35063091的博客 2729

【WRF工具】WRF Domain Wizard第一期:软件下载及安装

【WRF工具介绍】WRF Domain Wizard-确定模拟区域

WW、forever的博客 2918

AGP 升级,适配 8.5 版本(一)

本次升级是从之前的 AGP7+ 升级到 AGP8+

Primer5 2839

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

AGP tramsform

Transform

不会写代码的丝丽 739

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

AGP 7.0中Transform 被废弃,如何适配

Transform API 是 AGP1.5 就引入的特性,主要用于在 Android 构建过程中,在 Class转Dex的过程中修改 Class 字节码。利用 Transform API,我们可以拿到所有参与构建的 Class 文件,然后可以借助ASM 等字节码编辑工具进行修改,插入自定义逻辑。国内很多团队都或多或少的用 AGP 的 Transform API 来搞点儿黑科技,比如无痕埋点,耗时统计,方法替换等。但是在AGP7.0中Transform已经被标记为废弃了,并且将在AGP8.0中移除。而AGP

chuyouyinghe的专栏 2013

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

谨慎hook,一个hook Transform源码导致的错误

前言 上篇文章《总听说AGP,它到底做了什么?》和大家分析了 AGP(Android Gradle Plugin) 做了哪些事,了解到 AGP 就是为打包这个过程服务的。 那么,本篇文章就和大家聊一聊其中的 Transform,解决一下为什么在 AGP 3.x.x 的版本可以通过反射获取的 transformClassesWithDexBuilderForXXX Task 在 4.0.0 的版本就不灵了? 源码走起! 一、Transform的流程 读本篇文章以前,相信同学们已经具备 Transform 的

qq_35063091的博客 4388

Transform 现如今被废弃,ASM 该如何适配?

Transform API 是 AGP1.5 就引入的特性,主要用于在 Android 构建过程中,在 Class转Dex的过程中修改 Class 字节码。

m0_64420071的博客 1256

!AGP8+ 的 Transform + ASM 模板

所有的 class 输入都放置在一个目录下面,所以我们要遍历该目录下的所有 class 文件,为下一步处理做准备。获取所有 class 输入:transformInput.getDirectoryInputs()目录可直接获得 class 文件,通常修改 class 文件我们用。遍历压缩包获取 class 文件得到字节数组使用 ASM 操作即可。,处理完毕得到新的字节数组,重新写入文件,覆盖已有文件即可;至于要处理那些 class 文件,要如何处理?ASM 处理字节码输入的是字节码文件的字节数组。

Primer5 478

新技术又来了,拥抱AGP7.0,准备好告别Transform了吗?

本文作者 作者:究极逮虾户 AGP 7.0 之后Transform 替代方案的调研,相关代码在https://github.com/Leifzhang/AndroidAutoTrack这个库的最新一次提交,当然如果你正在学习 Transform,不需要因此而停止,一方面虽然流程是变化的,但ASM 这样字节码修改的核心是没有变化的;另一方面Transform 到完全被废弃个人觉得还需要比较久的时间。 本文更算一个周知吧~ 1 前文提要 之前就和大家介绍过AGP(Android Gradle...

chuyouyinghe的专栏 876

从开发一个插件看,安卓gradle插件适配AGP8.0

相信很多小伙伴项目还没有升级AGP7.0,可是最新的AGP已经到8.2了,适配AGP8.0也要提上日程了,尤其是一些插件项目,因为8.0删除了transform API,所以需要提前做好适配工作。如果你是一个插件小白,本篇可以教你从0开始在AGP7.0以上如何开发插件。如果你是一个插件开发者,相信本篇也可以给你适配AGP8.0带来一些帮助。以上就是如何开发兼容AGP8.0插件的全部内容了。

u012122009的博客 2332

Android Gradle 插件(AGP)中用于字节码插桩的实现方式对比

摘要:本文对比了Android Gradle插件中的两种字节码处理方案。ScopedArtifactsTransform(方案1)通过Task处理编译后的Class文件,支持文件级操作和非ASM工具,但存在性能低、调试困难等问题;InstrumentationTransform(方案2)基于ASM在编译过程中直接修改字节码,具有高性能、兼容性好等优势。建议纯字节码插桩场景优先选择方案2,仅当需要文件级操作或非ASM工具时才考虑方案1。

qq_20317257的博客 958

其实 Gradle Transform 就是个纸老虎 —— Gradle 系列(4)

而 Task 的依赖关系是通过 TransformTask 的输入输出关系隐式确定的,TransformManager 通过 TransformStream 链接各个 TransformTask 的输入输出,进而控制 Transform 的依赖关系顺序。是的,就是卷,而且这次直接是降维打击。Task 增量模式与 Transform 增量模式的区别在于,Task 增量执行时会跳过整个 Task 的动作列表,而 Transform 增量执行依然会执行 TransformTask,但输入内容会增加变更内容信息。

chuyouyinghe的专栏 642

牛逼的Transform Plus | Transform进阶教程

在`Transform`操作过程中,我们是通过文件路径的形式去访问一个个class文件的,这种情况下就无法确保访问的是否是有序和有语法的。

2301_81940605的博客 721

Android Gradle8.0 Transform 废弃如何适配,手撸Arouter插件教你使用

最近公司也在适配项目至Android14 否则市场不让上架,因此 gradle也必须适配升级了,就先来试试水。目前最新的AGP已经到8.3了,适配AGP8.0也要提上日程了,尤其是一些插件项目,因为8.0删除了transform API,所以需要提前做好适配工作。对于之前未了解过gradle自定义插件的同学可以看下我以前的7.0之前的gradle自定义插件的用法 教你如何一步步实现路由插件。Android 自定义Gradle插件,使用Transform。

chuyouyinghe的专栏 1032

Android开发之玩转 Gradle,你可不能不熟悉 Transform

这次面试问的还是还是有难度的,要求当场写代码并且运行,也是很考察面试者写代码因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

2401_84123201的博客 979
上一篇: !AGP8+ 的 Transform + ASM 模板
下一篇: 日常笔记 Mark
Primer@.com
博客等级 码龄9年 524粉丝 292原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值