[Android]Gradle 插件 DiscardFilePlugin(class注入&清空类和方法)


以下内容为原创,欢迎转载,转载请注明
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6732128.html

Android Gradle 插件 DiscardFilePlugin(清空类和方法)

An android gradle plugin for discard class or method in compile time.

用于在编译构建时期忽略清空类和方法的一个Android Gradle插件。

Github: https://github.com/wangjiegulu/DiscardFilePlugin

1.1 使用场景

在实际的生产中,我们总是会在我们的app中增加一些调试的工具,比如在debug模式下加入DebugPanelActivity(调试面板工具页面,提供比如“切换服务器”等操作)。我们需要在正式上线的release版本中清空相关类和方法,或者修改boolean isProductionEnvironment()方法,让它永远返回true以此来避免上线之后调试相关代码通过反编译等手段暴露出来。

1.2 @Discard注解

1.2.1 Target

  • ElementType.METHOD: 表示清空方法中的代码,编译过程中该方法中代码被清空。
  • ElementType.TYPE: 表示清空类,其实是清空类中的所有方法。

1.2.2 参数

1.2.2.1 apply

apply参数规范:key==exceptValue

表示当key==exceptValue时,Discard才会生效,才会真正在编译时去对方法或者类进行清空。因此可以在每个方法或者类中去进行不同的配置,在不同状态下通过如下方式对不同方法进行Discard:

@Discard(apply = "test1==true")
public void testMethod_1() {
    System.out.println("testMethod_1...");
}

@Discard(apply = "test2==true")
public void testMethod_2() {
    System.out.println("testMethod_2...");
}

使用gradle assembleDebug -Ptest1=true -Ptest2=false来构建时,testMethod_1()方法会被discard,而testMethod_2()不会被discard。构建完毕反编译class结果如下:

@Discard(apply = "test1==true")
public void testMethod_1() {
}

@Discard(apply = "test2==true")
public void testMethod_2() {
    System.out.println("testMethod_2...");
}

1.2.2.2 srcCode

替换方法的方法体,如果不设置,默认discard方法实现:

  • 返回类型为void: discard后方法体为{}
  • 返回类型为原始数据类型:discard后方法返回默认值,比如{ return 0; }
  • 返回类型为类对象时: discard后方法返回为{ return null; }

可以如下填写具体的方法体代码块:

@Discard(srcCode = "{super.onCreate($1); System.out.println(\"this: \" + $0);}")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        usernameEt = (EditText) findViewById(R.id.activity_main_username_et);
        passwordEt = (EditText) findViewById(R.id.activity_main_password_et);
        setTestAccount();
    }

discard之后的class反编译代码如下:

@Discard(
        srcCode = "{super.onCreate($1); System.out.println(\"this: \" + $0);}"
    )
    protected void onCreate(Bundle var1) {
        super.onCreate(var1);
        System.out.println("this: " + this);
    }

方法的$0表示当前对象this,方法参数依次为$1, $2, $3...详细文档参考这里

1.2.2.3 makeClassNames

可以在这里指定具体的类名,在discard时对未在classPath的类进行make。不常用,可以省略。

1.2.2.4 enable

表示该方法或者类的discard是否开启,默认为true,比较典型的场景为,在类上面增加@Discard对该类所有方法进行discard,但是需要某个方法不discard,这时可以使用@Discard(enable = false)来对方法进行排除在discard范围外。

1.3 使用方式

Gradle(Check newest version):

build.gradle in Project:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'com.github.wangjiegulu:discardfile:x.x.x'
    }
}

build.gradle in app or library:

apply plugin: 'com.github.wangjiegulu.plg.discardfile'

dependencies {
    compile 'com.github.wangjiegulu:discardfile-api:x.x.x'
}

1.3.1. build.gradle

// 使用插件
apply plugin: 'com.github.wangjiegulu.plg.discardfile'

// 配置需要修改的类所属在那些包下
discard {
    includePackagePath 'com.wangjie.plg.discardfile.sample.ui', 'com.wangjie.plg.discardfile.sample.include'
    excludePackagePath 'com.wangjie.plg.discardfile.sample.exclude'/*, 'com.wangjie.plg.discardfile.sample.ui.MainActivity'*/
}

1.3.2. 使用@Discard注解

创建自定义apply配置(publishdisable两种apply配置):

public class ApplyConstants {
    public static class Publish {
        private static final String PUBLISH = "publish";
        public static final String _TRUE = PUBLISH + "==true";
    }
    public static class DISABLE {
        private static final String DISABLE = "disable";
        public static final String _TRUE = DISABLE + "==true";
    }
}

在需要清空的类上添加@Discard注解,apply = ApplyConstants.Publish._TRUE表示只有在publish=true的情况下,才会执行Discard。

@Discard(apply = ApplyConstants.Publish._TRUE)
public class IncludeClassC {

    /**
     * 因为IncludeClassC类增加了`@Discard`注解,所以该方法也会被discard。
     */
    public void onIncludeMethodC() {
        System.out.println("onIncludeMethodC...");
    }

    /**
     * 替换该方法的实现为:{System.out.println("onIncludeMethodC_2... injected!");}
     */
    @Discard(apply = ApplyConstants.Publish._TRUE, srcCode = "{System.out.println(\"onIncludeMethodC_2... injected!\");}")
    public void onIncludeMethodC_2() {
        System.out.println("onIncludeMethodC_2...");
    }

    /**
     * 替换该方法永远返回true
     */
    @Discard(apply = ApplyConstants.Publish._TRUE, srcCode = "{return true;}")
    public boolean onIncludeMethodC_3() {
        System.out.println("onIncludeMethodC_3...");
        return false;
    }

    /**
     * 因为IncludeClassC类增加了`@Discard`注解,所以该方法也会被discard。
     */
    public int onIncludeMethodC_4() {
        System.out.println("onIncludeMethodC_4...");
        return 100;
    }

    /**
     * 由于使用了`@Discard`注解进行显式地声明禁用了本地的discard,所以该方法不会被discard
     */
    @Discard(apply = ApplyConstants.Publish._TRUE, enable = false)
    public String onIncludeMethodC_5() {
        System.out.println("onIncludeMethodC_5...");
        return "hello world";
    }

    /**
     * 替换该方法永远返回"hello world"字符串
     */
    @Discard(apply = ApplyConstants.Publish._TRUE, srcCode = "{return \"hello world injected!\";}")
    public String onIncludeMethodC_6() {
        System.out.println("onIncludeMethodC_6...");
        return "hello world";
    }
}

1.3.3. build运行

通过以下命令进行构建:

gradle clean assembleFullDebug -Ppublish=true -Pdisable=true

命令编译完成之后,该类的class文件将会根据配置的@Discard注解被自动修改成如下:

build/intermediates/transforms/discardFile/.../IncludeClassC.class

@Discard(
    apply = "publish==true"
)
public class IncludeClassC {
    public IncludeClassC() {
    }

    public void onIncludeMethodC() {
        Object var10000 = null;
    }

    @Discard(
        apply = "publish==true",
        srcCode = "{System.out.println(\"onIncludeMethodC_2... injected!\");}"
    )
    public void onIncludeMethodC_2() {
        System.out.println("onIncludeMethodC_2... injected!");
    }

    @Discard(
        apply = "publish==true",
        srcCode = "{return true;}"
    )
    public boolean onIncludeMethodC_3() {
        return true;
    }

    public int onIncludeMethodC_4() {
        return 0;
    }

    @Discard(
        apply = "publish==true",
        enable = false
    )
    public String onIncludeMethodC_5() {
        System.out.println("onIncludeMethodC_5...");
        return "hello world";
    }

    @Discard(
        apply = "publish==true",
        srcCode = "{return \"hello world injected!\";}"
    )
    public String onIncludeMethodC_6() {
        return "hello world injected!";
    }
}
时间: 2024-09-23 14:32:55

[Android]Gradle 插件 DiscardFilePlugin(class注入&清空类和方法)的相关文章

android-关于Android如何在不同Activity调用一个类的方法不同操作?

问题描述 关于Android如何在不同Activity调用一个类的方法不同操作? 我现在有个activity是全局运行的(简称A),还有个MainActivity(简称B),现在写了一个类(简称C),C中有个方法,A和B都要用到(这个实现了),我想实现B用到这个方法的时候会做一些额外的事.如何是用呢class CustomMessageHandler extends AVIMMessageHandler { public static String ME=""msg"&quo

详解Android Gradle插件3.0挖坑日记

为了提升编译速度,这几天用上了 AS 3.0 和 Gradle 3.0 插件,不得不说不论是 AS 3.0,还是 Gradle 3.0 都变化非常大,具体的更新日志参考下官网,其中 Gradle 3.0的配置和可能遇到的问题可以说是非常详细了.Gradle 4.1-all 官方下载速度很慢,我直接放出来下载地址和相关官方文档地址吧. Gradle 4.1 Android Studio  Gradle 配置属性 下面来列决下我遇到的坑和解决方法: 配置属性的改动 Gradle 3.0 和之前有一点

Eclipse3.2插件项目如何调用其他类的方法

问题描述 最近要把一个应用程序做成Eclipse插件,在插件actions类中的run方法调用同一项目中的上级目录中的类方法,却调用不到这是一个测试类代码packageviewtest;importjavax.imageio.ImageIO;importjavax.swing.JFrame;importjavax.swing.JPanel;importjavax.swing.WindowConstants;importjava.awt.BorderLayout;importjava.awt.Co

为Android Studio编写自定义Gradle插件的教程_Android

Google已经建议Android开发全部转向Android Studio开发,Android Studio 是使用gradle编译.打包的,那么问题来了,gradle可是有一堆东西...,为了彻底了解gradle,今天就来学习下如何写自己的gradle插件(当然插件源码是使用groovy写的),先看如下代码目录: 如上图所示,plugin目录是插件源码目录,sample是用来测试插件的. 1.在目录plugin/src/main/groovy/com/micky/gradle/下新建插件类My

为Android Studio编写自定义Gradle插件的教程

Google已经建议Android开发全部转向Android Studio开发,Android Studio 是使用gradle编译.打包的,那么问题来了,gradle可是有一堆东西...,为了彻底了解gradle,今天就来学习下如何写自己的gradle插件(当然插件源码是使用groovy写的),先看如下代码目录: 如上图所示,plugin目录是插件源码目录,sample是用来测试插件的. 1.在目录plugin/src/main/groovy/com/micky/gradle/下新建插件类My

详解如何使用Android Studio开发Gradle插件

缘由 首先说明一下为什么会有这篇文章.前段时间,插件化以及热修复的技术很热,Nuwa热修复的工具NuwaGradle,携程动态加载技术DynamicAPK,还有希望做最轻巧的插件化框架的Small.这三个App有一个共同的地方就是大量的使用了Gradle这个强大的构建工具,除了携程的框架外,另外两个都发布了独立的Gradle插件提供自动化构建插件,或者生成热修复的补丁.所以学习一下Gradle插件的编写还是一件十分有意义的事. 插件类型 Gradle的插件一般有这么几种: 一种是直接在项目中的g

[Android]使用Dagger 2依赖注入 - API(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092525.html 使用Dagger 2依赖注入 - API 原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-the-api/ 这章是展示使用Dagger 2在Android端实现依赖注入的系列中的一部分.今天我会探索Dagger 2的基础并且学习这个依赖注入框架的所有的API

8 个最优秀的 Android Studio 插件

Android Studio是目前Google官方设计的用于原生Android应用程序开发的IDE.基于JetBrains的IntelliJ IDEA,这是Google I/O 2013第一个宣布的作为Eclipse的继承者,深受广大Android社区的欢迎.在经过漫长的测试阶段后,最终版本于去年12月发布. Android Studio是一个功能全面的开发环境,装备了为各种设备--从智能手表到汽车--开发Android应用程序所需要的所有功能.不但总是有改进的余地,Android Studio

Android WebView的Js对象注入漏洞解决方案

最近在做一个项目过程中,发现了一个很严重的安全漏洞,这个漏洞是乌云平台(http://www.wooyun.org)报告出来的. 1,使用场景 我们很多时候要使用WebView来展示一个网页,现在很多应用为了做到服务端可控,很多结果页都是网页的,而不是本地实现,这样做有很多好处,比如界面的改变不需要重新发布新版本,直接在Server端修改就行了.用网页来展示界面,通常情况下都或多或少都与Java代码有交互,比如点击网页上面的一个按钮,我们需要知道这个按钮点击事件,或者我们要调用某个方法,让页面执