Apt实践:Retrofit2.0 + 非Restful API + Apt Converter

这个题目看起来很有档次!有木有大笑,好吧,先来一波词解释

(以下解释,多为百度,稍微有一点自己的理解,看客可以选择性跳过)


Retrofit2.0 :和Java领域的ORM概念类似, ORM把结构化数据转换为Java对象,而Retrofit 把REST API返回的数据转化为Java对象方便操作。同时还封装了网络代码的调用。以下为个人理解:retrofit 这个玩意吧,首先是个用来进行网络请求,然后,retrofit内部是希望完成发起请求到生成bean的过程。最后,配合RxJava口味更加。这里提一句,如果没有使用RxJava,咱项目还使用Okhttp吧,没必要自己为难自己。


非Restful API :这里没百度到。还是举个粟子吧,糖炒的,拿这次的接口来说,请求城市列表,我所需要的正确的数据

[
    {
      "cityid": "1",
      "parentid": "0",
      "citycode": "101010100",
      "city": "北京"
    },
    {
      "cityid": "24",
      "parentid": "0",
      "citycode": "101020100",
      "city": "上海"
    }
]

以上是Reetful APT ,但是我们现在大部分用到的数据是:
{
  "status": "0",
  "msg": "ok",
  "result": 
  [
    {
      "cityid": "1",
      "parentid": "0",
      "citycode": "101010100",
      "city": "北京"
    },
    {
      "cityid": "24",
      "parentid": "0",
      "citycode": "101020100",
      "city": "上海"
    }
  ]
}

这样数据,加入了statust状态与msg ,这样可以方便让我们做一些事情,哪一些事情?反正不是嘿嘿的事情,理论上,开始跟后台商(p)量(y)好,后台给你反的数据应该是保持这样的

{
  "status": "0",
  "msg": "ok",
  "result": "主要数据"
}
这种便是非Restful API的基本情况


Apt: APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。

随着Android Gradle 插件 2.2 版本的发布,Android Gradle 插件提供了名为 annotationProcessor 的功能来完全代替 android-apt ,自此android-apt 作者在官网发表声明证实了

后续将不会继续维护 android-apt ,并推荐大家使用 Android 官方插件annotationProcessor.
Apt 个人理解,本人也是第一次接触Apt,本次也算试手,apt 对于我来说,就是用注释生成一些脑残代码。所谓脑残,就是一些有规律的代码。比如通过Swtich 语句返回不同的Converter,这个往下看就能看到了


Converter :英文为转换器,在retrofit的使用中,Converter的作用,就是将返回的数据,进行解析,生成你想要的数据。Converter的实体化是是通过工厂模式,本人常用GsonResponseBodyConverter为官方提供,but,这个Converter只是用来Restful能用。。。也就是说,基本是没有什么项目会使用,二次but,本人的Converter的基本是模仿官方,还是有用的~~


-----------------------------------------以下是重点----------------------------------

对于Converter基本使用,我还是推荐如何使用Retrofit请求非Restful API, 这里重点是为Converter的生成方式加入APT,说明一下使用情况,就是跟后台锅锅没有商量好,

每一个接口的解析过程不能用通用的解析过程。也就是说,需要的新的Converter。这时候,翠花,上菜,呸!上接口!

    @GET("weather/city")
    Observable<ArrayList<CityBean>> getAllCity();

    @GET("weather/query")
    Observable<CityWeatherBean> getCityWeatherService(@Query("city") String city, @Query("citycode") String citycode, @Query("cityid") String cityid);

我们假定,getAllCity,与getCityWeatehrerSercice,并不是一种数据格式,分别需要DefaultConverter与SpicalConverter两个解析类来解析。而项目中的使用情况为,Retrofit为单例,通过addConverterFactory加入工厂, ConverterFactory的方法responseBodyConverter(Type,Annotation[],retrofit)来生成Converter,

这时,我们机会来了,请盯紧我们的方法中第二个参数,这是一个注解的集合,那么这个集合是怎么来的?怎么来的我还真是不知道,but,我知道集合有接口请求的注解(我怎么

知道的?上个文章里面有介绍。。。),那么,也就是说,我们也可以写一个注解,标识在上面,让Retrofit来获得,那么,这样可以让工厂通过这个标识,来生成相应的Converter,

说干就干。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ConverterInstance {
    Class value();
}
稍微解释一下这个注解,@target这个表示用在方法之上,@Retention RUNTIME 注解会在class字节码文件中存在,在运行时可以通过反射获取到,value()方法,使用时

需要指定一个类,这个,我们的注解就可以使用了。放到我们的接口上

    @ConverterInstance(DefualtConverter.class)
    @GET("weather/city")
    Observable<ArrayList<CityBean>> getAllCity();
这样,ConverterFactory便可以得到相应的数据了。

 for (Annotation a : annotations) {
            if (a instanceof ConverterInstance) {
                Class c = ((ConverterInstance) a).value();
                //实例化过程
                break;
            }
        }

这时候,比较熟悉java的知道,既然知道了Class,可以通过反射来new ,反射的问题,一直都被人讨论,就算是性能低下,但我们也没有停止使用,但是今天,我们并不需要

反射。那么问题来了,怎么直接new 这个类呢。说一个比较的傻的做法,自己写Swtich语句~~

 public static AbstractConverter create(Class mClass) {
        switch (mClass.getSimpleName()) {
            case "DefualtConverter":
                return new DefualtConverter();
            case "SpecialConvert":
                return new SpecialConvert();
            default:
                return new DefualtConverter();
        }
    }

看起来还不错,起码项目应该能运行,但是,问题也来了,两个也好写,如果多了呢,不多说,10个,写的时候,你会不会在想,这么脑残的设计,谁想的。。。是不是宁愿用

反射。当你有这种想法的时候,APT出现了,踩着七彩祥云来拯救世界了。我们利用APT,生成这种所谓的脑残代码,解放我们自己的思路。这里我们上一下主要的代码

    /**
     * 生成NewConverter
     */
    private void onConverter() {
        //类名
        String Class_Name = "NewConverter";
        //类设定  public final class NewConverter
        TypeSpec.Builder tb = classBuilder(Class_Name)
                .addModifiers(PUBLIC, FINAL)
                .addJavadoc("by Apt,用来返回一些特别的Converter 生成代码见apt 中的AnnotationProcessor nnConverter() \n")
                .addJavadoc("为什么不用@link...因为用了也不管用 \n");
        //方法设定 public static AbstractConverter create(Class mClass)
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("create")
                .addJavadoc("by apt \n")
                .returns(ClassName.get("com.yu.zz.retrofitapt.Retrofit", "AbstractConverter"))
                .addModifiers(PUBLIC, STATIC)
                .addParameter(Class.class, "mClass");

        //switch 语句
        CodeBlock.Builder blockBuilder = CodeBlock.builder();
        //括号开始
        blockBuilder.beginControlFlow(" switch (mClass.getSimpleName())");
        ArrayList<ClassName> names = new ArrayList<>();
        for (Element element : roundEnv.getElementsAnnotatedWith(ConverterInstance.class)) {
            ClassName className = null;
            try {
                Class currentType = element.getAnnotation(ConverterInstance.class).value();
                className = ClassName.get(currentType);
            } catch (MirroredTypeException mte) {
                DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();
                TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
                className = ClassName.get(classTypeElement);
            }
            //Case 语句:case 所列举的不能有重复,加入一层list的判断:
            if (!names.contains(className)) {
                names.add(className);
                blockBuilder.addStatement("case $S:  \n return  new $T()", className.simpleName(), className);
            }
        }

        //swtich default语句,返回默认的Converter
        blockBuilder.add("default: \n return new $T();\n", ClassName.get("com.yu.zz.retrofitapt.Retrofit", "DefualtConverter"));
        //括号线束
        blockBuilder.endControlFlow();
        //方法中加入代码
        methodBuilder.addCode(blockBuilder.build());
        //类中加入方法
        tb.addMethod(methodBuilder.build());
        // 生成源代码
        JavaFile javaFile = JavaFile.builder("com.apt", tb.build()).build();
        try {
            javaFile.writeTo(mFiler);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }


上面的生成方法,引用了三方库javapoet。

下面看一下我们的生成代码

package com.apt;

import com.yu.zz.retrofitapt.Retrofit.AbstractConverter;
import com.yu.zz.retrofitapt.Retrofit.DefualtConverter;
import com.yu.zz.retrofitapt.Retrofit.SpecialConvert;

/**
 * by Apt,用来返回一些特别的Converter 生成代码见apt AnnotationProcessor OnConverter
 * 为什么不用@link...因为用了也不管用
 */
public final class NewConverter {
    /**
     * by apt1
     */
    public static AbstractConverter create(Class mClass) {
        switch (mClass.getSimpleName()) {
            case "DefualtConverter":
                return new DefualtConverter();
            case "SpecialConvert":
                return new SpecialConvert();
            default:
                return new DefualtConverter();
        }
    }
}
这里,我复制了全部,包括package与iimport ,只是为了证明,这是些都是自动生成。这个Class是没问题的。

完成的APT,后,能过ReBuild的项目,我的代码就可以在项目中直接调用,这时候,更新我们的ConverterFactory

更新后的ConverterFactory

  @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type,
                                                            Annotation[] annotations,
                                                            Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        AbstractConverter<?> converter = null;

//        DefualtConverter converter = new DefualtConverter();
//        converter.setAdapter(adapter);

        //找到方法中的所有注解,找到我们自定义的ConverterInstance
        for (Annotation a : annotations) {
            if (a instanceof ConverterInstance) {
                Class c = ((ConverterInstance) a).value();
                converter = NewConverter.create(c);
                break;
            }
        }
        //如果没有标明用什么Converter ,生成默认的Converter
        if (null == converter)
            converter = new DefualtConverter();
        converter.setAdapter(adapter);
        return converter;
    }
到这,是不是觉得一脸懵逼,但是,我们的注释已经开始愉快的使用了。再也不怕后台妖孽了。(至少,我还没有碰到~~哈哈)。

ps:github :https://github.com/yehengzhishang/RetrofitAPT

ps2.0:在MaInAcitiy中加入了注释,为项目构建过程,在相应地方也加入了注释

还是来个例子

/**
 * step 1 新建一个project (加入联网权限,以及为Layout的设定)
 * <p>
 * step 2 gradle app 导入我们需要的三方数据
 * <p>
 * step 3 开始写网络请求(本次网络接口,用的阿里云的免费天气接口,为什么用这个,,,因为是免费的)
 * 3.1 写出实体bean {@link CityBean,com.yu.zz.retrofitapt.Bean.CityWeatherBean}
 * 3.2 写出rerforit 所需interface {@link com.yu.zz.retrofitapt.API.WeatherService}
 * 3.3 对于API 的封装 {@link com.yu.zz.retrofitapt.API.Api}
 * <p>
 * step 4 对于Converter第一次 代码设计
 * 4.1 通用的Converter {@link com.yu.zz.retrofitapt.Retrofit.DefualtConverter}
 * 4.2 ConverterFactory {@link com.yu.zz.retrofitapt.Retrofit.DefualtConverterFactory}
 * <p>
 * step 5 接口调试 (项目准备了两个接口,但是实际测试目前为止,只试过一个)
 * 5.1 recyclerview Adapter {@link CityAdapter}
 * 5.2 数据调试 {@link #getNetData()}
 * <p>
 * step 6. 加入apt ,生成{@link MyApiFactory}
 * 6.1 新建 module :lib  新建注解 {@link com.zz.yu.lib.ApiFactory}
 * 6.2 新建 module :apt (要选择 java library ,并且对gradle :apt进行三方库的引入)
 * 6.3 apt 新建Processor (AnnotationProcessor) 代码设计 onApi()
 * 6.4 加入注解 {@link com.yu.zz.retrofitapt.API.WeatherService} ApiFactory
 * 代码替换 {@link #getNetData()}
 * <p>
 * step 7 利用Apt 生成 {@link com.apt.NewConverter}
 * 7.1 lib 新建注解 {@link com.zz.yu.lib.ConverterInstance}
 * 7.2  Converter更改 {@link com.yu.zz.retrofitapt.Retrofit.DefualtConverter}
 * 新增{@link com.yu.zz.retrofitapt.Retrofit.AbstractConverter}
 * 新增 apt AnnotationProcessor 中加入 onConverter()
 * 7.3 {@link WeatherService#getAllCity()} 加入注解
 * 7.4 更改 {@link com.yu.zz.retrofitapt.Retrofit.DefualtConverterFactory } 逻辑
 */
public class MainActivity extends AppCompatActivity {

代码中相应的提示

  @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type,
                                                            Annotation[] annotations,
                                                            Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        AbstractConverter<?> converter = null;

              /*------------------step 4.2 始-------------------*/
//        DefualtConverter converter = new DefualtConverter();
//        converter.setAdapter(adapter);
              /*------------------step 4.2 终-------------------*/


              /*------------------step 7.4 始-------------------*/
        //找到方法中的所有注解,找到我们自定义的ConverterInstance
        for (Annotation a : annotations) {
            if (a instanceof ConverterInstance) {
                Class c = ((ConverterInstance) a).value();
                converter = NewConverter.create(c);
                break;
            }
        }
        //如果没有标明用什么Converter ,生成默认的Converter
        if (null == converter)
            converter = new DefualtConverter();
        converter.setAdapter(adapter);
            /*------------------step 7.4 终-------------------*/
        return converter;
    }
用 step +始或终标识,表示设计过程。方便理解

ps3.0 感谢 项目T mvp作者 North_2016 的支持 以及各类博客作者,太多,不一一列举,如有侵权,请通知我,我立即删除。


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值