0
点赞
收藏
分享

微信扫一扫

Retrofit系列文章(四) - 手写Retrofit核心架构部分

菜菜捞捞 2021-09-29 阅读 65

前言

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

举报

相关推荐

0 条评论