当前位置: 首页>移动开发>正文

grafana transform百分比 transform gradle

概述

Google从 Android Gradle 1.5.0 开始,提供了Transform API。通过Transform API,允许第三方以插件的形式,在Android应用程序打包成dex文件之前的编译过程中操作.class文件。我们只要实现一套Transform,去遍历所有.class文件的所有方法,然后进行修改,再对源文件进行替换,即可以达到插入代码的目的。

Transform可以做什么

首先,我们可以先执行一次build操作,命令行会输出如下内容:

> Transform core-runtime.aar (androidx.arch.core:core-runtime:2.0.0) with AarTransform
> Transform lifecycle-livedata-core.aar (androidx.lifecycle:lifecycle-livedata-core:2.0.0) with AarTransform
> Transform lifecycle-livedata.aar (androidx.lifecycle:lifecycle-livedata:2.0.0) with AarTransform
> Transform interpolator.aar (androidx.interpolator:interpolator:1.0.0) with AarTransform
> Transform savedstate.aar (androidx.savedstate:savedstate:1.0.0) with AarTransform
> Transform lifecycle-viewmodel.aar (androidx.lifecycle:lifecycle-viewmodel:2.1.0) with AarTransform
> Transform lifecycle-runtime.aar (androidx.lifecycle:lifecycle-runtime:2.1.0) with AarTransform
> Transform versionedparcelable.aar (androidx.versionedparcelable:versionedparcelable:1.1.0) with AarTransform
> Transform cursoradapter.aar (androidx.cursoradapter:cursoradapter:1.0.0) with AarTransform
> Transform core.aar (androidx.core:core:1.3.2) with AarTransform
> Transform customview.aar (androidx.customview:customview:1.0.0) with AarTransform

也就是在构建过程中,会执行一个个的Transform。那么回到刚开始的问题,Transform可以做什么,我先列一些大家常听的,以及常见的:

  • 无痕埋点:不需要侵入代码即可以对页面进行埋点,不过一般这种都是针对比较简单的case,复杂的业务场景很难通过无痕埋点处理。
  • 性能监控:这个也很常见。
  • 事件防抖:避免短期内多次点击按钮。
  • 热修复:在方法前插入预留函数已做替换。

那么Transform的操作到底是在什么时候将代码植入的呢?我们看一张google官方的打包图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jRGFYfrZ-1612442402262)(https://raw.githubusercontent.com/296777513/Picture/master/gradle_plugin/transform_1.png)]

Transform阶段就是在图中红圈的位置,也就是.class文件变成.dex文件过程进行插入的。说白了Transform就是Android官方提供给开发者在项目构建阶段由class到dex转换期间修改class文件的一套api。比较经典的应用就是字节码插桩和代码注入技术。有了这个API,我们就可以根据自己的业务需求做一些定制。

Transform使用

前面说了那么多,主要是介绍了,Transform是什么,能做什么。那么该如何使用呢?

我们先在我们build.gradle中新增一个依赖:

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    //gradle sdk
    implementation gradleApi()
    //groovy sdk
    implementation localGroovy()
    //新增
    implementation "com.android.tools.build:gradle:3.3.2"
}

然后新建一个MyTransform:

//注意Transform有很多路径
import com.android.build.api.transform.Transform

class MyTransform extends Transform {

    @Override
    String getName() {
        return "MyTransform"
    }

    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS
    }

    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }

    @Override
    boolean isIncremental() {
        return false
    }
}
getName

指定自定义 Transform 的名称,返回的是对应的Task名称

getInputTypes

可以看到这个方法是返回一个Set<QualifiedContent.ContentType>集合,其实就是返回Transform需要处理的文件类型。具体有哪些,TransformManager已经给我们提供了,我们来看一下:

public static final Set<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);
    public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
    public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
    public static final Set<ContentType> CONTENT_NATIVE_LIBS =
            ImmutableSet.of(NATIVE_LIBS);
    public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
    public static final Set<ContentType> CONTENT_DEX_WITH_RESOURCES =
            ImmutableSet.of(ExtendedContentType.DEX, RESOURCES);

类型

描述

CONTENT_CLASS

表示需要处理 java 的 class 文件

CONTENT_JARS

表示需要处理 java 的 class 与 资源文件

CONTENT_RESOURCES

表示需要处理 java 的资源文件

CONTENT_NATIVE_LIBS

表示需要处理 native 库的代码

CONTENT_DEX

表示需要处理 DEX 文件

CONTENT_DEX_WITH_RESOURCES

表示需要处理 DEX 与 java 的资源文件

getScopes

可以看到这个方法是返回一个Set<QualifiedContent.Scope>集合,其实就是返回Transform处理的作用域。具体有哪些,我们来看一下:

/** Only the project (module) content */
    PROJECT(0x01),
    /** Only the sub-projects (other modules) */
    SUB_PROJECTS(0x04),
    /** Only the external libraries */
    EXTERNAL_LIBRARIES(0x10),
    /** Code that is being tested by the current variant, including dependencies */
    TESTED_CODE(0x20),
    /** Local or remote dependencies that are provided-only */
    PROVIDED_ONLY(0x40),
    /**
     * Only the project's local dependencies (local jars)
     *
     * @deprecated local dependencies are now processed as {@link #EXTERNAL_LIBRARIES}
     */
    @Deprecated
    PROJECT_LOCAL_DEPS(0x02),
    /**
     * Only the sub-projects's local dependencies (local jars).
     *
     * @deprecated local dependencies are now processed as {@link#EXTERNAL_LIBRARIES}
    */
    @Deprecated
    SUB_PROJECTS_LOCAL_DEPS(0x08);

这里主要介绍下前面五个。

类型

描述

PROJECT

只处理当前的项目

SUB_PROJECTS

只处理子项目

EXTERNAL_LIBRARIES

只处理外部依赖库

TESTED_CODE

测试代码

PROVIDED_ONLY

只提供本地或者远程依赖项

同样,TransformManager为我们分装了Scope的返回集合,具体如下:

public static final Set<ScopeType> PROJECT_ONLY = ImmutableSet.of(Scope.PROJECT);
    public static final Set<Scope> SCOPE_FULL_PROJECT =
            Sets.immutableEnumSet(
                    Scope.PROJECT,
                    Scope.SUB_PROJECTS,
                    Scope.EXTERNAL_LIBRARIES);
    public static final Set<ScopeType> SCOPE_FULL_WITH_IR_FOR_DEXING =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.MAIN_SPLIT)
                    .build();
    public static final Set<ScopeType> SCOPE_FULL_WITH_FEATURES =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.FEATURES)
                    .build();
    public static final Set<ScopeType> SCOPE_FULL_WITH_IR_AND_FEATURES =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.MAIN_SPLIT)
                    .add(InternalScope.FEATURES)
                    .build();
    public static final Set<ScopeType> SCOPE_FEATURES = ImmutableSet.of(InternalScope.FEATURES);
    public static final Set<ScopeType> SCOPE_FULL_LIBRARY_WITH_LOCAL_JARS =
            ImmutableSet.of(Scope.PROJECT, InternalScope.LOCAL_DEPS);
    public static final Set<ScopeType> SCOPE_IR_FOR_SLICING =
            ImmutableSet.of(Scope.PROJECT, Scope.SUB_PROJECTS);
isIncremental

是否进行增量更新,如果返回true,TransformInput会包含一份修改的文件列表,如果返回 false,则会删除上次修改的记录并进行全量编译。

transform

这是最主要的方法,对文件和jar对处理都是在这里进行的,代码植入也是通过此方法进行操作的。常用到的属性有以下几个:

  • TransformInput:对输入的class文件转变成目标字节码文件,TransformInput就是这些输入文件的抽象。目前它包含DirectoryInput集合与JarInput集合。
  • DirectoryInput:源码方式参与项目编译的所有目录结构及其目录下的源文件。
  • JarInput:Jar包方式参与项目编译的所有本地jar或远程jar包。
  • TransformOutProvider:通过这个类来获取输出路径。
使用

当你编写完成之后,我们只需要在我们的plugin中添加如下代码就可以使用你自己写的Transform了。

class MyGradlePlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        ...
        def android = project.extensions.getByType(AppExtension)
        def classTransform = new MyTransform(project)
        //注册你的Transform
        android.registerTransform(classTransform)
        ...
    }
}

总结

回到标题,Transform是什么?Transform其实就是在编译过程中可以动态织入代码。最主要的目的就是解耦。让开发更注重于业务开发。一些数据监控、无痕埋点等逻辑交给Transfrom处理。

参考

Gradle-初探代码注入Transform



https://www.xamrdz.com/mobile/4bj1928719.html

相关文章: