上一章已经完成了简单的HelloWorld生成,这章的主要目的是生成下面的一个类,这个类主要是为Retrofit创建api接口实现,并提供全局唯一的api调用。
当然这里我是有很多不同的api并且属于不同的ip下的,如果你的api都属于一个ip或者说属于域名下,那么可能不需要这样的写法。
主要还是演示一下稍微复杂一些的的类应该怎样生成。
public class APIServiceManager { //自动生成好的类
private static ApiService apiService;
private static BreadtripAPI breadtripAPI;
private static QyerGuideAPI qyerGuideAPI;
private static TravelNotesAPI travelNotesAPI;
public static ApiService getApiService() {
return apiService==null ?
HttpServiceManager.getInstance().getRetrofit().create(ApiService.class)
:apiService;
}
public static BreadtripAPI getBreadtripAPI() {
return breadtripAPI==null ?
HttpServiceManager.getInstance().getRetrofit().create(BreadtripAPI.class)
:breadtripAPI;
}
public static QyerGuideAPI getQyerGuideAPI() {
return qyerGuideAPI==null ?
HttpServiceManager.getInstance().getRetrofit().create(QyerGuideAPI.class)
:qyerGuideAPI;
}
public static TravelNotesAPI getTravelNotesAPI() {
return travelNotesAPI==null ?
HttpServiceManager.getInstance().getRetrofit().create(TravelNotesAPI.class)
:travelNotesAPI;
}
}
第一步
使用上一章生成helloworld的方式,先硬生成一个APIServiceManager class
在我使用apt时发现了一个问题,如果你正确的生成了文件,那么如果你改动了代码,再次生成的时候需要Rebuild Project,但是如果这次的文件生成后报错,那么再次生成只需要点一下Run旁边的小锤子
也就是Make就可以了,所以我发现了个比较笨的调试方式,生成文件时故意搞错某个地方,那么下次生成就比较方便了
ok 那么就先生成一个错误的APIServiceManager class
final String CLASS_NAME="GAPIServiceManager";//这里我已经有了APIServiceManager class,所以为了演示和避免同名就随便加了个字符
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.returns(String.class)//这里原来是返回空值,为了让类报错返回值设置成String
.addParameter(String[].class,"arg")
.addStatement("$T.out.println($S)",System.class,"Hello world")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")//这里是类名也是java文件名,这里要改动成下面的样子。
TypeSpec helloWorld = TypeSpec.classBuilder(CLASS_NAME)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile =JavaFile
.builder("gear.yc.com.gearapplication.network",helloWorld)//这里的目录名是我自己项目的,后面会优化成获取注解类的类名,暂时写死。
.build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
生成后的文件
当然肯定会报错,要的就是这个效果。
public final class GAPIServiceManager {
public static String main(String[] arg) {
System.out.println("Hello world");
}
}
第二步
生成类的变量
FieldSpec fieldSpec =FieldSpec.builder(String.class,"apiService")//因为现在还获取不到类型所以先用String代替
.addModifiers(Modifier.PRIVATE,Modifier.STATIC)
.build();
TypeSpec helloWorld = TypeSpec.classBuilder(CLASS_NAME)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addField(fieldSpec)
.addMethod(main)
.build();
生成后的文件
public final class GAPIServiceManager {
private static String apiService;//已经生成了一个很像的变量
public static String main(String[] arg) {
System.out.println("Hello world");
}
}
可能会有多个参数那么就需要循环生成
List<FieldSpec> fieldSpecs =new ArrayList<>();
for (int i = 0; i <3 ; i++) {//先随便生成几个
FieldSpec fieldSpec =FieldSpec.builder(String.class,"apiService"+i)
.addModifiers(Modifier.PRIVATE,Modifier.STATIC)
.build();
fieldSpecs.add(fieldSpec);
}
TypeSpec helloWorld = TypeSpec.classBuilder(CLASS_NAME)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addFields(fieldSpecs)//这里要改成添加多个的哦
.addMethod(main)
.build();
生成后的文件
public final class GAPIServiceManager {
private static String apiService0;//有好几个比较像的了
private static String apiService1;
private static String apiService2;
public static String main(String[] arg) {
System.out.println("Hello world");
}
}
那么怎样来获取我们想要类型呢,最重要的来了,当然是用上一章添加的注解了...
在api接口上加上注解
有几个这种接口就加几个
@com.gear.apifinder.annotation.APIService
public interface ApiService {
前面代码中已经指定过需要的注解有哪些
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types=new LinkedHashSet<>();
types.add(APIManager.class.getCanonicalName());
types.add(APIService.class.getCanonicalName());
return types;
}
//roundEnv中会有这些被注解的类的element,只需要把他们拿出来使用就可以
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
获取带有APIService的类
List<Element> mServiceElements=new ArrayList<>();//因为有多个类需要容器
private void processOnAPIService(RoundEnvironment environment){
for (Element element : environment.getElementsAnnotatedWith(APIService.class)) {
mServiceElements.add(element);
}
if(mServiceElements.size()==0){//如果没有的话那么就不会生成任何文件
isGenerate=false;//判断是否要生成文件,初值为true
}
}
修改循环代码
if(!isGenerate){//如果没有需要的注解就不生成
return isGenerate;false
}
processOnAPIService(roundEnv);
List<FieldSpec> fieldSpecs =new ArrayList<>();
for (int i = 0; i <mServiceElements.size() ; i++) {
FieldSpec fieldSpec =FieldSpec
//这里只是想看一下加的注解有没有被识别,如果输出数量正确那么就可以继续
//当然如果你想要看element里面有什么其他的东西那么也可以用类似的方法
.builder(String.class,"apiService"+mServiceElements.size())
.addModifiers(Modifier.PRIVATE,Modifier.STATIC)
.build();
fieldSpecs.add(fieldSpec);
}
生成的类
//我四个类上加了注解
private static String apiService4;
private static String apiService4;
private static String apiService4;
private static String apiService4;
好了要开始装逼了...
for (int i = 0; i <mServiceElements.size() ; i++) {
//获取一下element后面要用到,element就是被注解的类,里面所有元素都可以获取到,不懂就看源码很清晰
Element element =mServiceElements.get(i);
//类型名称同理
TypeName typeName=TypeName.get(element.asType());
FieldSpec fieldSpec =FieldSpec
//要改动的就是这个方法的两个参数
.builder(typeName,element.getSimpleName().toString())
.addModifiers(Modifier.PRIVATE,Modifier.STATIC)
.build();
fieldSpecs.add(fieldSpec);
}
生成的类
//已经很像了,只是首字母应该要小写
private static ApiService ApiService;
private static BreadtripAPI BreadtripAPI;
private static QyerGuideAPI QyerGuideAPI;
private static TravelNotesAPI TravelNotesAPI;
我没有找到直接从element中获取小写的方式,只能用方法改变一下首字母了
public static String firstUpperToLetter(String str){
char[] array = str.toCharArray();
array[0] += 32;
return String.valueOf(array);
}
//明显在后面输出方法的时候会用到,用变量接一下
String fieldName=firstUpperToLetter(element.getSimpleName().toString());
FieldSpec fieldSpec =FieldSpec
.builder(typeName,fieldName)
make 一下 ,生成的文件就不贴了,自己看吧
第三步
接下来就要生成方法了,其实和生成变量差不多的
MethodSpec method = MethodSpec.methodBuilder("get"+element.getSimpleName().toString())//生成方法名
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.returns(typeName)
//先硬写一下生成看看,一会慢慢改
.addStatement("return apiService==null ?\n" +
" HttpServiceManager.getInstance().getRetrofit().create(ApiService.class)\n" +
" :apiService;")
.build();
methodSpecs.add(method);
TypeSpec helloWorld = TypeSpec.classBuilder(CLASS_NAME)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addFields(fieldSpecs)
.addMethods(methodSpecs)//添加多个方法
.build();
生成的类
//除了方法的内容基本上差不多了
public static ApiService getApiService() {
return apiService==null ?
HttpServiceManager.getInstance().getRetrofit().create(ApiService.class)
:apiService;;
}
public static BreadtripAPI getBreadtripAPI() {
return apiService==null ?
HttpServiceManager.getInstance().getRetrofit().create(ApiService.class)
:apiService;;
}
修改方法内容
MethodSpec method = MethodSpec.methodBuilder("get"+element.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.returns(typeName)
//这里主要是$T的使用,如果是使用fieldName来拼接字符传的形式,那么不会自动生成import的,所以这里要使用$T.class的方式
//那么为什么不使用$S标识fieldName呢,可以自己试一下。
.addStatement("return "+fieldName+"==null ?\n" +
" HttpServiceManager.getInstance().getRetrofit().create($T.class)\n" +
" :"+fieldName+";",typeName)//此处拼接一个;号纯属为了让类出错,如果去掉你的类就不报错啦
.build();
生成的类
public static ApiService getApiService() {
return apiService==null ?
HttpServiceManager.getInstance().getRetrofit().create(ApiService.class)
:apiService;;//去掉;号这个类就ok了
}
public static BreadtripAPI getBreadtripAPI() {
return breadtripAPI==null ?
HttpServiceManager.getInstance().getRetrofit().create(BreadtripAPI.class)
:breadtripAPI;;
}
当然细心的同学会发现
HttpServiceManager.getInstance().getRetrofit().create
这一句还没有自动生成,那么你就自己来做吧,用上面所说的方式应该很简单的了,别忘记还有个annotation APIManager
小结
到这里使用APT生成文件的一些基础操作就结束了,后面要生成更复杂一些的文件,不过万变不离其宗,使用的思想都是差不多的,这里先留个坑,后面研究好了用一两章来填上。
上面的代码还是很简陋的,具体优化的方式可以根据自己经验去做。
如果大家发现了什么问题,或者某个地方有更好的方式实现也请告诉我,感谢~
最后再推荐一篇文章
android-apt 即将退出历史舞台
章节目录
第一章 APT之生成Retrofit+Rxjava管理类
第二章 APT之HelloWorld Class
第三章 APT之生成接口管理类