Android中,怎么优雅的生成代码?
前言
在前面的Android6.0权限处理不再复杂文章中,介绍过一个开源框架PermissionsDispatcher(权限调度者)框架,基于该框架的条件下,有了介绍注解以及注解APT的两篇文章,分别是:
Android进阶之Annotation(注解)的使用
Android编译时,怎么自动生成代码?
在【Android编译时,怎么自动生成代码?】这篇文章的结尾中,提到过可以使用JavaPoet来优雅的生成代码。那今天我们的主要内容就是来介绍一下【JavaPoet】该开源框架。
环境的搭建
1,新建项目名为【myJavaPoet】的Android项目;
2,在其中新建Module名为【poet】的模块,选择【Java Library】,如下:
PS:
JavaPoet必须在Java Library中使用,例如在rt.jar包中的javax包属于Android核心库中没有,所以不能直接在app Module和Android Library中使用,必须要创建一个Java Library,然后由Java Library导出jar包使用,如下图:
rt.jar3,在【poet.build.gradle】中,添加如下信息:
dependencies { compile 'com.squareup:javapoet:1.8.0'}
完成后的项目目录结构如下:
项目目录结构下面开始讲例子,希望看例子的时候,注释切记一定要多注意看~~~
例子1,PoetHello
主要讲的是类、方法的生成,在其中添加修饰、返回类型、抽象方法,以及如何写入文件系统等等。。。
PoetHello的代码如下:
package com.example;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.TypeSpec;import java.io.File;import javax.lang.model.element.Modifier;/** * 使用JavaPoet写一个Hello 2017/4/24 11:01 */public class PoetHello { /** * 生成器 2017/4/24 14:49 */ public static void product() { try { // 创建方法 2017/4/24 14:26 MethodSpec method = MethodSpec.methodBuilder("hello") .addModifiers(Modifier.PUBLIC) // 修饰符 .returns(String.class) // 返回类型 .addParameter(String.class, "name", Modifier.FINAL) // 添加final参数 2017/4/25 09:03 .addStatement("String say") // 负责分号、换行 2017/4/24 14:52 .addStatement("say = $S + name", "Hello ") .addStatement("return say") .build(); // 抽象方法 2017/4/24 17:03 MethodSpec abstractMethod = MethodSpec.methodBuilder("abstractHello") .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED) .build(); // 创建类 TypeSpec hello = TypeSpec.classBuilder("Hello") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addMethod(method) // 添加方法 .addMethod(abstractMethod) .build(); // 写入文件系统 2017/4/24 14:27 JavaFile javaFile = JavaFile.builder("com.example.all_product", hello) .build(); javaFile.writeTo(new File("poet/src/main/java")); // 指定文件路径 } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetHello.product(); }}
生成的Hello.java类,代码如下:
package com.example.all_product;import java.lang.String;public abstract class Hello { public String hello(String name) { String say; say = "Hello " + name; return say; } protected abstract void abstractHello();}
例子2,PoetFor
主要讲的就是beginControlFlow() 、endControlFlow()的配合使用,提供换行符与缩进。以for循环为例子,其它使用方法类似,如if、while、switch等等。。。
其中还有$S、$T、$N、$L的使用,意思分别如下:
$S for Strings
顾名思义,$S代表的是String,将其看成是一个字符创即可。
$T for Types
这里的Type/类型,其实可以简单的理解成我们的Class类即可。
$N for Names
可以理解成引用另一个变量名即可。
$L for Literals
这个刚开始其实是比较不好理解,但场景使用多了就应该慢慢能理解了,在这里其实就可以简单的理解成【Formatter】即可。
PoetFor代码如下(切记看注释):
package com.example;import com.squareup.javapoet.FieldSpec;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.TypeSpec;import java.io.File;import javax.lang.model.element.Modifier;/** * 生成for代码 2017/4/24 14:57 */public class PoetFor { /** * for循环生成器 2017/4/24 14:57 */ public static void product(int from, int to) { try { // 创建全局字段,并初始化 2017/4/25 09:15 FieldSpec fieldSpec = FieldSpec.builder(String.class, "DESC") .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .initializer("$S", "Sum is ") .build(); // 生成方法 2017/4/24 15:09 MethodSpec methodSpec = MethodSpec.methodBuilder("forInteger") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) // 无返回值 .addStatement("int sum = 0") // beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。 2017/4/24 15:09 // $L 占位符,类似于Formatter,替换int类型 2017/4/24 15:25 .beginControlFlow("for (int i = $L; i <= $L; i++)", from, to) .addStatement("sum += i") .endControlFlow() // $T 替换类名 $S 替换字符串 2017/4/24 15:31 // 使用System会自动import使用 2017/4/24 15:34 .addStatement("$T.out.println($N + sum)", System.class, "DESC") .build(); // 类生成 2017/4/24 15:10 TypeSpec typeSpec = TypeSpec.classBuilder("ForInteger") .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .addField(fieldSpec) .build(); // 文件生成 2017/4/24 15:10 JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec) .addFileComment("JavaPoet生成For循环代码 2017/4/24 15:07") .build(); javaFile.writeTo(new File("poet/src/main/java")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetFor.product(0, 10); }}
生成的ForInteger.java类如下:
// JavaPoet生成For循环代码 2017/4/24 15:07package com.example.all_product;import java.lang.String;import java.lang.System;public class ForInteger { private static final String DESC = "Sum is "; public static void forInteger() { int sum = 0; for (int i = 0; i <= 10; i++) { sum += i; } System.out.println(DESC + sum); }}
例子3,PoetList
主要讲的是ClassName的硬指定,以及引用指定;然后再讲了使用ParameterizedTypeName.get(ClassName, ClassName)创建List
PoetList的代码如下:
package com.example;import com.example.all_product.ForInteger;import com.squareup.javapoet.ClassName;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.ParameterizedTypeName;import com.squareup.javapoet.TypeName;import com.squareup.javapoet.TypeSpec;import java.io.File;import javax.lang.model.element.Modifier;/** * 使用ClassName(重要),生成List代码 2017/4/24 16:04 */public class PoetList { /** * 生成器 2017/4/24 16:04 */ public static void product() { try { // list泛型的类型 2017/4/24 16:06 ClassName type = ClassName.get("com.example.all_product", "ForInteger"); // List类型 ClassName list = ClassName.get("java.util", "List"); // ArrayList类型 ClassName arrayList = ClassName.get("java.util", "ArrayList"); // 生成泛型类型,类型的名称、参数的名称 2017/4/24 16:08 TypeName listType = ParameterizedTypeName.get(list, type); MethodSpec methodSpec = MethodSpec.methodBuilder("listType") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(listType) .addStatement("$T lstHello = new $T<>()", listType, arrayList) // ClassName类的绝对路径,内部利用该路径进行反射获取实体类,使用场景是当该类也是生成类时,可以硬指定 2017/4/24 16:18 .addStatement("lstHello.add(new $T())", type) // ClassName指定 .addStatement("lstHello.add(new $T())", ForInteger.class) // 引用指定 .addStatement("return lstHello") .build(); TypeSpec typeSpec = TypeSpec.classBuilder("ListType") .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .build(); JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec) .build(); javaFile.writeTo(new File("poet/src/main/java")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetList.product(); }}
生成的ListType.java类,代码如下:
package com.example.all_product;import java.util.ArrayList;import java.util.List;public class ListType { public static List listType() { List lstHello = new ArrayList<>(); lstHello.add(new ForInteger()); lstHello.add(new ForInteger()); return lstHello; }}
例子4,PoetMap
上面例子3讲的是List
PoetMap的代码如下:
package com.example;import com.squareup.javapoet.ClassName;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.ParameterizedTypeName;import com.squareup.javapoet.TypeName;import com.squareup.javapoet.TypeSpec;import java.io.File;import javax.lang.model.element.Modifier;/** * 使用ClassName(重要),生成Map代码 2017/4/25 13:39 */public class PoetMap { /** * 生成器 2017/4/25 13:39 */ public static void product() { try { // list泛型的类型 2017/4/24 16:06 ClassName type = ClassName.get("com.example.all_product", "ForInteger"); // key 2017/4/25 13:38 ClassName string = ClassName.get("java.land", "String"); // map类型 ClassName map = ClassName.get("java.util", "Map"); // HashMap类型 ClassName hashMap = ClassName.get("java.util", "HashMap"); // 生成Map类型,类型的名称、Key、Value 2017/4/25 14:08 TypeName listType = ParameterizedTypeName.get(map, string, type); MethodSpec methodSpec = MethodSpec.methodBuilder("listType") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(listType) .addStatement("$T mapHello = new $T<>()", listType, hashMap) .addStatement("mapHello.put($S, new $T())", "key", type) .addStatement("return mapHello") .build(); TypeSpec typeSpec = TypeSpec.classBuilder("MapType") .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .build(); JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec) .build(); javaFile.writeTo(new File("poet/src/main/java")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetMap.product(); }}
生成的MapType.java,代码如下:
package com.example.all_product;import java.util.HashMap;import java.util.Map;public class MapType { public static Map listType() { Map mapHello = new HashMap<>(); mapHello.put("key", new ForInteger()); return mapHello; }}
例子5,PoetInterface
该例子讲的是利用JavaPoet来生成接口,使用的是TypeSpec.interfaceBuilder(InterfaceName)方法来创建接口类,也可以使用TypeSpec中的其它方法来创建枚举、注解等等。。。
PoetInterface的代码如下:
package com.example;import com.squareup.javapoet.FieldSpec;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.TypeSpec;import java.io.File;import javax.lang.model.element.Modifier;/** * 生成接口 2017/4/25 09:16 */public class PoetInterface { /** * 生成器 2017/4/25 09:40 */ public static void product() { try { // 生成方法,必须有Modifier.ABSTRACT或STATIC 2017/4/25 09:25 MethodSpec methodSpec = MethodSpec.methodBuilder("getName") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addJavadoc("获取名称\n") // 添加注释 2017/4/25 11:24 .returns(String.class) .build(); // 生成字段,必须同时有Modifier.STATIC, Modifier.FINAL 2017/4/25 09:31 FieldSpec fieldSpec = FieldSpec.builder(String.class, "name") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", "CCB") .build(); // interfaceBuilder方法标志生成接口类 2017/4/25 09:25 TypeSpec typeSpec = TypeSpec.interfaceBuilder("UserInterface") .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .addField(fieldSpec) .build(); // 写入文件 JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec) .addFileComment("Poet生成接口 2017/4/25 09:24") .build(); javaFile.writeTo(new File("poet/src/main/java")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetInterface.product(); }}
生成的UserInterface.java接口类,代码如下:
// Poet生成接口 2017/4/25 09:24package com.example.all_product;import java.lang.String;public interface UserInterface { String name = "CCB"; /** * 获取名称 */ String getName();}
例子6,PoetSort
主要讲的就是怎么创建匿名类。该例子先自动创建一个实体,再引用该实体类做sort排序。
PoetSort的代码如下:
package com.example;import com.squareup.javapoet.ClassName;import com.squareup.javapoet.FieldSpec;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.ParameterizedTypeName;import com.squareup.javapoet.TypeSpec;import java.io.File;import java.util.Collections;import javax.lang.model.element.Modifier;/** * 写匿名类进行排序 2017/4/25 09:39 */public class PoetSort { public static void product() { try { // 生成实体 2017/4/25 09:41 TypeSpec typeEntity = TypeSpec.classBuilder("UserEntity") .addModifiers(Modifier.PUBLIC) .addField(FieldSpec.builder(String.class, "name") .addModifiers(Modifier.PRIVATE) .build()) .addMethod(MethodSpec.methodBuilder("getName") .addModifiers(Modifier.PUBLIC) .returns(String.class) .addStatement("return this.$N", "name") .build()) .addMethod(MethodSpec.methodBuilder("setName") .addModifiers(Modifier.PUBLIC) .addParameter(String.class, "name") .returns(void.class) .addStatement("this.$N = $N", "name", "name") .build()) .addField(FieldSpec.builder(int.class, "time") .addModifiers(Modifier.PRIVATE) .build()) .addMethod(MethodSpec.methodBuilder("getTime") .addModifiers(Modifier.PUBLIC) .returns(int.class) .addStatement("return this.$N", "time") .build()) .build(); // 写入用户实体 2017/4/25 09:48 JavaFile javaUserEntity = JavaFile.builder("com.example.all_product", typeEntity) .build(); javaUserEntity.writeTo(new File("poet/src/main/java")); // 生成排序匿名类 2017/4/25 09:49 ClassName classComparator = ClassName.get("java.util", "Comparator"); // 指向Comparator ClassName classList = ClassName.get("java.util", "List"); // 指向List ClassName classUser = ClassName.get("com.example.all_product", "UserEntity"); // 指向User实体 // 匿名比对类 2017/4/25 09:52 TypeSpec typeCompare = TypeSpec.anonymousClassBuilder("") .addSuperinterface(ParameterizedTypeName.get(classComparator, classUser)) .addMethod(MethodSpec.methodBuilder("compare") .addAnnotation(Override.class) // 添加注解 2017/4/25 10:56 .addModifiers(Modifier.PUBLIC) .addParameter(classUser, "obj1") .addParameter(classUser, "obj2") .returns(int.class) .beginControlFlow("if ($N.$N() > $N.$N())", "obj1", "getTime", "obj2", "getTime") .addStatement("return 1") .endControlFlow() .beginControlFlow("else if ($N.$N() < $N.$N())", "obj1", "getTime", "obj2", "getTime") .addStatement("return -1") .endControlFlow() .beginControlFlow("else") .addStatement("return 0") .endControlFlow() .build()) .build(); // 外部类 2017/4/25 09:59 TypeSpec typeSort = TypeSpec.classBuilder("UserSort") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("sort") .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(ParameterizedTypeName.get(classList, classUser), "lstUsers") .addStatement("$T.sort($N, $L)", Collections.class, "lstUsers", typeCompare) .build()) .build(); // 写入排序类 2017/4/25 10:03 JavaFile javaUserSort = JavaFile.builder("com.example.all_product", typeSort) .build(); javaUserSort.writeTo(new File("poet/src/main/java")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetSort.product(); }}
生成的UserEntity.java和UserSort.java类的代码如下:
package com.example.all_product;import java.lang.String;public class UserEntity { private String name; private int time; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public int getTime() { return this.time; }}
package com.example.all_product;import java.lang.Override;import java.util.Collections;import java.util.Comparator;import java.util.List;public class UserSort { public void sort(List lstUsers) { Collections.sort(lstUsers, new Comparator() { @Override public int compare(UserEntity obj1, UserEntity obj2) { if (obj1.getTime() > obj2.getTime()) { return 1; } else if (obj1.getTime() < obj2.getTime()) { return -1; } else { return 0; } } }); }}
例子7,PoetExtend
主要就讲一下怎么使用superclass(ClassName)的方法来创建继承的子类,同理,也可以使用addSuperinterface(ClassName)的方法来创建实现接口,实现接口后,怎么重写接口的方法,可参考例子5。。。
PoetExtend的代码如下:
package com.example;import com.squareup.javapoet.ClassName;import com.squareup.javapoet.FieldSpec;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.TypeSpec;import java.io.File;import javax.lang.model.element.Modifier;/** * 生成继承的子类 2017/4/25 14:48 */public class PoetExtend { public static void product() { try { // 生成实体 2017/4/25 14:48 TypeSpec typeEntity = TypeSpec.classBuilder("SubUserEntity") .addModifiers(Modifier.PUBLIC) // 继承UserEntity类 2017/4/25 14:58 .superclass(ClassName.get("com.example.all_product", "UserEntity")) // 实现UserInterface接口 .addSuperinterface(ClassName.get("com.example.all_product", "UserInterface")) .addField(FieldSpec.builder(int.class, "id") .addModifiers(Modifier.PRIVATE) .build()) .addMethod(MethodSpec.methodBuilder("getId") .addModifiers(Modifier.PUBLIC) .returns(int.class) .addStatement("return this.$N", "id") .build()) .build(); // 写入用户实体 2017/4/25 14:55 JavaFile javaUserEntity = JavaFile.builder("com.example.all_product", typeEntity) .build(); javaUserEntity.writeTo(new File("poet/src/main/java")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PoetExtend.product(); }}
生成的SubUserEntity代码如下:
package com.example.all_product;public class SubUserEntity extends UserEntity implements UserInterface { private int id; public int getId() { return this.id; }}
总结
怎么动态的生成java代码,我们今天主要讲7个例子就好了,在我们写项目时,如果需要动态生成代码时,这应该足够应付80%的开发工作了,如果需要深入创建更复杂的代码,请移步参考JavaPoet开源框架。
结合前面几篇文章,就已经包含了创建注解、注解解析、代码生成,慢慢。。。慢慢。。。我们向自己写框架靠近。。。其实框架中经常使用的还有另一个检查工具,lint。我们可以自定义lint的检查规则,不过,我现在有一个地方卡住了,还没解决,后面如果解决了,有机会就一并讲一下。
今天就讲到这里了。。。
谢谢支持~~~
更多相关文章
- android 破解软件
- Android(安卓)里的FrameBuffer
- Android热更新框架Nuwa的使用
- egret 发布android原生项目(一)打包apk
- Android开发学习笔记:浅谈GridView
- Android之WebView安全
- Android(安卓)studio 快捷键汇总
- Android实现应用开机自启动
- Android(安卓)开发ListView适配器优化