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

时间:2021-03-09 17:47:09

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

public interface UserAPI {


@POST("/mobile/settings/login.html")
@FormUrlEncoded
Flowable<UserEntity> doLogin(@QueryMap Map<String, Object> map);


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



}

public interface IRetrofitWeather {

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


}

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

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

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

Call<WeatherApiData> 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<LoginEntity> login(String username, String password, Map<String, Object> map) {
return Api.getInstance().retrofit.create(com.jason.UserService.class).login(username,password,map).compose(RxSchedulers.io_main());
}
public static Flowable<WeatherEntity> getWeather(String location, Map<String, Object> 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<LoginEntity> login(String username, String password, Map<String, Object> map) {
return OkHttpClient.getInstance().retrofit.create(com.jason.UserService.class).login(username,password,map).compose(RxSchedulers.io_main());
}
public static Flowable<WeatherEntity> getWeather(String location, Map<String, Object> 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