Android 利用APT对网络请求进行极简封装

团队并行开发,每个人对自己模块的api进行封装,

public interface UserAPI {


    @POST("/mobile/settings/login.html")
    @FormUrlEncoded
    Flowable doLogin(@QueryMap Map map);


    @POST("/mobile/settings/logout.html")
    @FormUrlEncoded
    Flowable doLogout();



}

public interface IRetrofitWeather {

    /**
     * retrofit 封装
     * @param location
     */
    @FormUrlEncoded
    @POST("telematics/v3/weather?")
    Call getWeather(@Field("location") String location,
                                  @Field("output") String ouput,
                                  @Field("ak") String ak);


}

然后利用Retrofit的create方法创建出对应的apiservice进行网络请求,各个模块都需要含有apiservice实例的简单单例封装,那么多个模块就有多个单例,这个时候就需要工厂模式进行设计。

    @Override
    public void getWeather(String location, Callback callback) {

        IRetrofitWeather api = retrofit.create(IRetrofitWeather.class);

        Call call = api.getWeather(location, APIConstant.URL.OUTPUT, APIConstant.URL.AK);

        call.enqueue(callback);

    }

如果模块需要调用User模块的接口时,就会有很麻烦。而且不可避免的抽象工厂需要设计多个抽象类和方法,使得设计非常绕,层次多,类多。那么我们利用APT(Annotation Processing Tool)机制,动态的生成HttpClient可以解决以上问题。

@HttpClient
public interface UserAPI 

首先,我们在每个需要进行网络调用的api加上@HttpClient标签,

public final class HttpClient {
  /**
   * @created by apt
   */
  public static Flowable login(String username, String password, Map map) {
    return Api.getInstance().retrofit.create(com.jason.UserService.class).login(username,password,map).compose(RxSchedulers.io_main());
  }
  public static Flowable getWeather(String location, Map map) {
    return Api.getInstance().retrofit.create(com.jason.WeatherService.class).getWeather(location,map).compose(RxSchedulers.io_main());
  }
}

在编译时系统HttpClientProccessor运行时会自动生成HttpClient类,并且各个模块的api方法都会在HttpClient中生成,HttpClient中封装了Retrofit通用单例和Rxjava的通用部分,免去了上面封装需要的callback。

                HttpClient.login(username, pwd, defaultMap)
                .subscribe(loginEntity -> {
                    Log.i("LoginPresenter", "onNext userEntity:"+loginEntity);
                    getView().loginSuccess(loginEntity);
                }, throwable -> {
                    Log.i("LoginPresenter", "error msg:"+throwable.toString());
                })

各个模块使用时可直接使用HttpClient中的public静态方法,Rxjava免去了我们去写另外一个包含callback或listener的接口,也就不需要工厂类做解耦设计。


public class HttpClientProcessor implements IProcessor {
    @Override
    public void process(RoundEnvironment roundEnv, AnnotationProcessor mAbstractProcessor) {
        String CLASS_NAME = "HttpClient";
        
        TypeSpec.Builder tb = classBuilder(CLASS_NAME).addModifiers(PUBLIC, FINAL).addJavadoc("@API factory created by apt");
        try {
            for (TypeElement element : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(HttpClient.class))) {
                mAbstractProcessor.mMessager.printMessage(Diagnostic.Kind.NOTE, "正在处理: " + element.toString());
                for (Element e : element.getEnclosedElements()) {
                    ExecutableElement executableElement = (ExecutableElement) e;
                    MethodSpec.Builder methodBuilder =
                            MethodSpec.methodBuilder(e.getSimpleName().toString())
                                    .addJavadoc("@created by apt")
                                    .addModifiers(PUBLIC, STATIC);

                        methodBuilder.returns(TypeName.get(executableElement.getReturnType()));
                        String paramsString = "";
                        for (VariableElement ep : executableElement.getParameters()) {
                            methodBuilder.addParameter(TypeName.get(ep.asType()), ep.getSimpleName().toString());
                            paramsString += ep.getSimpleName().toString() + ",";
                        }
                        methodBuilder.addStatement(
                                "return $T.getInstance()" +
                                        ".service.$L($L)" /*+
                                        ".compose($T.io_main())"*/
                                , ClassName.get("com.jason.api", "OkHttpClient")
                                , e.getSimpleName().toString()
                                , paramsString.substring(0, paramsString.length() - 1)
                                
                        tb.addMethod(methodBuilder.build());
                    
                }
            }
            JavaFile javaFile = JavaFile.builder(Utils.PackageName, tb.build()).build();// 生成源代码
            javaFile.writeTo(mAbstractProcessor.mFiler);// 在 app module/build/generated/source/apt 生成一份源代码
        } catch (FilerException e) {
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

HttpClientProccessor的代码,利用AOP 切片编程的思想,在编译时去遍历所有Java文件,包含@HttpClient注解的类的方法,都会引入到新生成的HttpClient这个类中。


public final class HttpClient {
  /**
   * @created by apt
   */
  public static Flowable login(String username, String password, Map map) {
    return OkHttpClient.getInstance().retrofit.create(com.jason.UserService.class).login(username,password,map).compose(RxSchedulers.io_main());
  }
  public static Flowable getWeather(String location, Map map) {
    return OkHttpClient.getInstance().retrofit.create(com.jason.WeatherService.class).getWeather(location,map).compose(RxSchedulers.io_main());
  }
}

Api是对对各个模块的通用的Retrofit serviceApi,我们看到HttpClient将Weather和User的接口都加入进来,所有接口都在HttpClient中,HttpClient是自动生成,

利用APT的方式,我们生成HttpClient的方式免去了多人去修改工厂类的情况,这种方式由于编译时需要遍历的关系,编译时会慢一点。

感谢North文章的帮助

http://www.jianshu.com/p/dca3e2c8608a


你可能感兴趣的:(Android,框架)