前言
Retrofit系列文章
Retrofit系列文章(一) - Retrofit简介
Retrofit系列文章(二) - Retrofit常见问题的解决
Retrofit系列文章(三) - Retrofit源码分析
Retrofit系列文章(四) - 手写Retrofit核心架构部分
拓展 - 应用是怎么来的?
1. 概述
Retrofit 其实就是 对于 OkHttp的封装,解耦,采用的是动态代理。主要有3个步骤:
第一步:动态代理:每一个方法的执行都会调用 retrofit.create()方法中的动态代理;
第二步:解析参数注解;
第三步:封装 OkHttpCall,发起一个网络请求;
2. 具体实现如下:
这里只是写 GET请求、POST请求、QUERY参数注解
1>:对于 GET请求、POST请求、QUERY参数注解:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 16:39
* Version 1.0
* Params:
* Description:
*/
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
@Target(ElementType.METHOD) // 注解放的位置是方法上边
public @interface GET {
String value();
}
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 16:39
* Version 1.0
* Params:
* Description:
*/
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
@Target(ElementType.METHOD) // 注解放在方法上边
public @interface POST {
String value();
}
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 16:40
* Version 1.0
* Params:
* Description:
*/
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
@Target(ElementType.PARAMETER) // 注解放的位置是 参数上边
public @interface Query {
String value();
}
2>:入口在MainActivity中:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RetrofitClient.getServiceApi().userLogin("Novate" , "123456")
.enqueue(new Callback<UserLoginResult>() {
@Override
public void onResponse(Call<UserLoginResult> call, Response<UserLoginResult> response) {
Log.e("TAG" , response.body.toString()) ;
}
@Override
public void onFailure(Call<UserLoginResult> call, Throwable t) {
}
});
}
}
3>:点击MainActivity中的.getServiceApi(),进入到RetrofitClient中:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 17:36
* Version 1.0
* Params:
* Description:
*/
public class RetrofitClient {
private final static ServiceApi mServiceApi;
static {
OkHttpClient okHttpClient = new OkHttpClient
.Builder().addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override // 处理不打印log日志问题
public void log(String message) {
Log.e("TAG",message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
Retrofit retrofit = new Retrofit.Builder()
// 访问后台接口的主路径
.baseUrl("http://192.168.8.169:8080/OkHttpServer/")
// 添加 OkHttpClient,不添加默认就是 光杆 OkHttpClient
.client(okHttpClient)
.build();
// 创建一个 实例对象
mServiceApi = retrofit.create(ServiceApi.class);
}
public static ServiceApi getServiceApi() {
return mServiceApi;
}
}
4>:点击 retrofit.create(),进入 Retrofit代码中:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 18:43
* Version 1.0
* Params:
* Description:
*/
public class Retrofit {
final String baseUrl;
final okhttp3.Call.Factory callFactory;
// ConcurrentHashMap: 线程安全的 HashMap
private Map<Method,ServiceMethod> serviceMethodMapCache = new ConcurrentHashMap<>();
public Retrofit(Builder builder){
this.baseUrl = builder.baseUrl ;
this.callFactory = builder.callFactory ;
}
public <T>T create(Class<T> service) {
// 重点在这里
// 第1步:动态代理
return (T)Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 每个方法的执行 都会来到这个动态代理
Log.e("TAG" , "methodName: " + method.getName()) ;
// 判断是不是 Object 的方法
if (method.getDeclaringClass() == Object.class){
return method.invoke(this , args) ;
}
// 第2步:解析参数注解
ServiceMethod serviceMethod = loadServiceMethod(method) ;
// 第3步:封装 OkHttpCall
OkHttpCall okHttpCall = new OkHttpCall(serviceMethod , args) ;
return okHttpCall;
}
});
}
private ServiceMethod loadServiceMethod(Method method) {
// 享元设计模式,其实就是做一个缓存
ServiceMethod serviceMethod = serviceMethodMapCache.get(method);
if (serviceMethod == null){
serviceMethod = new ServiceMethod.Builder(this , method).build() ;
serviceMethodMapCache.put(method , serviceMethod) ;
}
return serviceMethod;
}
public static class Builder {
String baseUrl ;
okhttp3.Call.Factory callFactory ;
public Builder baseUrl(String baseUrl) {
this.baseUrl = baseUrl ;
return this ;
}
public Builder client(okhttp3.Call.Factory callFactory) {
this.callFactory = callFactory ;
return this ;
}
public Retrofit build() {
if (callFactory == null){
callFactory = new OkHttpClient() ;
}
return new Retrofit(this);
}
}
}
5>:凡是使用 Retrofit的方法,每个方法的执行都会首先进入到 Retrofit中的 create中的 动态代理方法中,然后解析参数注解,然后封装OkHttpClient
6>:访问后台数据的 接口类如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 16:46
* Version 1.0
* Params:
* Description: 请求后台访问数据的 接口类
*/
public interface ServiceApi {
// 接口涉及到解耦,userLogin 方法是没有任何实现代码的
// 如果有一天要换其他的网络框架 比如:GoogleHttp
@GET("LoginServlet")// 登录接口 GET(相对路径)
Call<UserLoginResult> userLogin(
// @Query(后台需要解析的字段)
@Query("userName") String userName,
@Query("password") String userPwd);
// POST
// 上传文件怎么用?
}
7>:ServiceMethod类如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 18:54
* Version 1.0
* Params:
* Description:
*/
public class ServiceMethod {
final Retrofit retrofit;
final Method method;
String httpMethod;
String relativeUrl;
final ParameterHandler<?>[] parameterHandlers;
public ServiceMethod(Builder builder){
this.retrofit = builder.retrofit;
this.method = builder.method;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.parameterHandlers = builder.parameterHandlers;
}
public okhttp3.Call createNewCall(Object[] args) {
// 还需要一个对象,专门用于添加参数的
RequestBuilder requestBuilder = new RequestBuilder(retrofit.baseUrl , relativeUrl , httpMethod , parameterHandlers , args) ;
// 添加参数
String url = retrofit.baseUrl + relativeUrl ;
return retrofit.callFactory.newCall(requestBuilder.build());
}
public <T>T parseBody(ResponseBody responseBody) {
// 要获取解析的类型 T 获取方法返回值的类型
// 获取返回值所有对象的泛型
Type returnType = method.getGenericReturnType();
Class <T> dataClass = (Class <T>) ((ParameterizedType) returnType).getActualTypeArguments()[0];
// 解析工厂去转换
Gson gson = new Gson();
T body = gson.fromJson(responseBody.charStream(),dataClass);
return body;
}
public static class Builder {
final Retrofit retrofit ;
final Method method ;
final Annotation[] methodAnnotations ;
String httpMethod;
String relativeUrl;
Annotation[][] parameterAnnotations ;
final ParameterHandler<?>[] parameterHandlers ;
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit ;
this.method = method ;
methodAnnotations = method.getAnnotations() ;
// 二维数组
parameterAnnotations = method.getParameterAnnotations() ;
parameterHandlers = new ParameterHandler[parameterAnnotations.length] ;
}
public ServiceMethod build() {
// 在这里解析,OkHttp 请求的时候:url = baseUrl + relativeUrl(相对路径)、method
for (Annotation methodAnnotation : methodAnnotations) {
// 解析 GET、POST
parseAnnotationMethod(methodAnnotation) ;
}
// 解析参数注解
int count = parameterHandlers.length ;
for (int i = 0 ; i < count ; i++) {
Annotation parameter = parameterAnnotations[i][0];
// 获取的是 Query、QueryMap等等参数注解 或者其他
Log.e("TAG" , "parameter = " + parameter.annotationType().getName()) ;
// 这里会涉及到 模板、策略设计模式
if (parameter instanceof Query){
// 一个一个封装成 ParameterHandler,不同的参数注解 选择 不同的 策略
parameterHandlers[i] = new ParameterHandler.Query<>(((Query) parameter).value()) ;
}
}
return new ServiceMethod(this);
}
private void parseAnnotationMethod(Annotation methodAnnotation) {
// 获取value,请求方法
if (methodAnnotation instanceof GET){
parseMethodAndPath("GET" , ((GET) methodAnnotation).value()) ;
}else if (methodAnnotation instanceof POST){
parseMethodAndPath("POST" , ((POST) methodAnnotation).value()) ;
}
}
private void parseMethodAndPath(String method, String value) {
this.httpMethod = method ;
this.relativeUrl = value ;
}
}
}
8>:Call代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 16:45
* Version 1.0
* Params:
* Description:
*/
public interface Call<T> {
void enqueue(Callback<T> callback);
}
9>:Callback接口如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 17:37
* Version 1.0
* Params:
* Description:
*/
public interface Callback<T> {
/**
* Invoked for a received HTTP response.
* <p>
* Note: An HTTP response may still indicate an application-level failure such as a 404 or 500.
* Call {@link Response#isSuccessful()} to determine if the response indicates success.
*/
void onResponse(Call<T> call, Response<T> response);
/**
* Invoked when a network exception occurred talking to the server or when an unexpected
* exception occurred creating the request or processing the response.
*/
void onFailure(Call<T> call, Throwable t);
}
10>:封装的OkHttpClient代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 17:42
* Version 1.0
* Params:
* Description:
*/
public class OkHttpCall<T> implements Call<T> {
final ServiceMethod serviceMethod ;
final Object[] args ;
public OkHttpCall(ServiceMethod serviceMethod , Object[] args){
this.serviceMethod = serviceMethod ;
this.args = args ;
}
@Override
public void enqueue(final Callback<T> callback) {
// 在这里发起一个请求,给一个回调就可以了
Log.e("TAG" , "正式的发起一个请求") ;
okhttp3.Call call = serviceMethod.createNewCall(args) ;
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
if (callback != null){
callback.onFailure(OkHttpCall.this , e);
}
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
// 解析Response -> Resposne<T> 回调
Log.e("TAG" , response.body().toString()) ;
// 涉及到解析,不能在这里写死,需要使用 ConvertFactory工厂
Response rResponse = new Response() ;
rResponse.body = serviceMethod.parseBody(response.body()) ;
if (callback != null){
callback.onResponse(OkHttpCall.this , rResponse);
}
}
});
}
}
11>:ParameterHandler代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 18:38
* Version 1.0
* Params:
* Description:
*/
public interface ParameterHandler<T> {
public void apply(RequestBuilder requestBuilder,T value);
// 这里有很多策略:比如Query、QueryMap、Part、Filed等等
class Query<T> implements ParameterHandler<T>{
// 代表的是:保存的参数的key,就是 给后台要提交的 userName、userPwd
private String key;
public Query(String key){
this.key = key;
}
@Override
public void apply(RequestBuilder requestBuilder,T value) {
// 添加到 Request中 value -> String 要经过一个工厂
requestBuilder.addQueryName(key,value.toString());
}
}
// 还有一些其他
}
12>:RequestBuilder代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 18:02
* Version 1.0
* Params:
* Description:
*/
public class RequestBuilder {
ParameterHandler<Object>[] parameterHandlers ;
Object[] args ;
HttpUrl.Builder httpUrl ;
public RequestBuilder(String baseUrl, String relativeUrl, String httpMethod, ParameterHandler<?>[] parameterHandlers, Object[] args) {
this.parameterHandlers = (ParameterHandler<Object>[]) parameterHandlers;
this.args = args ;
httpUrl = HttpUrl.parse(baseUrl + relativeUrl).newBuilder() ;
}
public Request build() {
//
int count = args.length ;
for (int i = 0 ; i < count ; i++) {
// userName = Novate
parameterHandlers[i].apply(this , args[i]);
}
// 在这里构建一个请求
Request request = new Request.Builder().url(httpUrl.build()).build();
return request;
}
public void addQueryName(String key, String value) {
// userName = Novate&userPwd = 123456
httpUrl.addQueryParameter(key , value) ;
}
}
13>:服务器响应代码:Response如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/6/30 17:38
* Version 1.0
* Params:
* Description:
*/
public class Response<T> {
public T body;
}
以上就是:手写 Retrofit 的核心代码;
3. 总结
其实,Retrofit核心代码逻辑就是:
1>:凡是 通过 Retrofit调用的 方法,首先都会调用 Retrofit.create()方法中的动态代理;
2>:然后解析参数注解;
3>:最后封装 OkHttpCall,然后在它里边的 enqueue()方法中 发起一个请求,然后再给一个 回调就可以了;
代码已上传至github:
https://github.com/shuai999/Architect_day34.git