在做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(Set extends 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();
由于项目中设计到的隐私这里只能简单的写一下逻辑,方便以后查阅。