Android使用APT在编译时期修改类代码

   在做Android项目时候需要将项目中类中的一些敏感常量进行保护,尤其是项目中的URL地址,所以想到的一个策略就是在编译时将该类中的URL进行加密然后生成对应java文件,然后在apk编译时期将原来的class文件删除,在Android Studio 编译apk将class编译成dex文件之前将原来常量的URL对应类的class文件删除。

    这里主要是利用apt生成类,然后写一个简单的gradle插件将class文件删除。在项目中新建一个模块该模块中主要就是包含一个注解,该注解就是定义在需要加密的URL的Field上:

    

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ConstantEncript {
    String value();
}

    在需要处理的变量上使用:

@ConstantEncript("http://www.*****.com")
private  String URL;
    在AS中配置apt,在新建模块的gradle中添加
apply plugin: 'java'

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.google.auto.service:auto-service:1.0-rc2'
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

    在项目下的gradle中dependencies中添加

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    在app下的gradle中添加
apply plugin: 'com.neenbedankt.android-apt'

    并在dependencies中添加

apt project(":新建module名称")
compile project(':新建module名称')

    这样AS中apt就配置好了,剩下的就是写代码了,在新的module中新建一个类CustomeProcessor 让这个类集成AbstractProcessor

@AutoService(Processor.class)
@SupportedAnnotationTypes("catkin.com.annation.ConstantEncript")
public class CustomeProcessor extends AbstractProcessor {

    private Filer filer;

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnv.getFiler();
    }

   AutoService这个注解是google提供的就是在module中的gradle中添加那个compile。然后要重写AbstractProcessor中的几个方法getSupportedSourceVersion()获取支持的版本这里写SourceVersion.LatestSupported()就行,还有一个就是 getSupportedAnnotationTypes()这个就是获取支持的注解类型,也可以写到类的上面用@SupportedAnnationTypes里面就是注解的路径,多个注解可以用逗号隔开就是{"","".....}。重写init方法,并获取Filer对象用于生成类文件,方法中的processingEnv是父类中的成员变量。重写process方法,这个方法中主要就是做生成类的操作。

@Override
public boolean process(Setextends TypeElement> set, RoundEnvironment roundEnvironment) {
    String package_name = "";
    String class_name = "";
    for (Element ele : roundEnvironment.getElementsAnnotatedWith(ConstantEncript.class)) {
           //获取类的包名,要在该类的同级包下面新添加一个类
        package_name = processingEnv.getElementUtils().getPackageOf(ele).getQualifiedName().toString()+".";
        Element typeElement = ele.getEnclosingElement();
           //获取被ConstantEncript注解
        ConstantEncript annation = ele.getAnnotation(ConstantEncript.class);
            //获取类名
        class_name = "Complier_$" + typeElement.getSimpleName().toString();
    }
    try {
        String finalName = package_name + class_name;
              //在调用该方法前最好要把需要生成的类的全路径拼好,最好不要手动在反方中添加
              // JavaFileObject source = filer.createSourceFile("abc.bcd.a"+"b")这样会有莫名其妙的问题

        JavaFileObject source = filer.createSourceFile(finalName);
        Writer writer = source.openWriter();
        StringBuilder builder = new StringBuilder()
                .append("package"+package_name+";\n\n")
                .append("import java.io.IOException;\n\n")
                .append("import catkin.com.Connect;\n\n")
                .append("public class ")
                .append(class_name + " implements Connect")
                .append(" {\n\n") // open class
                .append("\tprivate static final String URL = \""加密处理的URl"\";\n")
                .append("\t@Override\n")
                .append("\tpublic String connect(){\n")
                .append("\t\t return URL;\n")
                .append("}\n")
                .append("}\n");

        writer.write(builder.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return true;
}

    这样就实现了生成加密处理的URl的类,还有一个就是在gradle编译时删除掉原来的url的类,以后再写,

    还有一个就是在生成代码时候继承了一个类,这个就是为了在获取对象,然后调用对象的方法,因为这个类是编译生成的,在代码中new不出来,所以写了一个接口,用反射将父类的引用指向子类,调用子类的方法,这样就不会报错了。

public interface Connect {
     String  connect();
}

Connect connect = (Connect) Class.forName(Complier_$类的名字.class.getName()).newInstance();
String result = connect.connect();

由于项目中设计到的隐私这里只能简单的写一下逻辑,方便以后查阅。

    

你可能感兴趣的:(Android使用APT在编译时期修改类代码)