手把手实现一个mini-Retrofit框架

前文

本篇文章将采用按部就班的编码方式,从零开始实现一个Retorift框架;在实现过程当中不断提出问题并分析实现,最终开发出一个mini版的Retrofit框架java

演示一个使用OkHttp的项目Demo

为了更好的演示框架的实现过程,这里我先建立了一个简单的Demo项目android

这个Demo项目中主要包含3个部分git

  1. Json数据对应JavaEntity类
  2. 项目中包装网络请求回调的Callback
  3. 一个包含项目全部网络接口请求的管理类RestService

JavaBean

@Data
@ToString
public class BaseResponse<T> {
    private boolean error;
    private T results;
}
复制代码
package com.knight.sample.entity;

import java.util.List;
import java.util.Map;

public class XianduResponse extends BaseResponse<List<GankEntity>> {
}

复制代码

NetCallback

package com.knight.sample;

import java.io.IOException;

/** * 项目封装的统一网络请求的回调 * @param <T> */
public interface NetCallback<T> {
    void onFailure(Exception e);

    void onSuccess(T data);
}
复制代码

NetWorkService

package com.knight.sample;

import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class RestService {
    private static OkHttpClient okHttpClient;
    public static void init() {
        okHttpClient = new OkHttpClient.Builder()
                .build();
    }

    public static<T> void todayGank(Class<T> responseClazz,NetCallback<T> callback) {
        Request request = new Request.Builder().url("http://gank.io/api/today")
                .get()
                .build();
        okHttpClient.newCall(request).enqueue(new WrapperOkHttpCallback<>(responseClazz,callback));
    }

    public static<T> void xianduGank(int count, int page,Class<T> responseClazz,NetCallback<T> callback) {
        Request request = new Request.Builder()
                .url("http://gank.io/api/xiandu/data/id/appinn/count/" + count + "/page/" + page)
                .get().build();
        okHttpClient.newCall(request).enqueue(new WrapperOkHttpCallback<>(responseClazz,callback));
    }

    static class WrapperOkHttpCallback<T> implements Callback {
        private static Gson gson = new Gson();
        private Class<T> clazz;
        private NetCallback<T> callback;

        public WrapperOkHttpCallback(Class<T> responseClazz, NetCallback<T> netCallback) {
            this.clazz = responseClazz;
            this.callback = netCallback;
        }

        @Override
        public void onFailure(Call call, IOException e) {
            Log.e("WrapperOkHttpCallback", "onFailure");
            e.printStackTrace();
            callback.onFailure(e);
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            JsonReader jsonReader = gson.newJsonReader(response.body().charStream());
            T entity = gson.getAdapter(clazz).read(jsonReader);
            Log.d("response", entity.toString());
            callback.onSuccess(entity);

        }
    }

}

复制代码

在NetworkService类中咱们目前定义了2个Http 请求 todayGankxianduGank ,目前两个请求方式都是 Get 其中 xianduGank 须要传入 countpage参数分别表示每页数据的数据以及请求的页码,除此以外这两个网络请求都须要传入 一个Class对象表示响应的Json数据对应的Model,以便在内部使用Gson来解析,以及网络请求的异步回调 NetCallbackgithub

咱们不直接使用OkHttp提供的Callback 而是在内部简单的作了封装转换成项目本身的NetCallback,由于对项目的开发人员来讲,更但愿的是可以直接在Callback的success回调中直接获得响应的Json数据对应的JavaBean.编程

本次提交详细代码见json

思考项目现状

上文模拟的代码只是一个简单的例子,可能会有更好的封装方式,但这并非咱们这篇文章想要讨论的重点。 咱们回到示例中RestService类中的代码部分,看下目前网络请求的写法;设计模式

由于咱们项目中已经有了OKHttp这个网络库了,有关Http具体的链接及通讯的脏话累活均可以交给它来处理,对于项目开发者,事实上咱们只须要配置如下Http请求部分api

  • 请求的url 地址
  • 请求的方式 (GET、POST、PUT...)
  • 请求内容

假设咱们已经具有了 Java注解 以及 动态代理的相关知识,并了解如下知识缓存

  • 注解能够添加在方法上
  • Retention为RUNTIME的注解能够在虚拟机运行时也获取到注解上的信息
  • Java的动态代理能够运行时生成原接口类型的代理实现类对象并hook方法的调用

每个网络接口调用请求的url地址和请求方式都是惟一的 ,那么对于一个简单的网络请求 咱们能不能使用 注解 + 动态代理 来简化这一过程,改成声明式的编程方式来实现网络调用,好比就像这样性能优化

/** * Created by zhuoxiuwu * on 2019/4/25 * email nimdanoob@gmail.com */
public interface NetRestService {

    @GET("http://gank.io/api/today")
    public Call todayGank();
}
复制代码

咱们在一个抽象接口类中添加了一个方法,在方法上添加了注解 @GET 表示这是一个Http GET请求的调用
注解中GET带的默认参数表示GET请求的地址。声明这个方法后,咱们再经过Java动态代理技术在运行时解析这个方法上的注解的信息,内部经过调用OKHttp的相关方法生成一个 Call对象

有了大概思路了,咱们接下来先简单的实现这样一个小例子来验证咱们的想法是否可行

编码实现

3.1 简单实现一个支持GET、POST请求的Retrofit

新建一个注解类@GET

package retrofit2.http;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/** * Created by zhuoxiuwu * on 2019/4/25 * email nimdanoob@gmail.com */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
    //注解中 方法名写成value 这样的话,在使用注解传入参数时就不用带key了,它会做为一个默认的调用
    String value();
}
复制代码

新建一个处理Http接口类的动态代理的类Retrofit,由于咱们实际网络请求的调用是依赖OKHttp,因此咱们要求构造函数传入OkHttp对象
目前Retrofit 类只有一个方法public T createService(final Class service) 它接收一个抽象类,并生成该抽象类的代理实现。

package retrofit2;



import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.http.GET;

public class Retrofit {

    private OkHttpClient mOkHttpClient;

    public Retrofit(OkHttpClient mOkHttpClient) {
        this.mOkHttpClient = mOkHttpClient;
    }

    @SuppressWarnings("unchecked")
    public <T> T createService(final Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method,
                                         Object[] args) throws Throwable {
                        //获取方法全部的注解
                        final Annotation[] annotations = method.getAnnotations();
                        for (int i = 0; i < annotations.length; i++) {
                            if (annotations[i] instanceof GET) { //若是注解是GET类型

                                final GET annotation = (GET) annotations[i];
                                final String url = annotation.value();
                                final Request request = new Request.Builder()
                                        .url(url)
                                        .get().build();
                                return mOkHttpClient.newCall(request);
                            }
                        }
                        return null;
                    }
                });
    }
}

复制代码

目前咱们主要的目标是为了验证这个方案的可行性,所以createService方法内部的逻辑很简单

1.获取方法上的全部注解

//获取方法全部的注解
                        final Annotation[] annotations = method.getAnnotations();
复制代码

2.判断若是存在@GET注解则获取注解内的值做为请求的地址

if (annotations[i] instanceof GET) { //若是注解是GET类型

    final GET annotation = (GET) annotations[i];
    final String url = annotation.value();
复制代码

3.根据url构造GET请求的Request对象,并做为参数调用OkHttpClient的newCall方法生成Call对象做为该方法调用的返回值

final Request request = new Request.Builder()
                                        .url(url)
                                        .get().build();
                                return mOkHttpClient.newCall(request);
复制代码

以上完成了一个对@GET注解申明的Http请求的动态代理封装,下面咱们在本身的项目中验证一下

3.2在项目中验证

1.建立一个接口类,并添加一个方法,方法的返回类型为Call,方法是添加了@GET注解

package com.knight.sample;

import okhttp3.Call;
import retrofit2.http.GET;

/**
 * Created by zhuoxiuwu
 * on 2019/4/25
 * email nimdanoob@gmail.com
 */
public interface NetRestService {

    @GET("http://gank.io/api/today")
    public Call todayGank();
}

复制代码

2.在项目中添加测试方法并调用

private void getToDayGankByRetrofit() {
        final Retrofit retrofit = new Retrofit(new OkHttpClient());
        retrofit.createService(NetRestService.class).todayGank().enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                JsonReader jsonReader = gson.newJsonReader(response.body().charStream());
                TodayGankResponse todayGankResponse = gson.getAdapter(TodayGankResponse.class).read(jsonReader);
                showHttpResult(todayGankResponse.toString());
                 Log.d("RetrofitTest","调用成功,结果为"+todayGankResponse.toString());
            }
        });
    }
复制代码

运行以后,方法调用成功并获得了响应结果

D/RetrofitTest: 调用成功,结果为BaseResponse(error=false, results={Android=[GankEntity(url=https://github.com/iqiyi/Neptune, desc=适用于Android的灵活,强大且轻量级的插件框架...
复制代码

经过简单的一个实现,咱们成功验证了使用注解加动态代理的方式实现一个声明式的网络请求框架是可行的,那么后续咱们须要继续完善这个项目,提供对更多请求方式 以及参数的支持


对于其余请求方式的支持,咱们能够添加更多的表示请求方式的注解,当用户设置了不一样的注解,在内部咱们使用OKHttp调用相应的方法。Http的请求方式大概以下

  • @DELETE
  • @GET
  • @HEAD
  • @PATCH
  • @POST
  • @PUT
  • @OPTIONS

本次提交见git

3.3继续实现POST注解

为了加深理解,咱们继续简单的实现一个POST请求,并支持传入一个参数对象,做为POST请求的JSON数据

首先咱们添加一个POST注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
    String value();
}

复制代码
package retrofit2;


import com.google.gson.Gson;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import retrofit2.http.GET;
import retrofit2.http.POST;

public class Retrofit {

    private OkHttpClient mOkHttpClient;

    public Retrofit(OkHttpClient mOkHttpClient) {
        this.mOkHttpClient = mOkHttpClient;
    }

    @SuppressWarnings("unchecked")
    public <T> T createService(final Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //获取方法全部的注解
                        final Annotation[] annotations = method.getAnnotations();

                        for (int i = 0; i < annotations.length; i++) {
                            if (annotations[i] instanceof GET) { //若是注解是GET类型
                                final GET annotation = (GET) annotations[i];
                                return parseGet(annotation.value(), method, args);
                            } else if (annotations[i] instanceof POST) {
                                final POST annotation = (POST) annotations[i];
                                return parsePost(annotation.value(), method, args);
                            }
                        }
                        return null;
                    }
                });
    }


    private Call parseGet(String url, Method method, Object args[]) {
        final Request request = new Request.Builder()
                .url(url)
                .get().build();
        return mOkHttpClient.newCall(request);
    }

    private Gson gson = new Gson();
    private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");

    private Call parsePost(String url, Method method, Object args[]) {
        final Type[] genericParameterTypes = method.getGenericParameterTypes();
        if (genericParameterTypes.length > 0) {
            final Class<?> clazz = Utils.getRawType(genericParameterTypes[0]);
            final String jsonBody = gson.toJson(args[0], clazz);
            final Request request = new Request.Builder()
                    .url(url)
                    .post(RequestBody.create(MEDIA_TYPE, jsonBody))
                    .build();
            return mOkHttpClient.newCall(request);
        }
        return null;
    }


}

复制代码

在 paresePost方法中咱们首先经过Method的getGenericParameterTypes方法获取全部参数的Type类型,而且经过Type类得到参数的原始Class类型,以后就可使用Gson转换成对应的Json对象了。

点击查看本次git提交的详细代码

3.4 实现ConverterFactory 解耦Json转换

在上面的例子中,咱们直接在框架Retrofit中使用了Gson库作Json转换,但做为一个框架来讲 咱们不但愿直接强耦合一个第三方Json转换库,这部分更但愿交由开发者根据具体状况自由选择;
所以咱们能够对这部分作下抽象封装,提取成一个负责Json转换的接口, 让应用层传入该接口的具体实现.

package retrofit2;

import java.lang.reflect.Type;

import javax.annotation.Nullable;

import okhttp3.RequestBody;

/**
 * Created by zhuoxiuwu
 * on 2019/4/25
 * email nimdanoob@gmail.com
 */
public interface Converter<F, T> {
    @Nullable
    T convert(F value);

    abstract class Factory {
        public @Nullable
        Converter<?, RequestBody> requestBodyConverter(Type type) {
            return null;
        }

    }
}

复制代码

应用层须要传入一个ConverterFactory,该工厂类负责根据传入的Type类型,返回一个可以将该Type类型的对象转换成RequestBody的Converter

咱们对Retrofit的构造函数以及paresePost方法作下修改,要求构造函数中传入一个ConverterFactory的实现,并在paresePost方法中使用这个ConverterFactory来作Java对象到ReqeustBody的转换

public class Retrofit {

    private OkHttpClient mOkHttpClient;

    private Converter.Factory mConverterFactory;

    public Retrofit(OkHttpClient mOkHttpClient, Converter.Factory mConverterFactory) {
        this.mOkHttpClient = mOkHttpClient;
        this.mConverterFactory = mConverterFactory;
    }
    //..省略部分代码
    
    private Call parsePost(String url, Method method, Object args[]) {
        final Type[] genericParameterTypes = method.getGenericParameterTypes();
        if (genericParameterTypes.length > 0) {
            //直接调用获得RequestBody
            final RequestBody requestBody = requestBodyConverter(genericParameterTypes[0]).convert(args[0]);
            final Request request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .build();
            return mOkHttpClient.newCall(request);
        }
        return null;
    }


    public <T> Converter<T, RequestBody> requestBodyConverter(Type type) {
        return (Converter<T, RequestBody>) mConverterFactory.requestBodyConverter(type);
    }

复制代码

在应用层,咱们实现并传入一个Gson的ConvertFactory的实现

package com.knight.sample;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.nio.charset.Charset;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import retrofit2.Converter;

/** * Created by zhuoxiuwu * on 2019/4/25 * email nimdanoob@gmail.com */
public class GsonConverterFactory extends Converter.Factory {
    public static GsonConverterFactory create() {
        return create(new Gson());
    }

    public static GsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new GsonConverterFactory(gson);
    }

    private final Gson gson;

    private GsonConverterFactory(Gson gson) {
        this.gson = gson;
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type) {
        //经过Type 转换成Gson的TypeAdapter
        //具体类型的json转换依赖于这个TypeAdapter
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonRequestBodyConverter<>(gson, adapter);

    }


    final static class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
        private static final Charset UTF_8 = Charset.forName("UTF-8");

        private final Gson gson;
        private final TypeAdapter<T> adapter;

        GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public RequestBody convert(T value) {
            Buffer buffer = new Buffer();
            Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter = null;
            try {
                jsonWriter = gson.newJsonWriter(writer);
                adapter.write(jsonWriter, value);
                jsonWriter.close();
                return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }

        }
    }

}

复制代码

点击查看本次git提交的详细代码

3.5 实现CallAdapter 支持方法返回类型

继续回到Http请求的声明中,目前咱们方法所支持的返回类型都是OKHttp的Call对象,而Call对象从使用上来讲,目前仍是有些繁琐,原生的Call对象返回的是ResponseBody还须要开发者本身处理并作转换。

public interface NetRestService {

    @GET("http://gank.io/api/today")
    public Call todayGank();
}
复制代码

也许咱们但愿这个方法能够这样定义

public interface NetRestService {

    @GET("http://gank.io/api/today")
    public TodayGankResponse todayGank();
}
复制代码

也许咱们能够在框架内部经过判断方法的返回类型是否是Call对象,若是不是,就在框架内部直接同步调用网络请求获得响应的Json内容后直接转换成JavaBean对象做为方法的返回值。
可是这个设想存在这样几个问题

  1. 要实现直接返回Http结果则方法调用是同步调用,若是在主线程作IO请求确定是不合理的

  2. 若是内部IO异常了,或者JSON转换失败了方法返回的是什么呢?为null吗?

所以更合理的话,在应用咱们但愿的是返回一个包装的支持异步调用的类型;

好比咱们的项目本身新增了一个支持异步调用的NetCall抽象接口

/**
 * Created by zhuoxiuwu
 * on 2019/4/26
 * email nimdanoob@gmail.com
 */
public interface NetCall<T> {
    public void execute(NetCallback<T> netCallback);
}

复制代码

咱们但愿咱们的方法能够这样申明

public interface NetRestService {

    @GET("http://gank.io/api/today")
    public NetCall<TodayGankResponse> todayGank();
}
复制代码

这样的话在应用层咱们调用的时候就能够像这样使用

retrofit.createService(NetRestService.class).todayGank()
                .execute(new NetCallback<TodayGankResponse>() {
                    @Override
                    public void onFailure(Exception e) {

                    }

                    @Override
                    public void onSuccess(TodayGankResponse data) {
                        Log.d("RetrofitTest","调用成功,结果为"+data.toString());
                        showHttpResult(data.toString());
                    }
                });
复制代码

那么具体要怎么实现呢,这个功能至关于让Retrofit框架支持 对方法返回类型的自定义适配;和Converter接口同样的思路,咱们在框架能够定义一个 CallAdapter接口,让应用层来具体实现并传入

package retrofit2;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import okhttp3.Call;

/** * Created by zhuoxiuwu * on 2019/4/26 * email nimdanoob@gmail.com */
public interface CallAdapter<T> {

    T adapt(Call call);


    abstract class Factory {

        public abstract CallAdapter<?> get(Type returnType,Retrofit retrofit);

        /** * 这是一个框架提供给开发者的util方法 * 用于获取类型的泛型上的类型 * 好比 Call<Response> 则 第0个泛型是Response.class */
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
            return Utils.getParameterUpperBound(index, type);
        }

        /** * 获取Type对应的Class * @param type * @return */
        protected static Class<?> getRawType(Type type) {
            return Utils.getRawType(type);
        }
    }
}

复制代码

在应用层咱们能够实现一个NetCallAdapter,支持Call对象到 NetCall对象的转换

package com.knight.sample;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;

/** * Created by zhuoxiuwu * on 2019/4/26 * email nimdanoob@gmail.com */
package com.knight.sample;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;

/** * Created by zhuoxiuwu * on 2019/4/26 * email nimdanoob@gmail.com */
public class NetCallAdapterFactory extends CallAdapter.Factory {

    /** * returnType参数 和 retroift参数 由底层框架传递给开发者 * @param returnType * @param retrofit * @return */
    @Override
    public CallAdapter<?> get(final Type returnType, final Retrofit retrofit) {
        //判断返回类型是不是 NetCall
        if (getRawType(returnType) != NetCall.class) {
            return null;
        }
        //要求开发者方法的返回类型必须写成 NetCall<T> 或者NetCall<? extends Foo> 的形式,泛型内的类型就是Json数据对应的Class
        if (!(returnType instanceof ParameterizedType)) {
            throw new IllegalStateException(
                    "NetCall return type must be parameterized as NetCall<Foo> or NetCall<? extends Foo>");
        }
        final Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);
        
        return new CallAdapter<NetCall>() {

            @Override
            public NetCall adapt(final Call call) {

                return new NetCall() {
                    @Override
                    public void execute(final NetCallback netCallback) {
                        call.enqueue(new Callback() {
                            @Override
                            public void onFailure(Call call, IOException e) {
                                netCallback.onFailure(e);
                            }

                            @Override
                            public void onResponse(Call call, Response response) throws IOException {
                                //由retrofit 提供 ResponseBody 到 某个Type Class的转换
                                final Object value = retrofit.responseBodyTConverter(innerType).convert(response.body());
                                netCallback.onSuccess(value);
                            }
                        });
                    }
                };
            }
        };
    }
}
复制代码

Retrofit类中,咱们添加了判断方法返回类型的逻辑,若是发现方法的返回类型不是Call类型,则使用CallAdapter作转换

package retrofit2;


import com.google.gson.Gson;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.POST;

public class Retrofit {

    private OkHttpClient mOkHttpClient;

    private Converter.Factory mConverterFactory;

    private CallAdapter.Factory mCallAdapterFactory;

    public Retrofit(OkHttpClient mOkHttpClient, Converter.Factory mConverterFactory, CallAdapter.Factory callAdapterFactory) {
        this.mOkHttpClient = mOkHttpClient;
        this.mConverterFactory = mConverterFactory;
        this.mCallAdapterFactory = callAdapterFactory;
    }

    @SuppressWarnings("unchecked")
    public <T> T createService(final Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //获取方法全部的注解
                        final Annotation[] annotations = method.getAnnotations();
                        final Type returnType = method.getGenericReturnType();
                        for (int i = 0; i < annotations.length; i++) {
                            if (annotations[i] instanceof GET) { //若是注解是GET类型
                                final GET annotation = (GET) annotations[i];
                                return parseGet(annotation.value(), method, args);
                            } else if (annotations[i] instanceof POST) {
                                final POST annotation = (POST) annotations[i];
                                return parsePost(annotation.value(), method, args);
                            }
                        }
                        return null;
                    }
                });
    }


    private Object parseGet(String url, Method method, Object args[]) {
        final Request request = new Request.Builder()
                .url(url)
                .get().build();

        final Call call = mOkHttpClient.newCall(request);


        return adaptCall(method, call);
    }


    private Object parsePost(String url, Method method, Object args[]) {
        final Type[] genericParameterTypes = method.getGenericParameterTypes();
        if (genericParameterTypes.length > 0) {
            final RequestBody requestBody = requestBodyConverter(genericParameterTypes[0]).convert(args[0]);
            final Request request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .build();
            final Call call = mOkHttpClient.newCall(request);

            return adaptCall(method, call);

        }
        return null;
    }

    /** * 负责 任意Java类型到 RequestBody的转换 * @param type * @param <T> * @return */
    public <T> Converter<T, RequestBody> requestBodyConverter(Type type) {
        return (Converter<T, RequestBody>) mConverterFactory.requestBodyConverter(type);
    }

    /** * 负责ResponseBody到Type类型的转换 * @param type * @param <T> * @return */
    public <T> Converter<ResponseBody,T> responseBodyTConverter(Type type){
        return (Converter<ResponseBody, T>) mConverterFactory.responseBodyConverter(type);
    }


    /** * 获取方法的返回类型,并使用CallAdapter作类型转换 * @param method * @param call * @return */
    private Object adaptCall(Method method, Call call) {
        final Type returnType = method.getGenericReturnType();
        if (Utils.getRawType(returnType) != Call.class) {
            final CallAdapter<?> callAdapter = mCallAdapterFactory.get(returnType,this);
            return callAdapter.adapt(call);
        } else {
            return call;
        }
    }

}
复制代码

框架的后续实现及优化

到目前为止咱们已经实现了一个简单的retrofit框架,也许代码不够精简,边界处理没有十分严谨,但已经初具雏形。咱们能够继续思考现有项目的不足 添加更多的支持。

好比在网络请求方面目前只支持GET、POST,那么咱们后续须要添加更多请求方式的支持

在Retrofit对象的构造上,目前咱们的构造函数传入了3个对象,若是后续有更多的参数须要配置化,那么咱们可使用 Builder设计模式来构建Retrofit

在CallAdapter的设计上,咱们目前只支持传入一个CallAdapterFactory,所以方法的返回类型除了原生的Call对象外 只支持应用开发者新增一个。实际上,这不太合理,所以这部分咱们能够支持开发者传入一个列表,在内部咱们迭代这个List ,遍历过程当中调用CallAdapter<?> get(Type returnType,Retrofit retrofit);,若是这个Factory返回了null,则说明它对该类型不支持,则继续调用下个CallFactory 直到找到合适的

abstract class Factory {
        //若是返回了null 则该Factory不支持该returnType的转换
        public abstract CallAdapter<?> get(Type returnType,Retrofit retrofit);
    }
复制代码

在框架的性能优化上,目前咱们每次调用 createService(final Class service) 都是返回一个新的代理类,其实咱们能够创建一个 Service类类型到该类型代理对象的Map缓存,若是发现缓存池有则直接复用该对象。更进一步的思考,咱们是否能够以Method方法为纬度,每次调用一个抽象方法时咱们解析该方法上的注解生成一个本身的ServiceMethod类对象并加到缓存池中,下次调用一样的方法时,咱们就不须要解析注解了,而是直接使用内部的ServiceMethod

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
            Retrofit retrofit, Method method){

        //...解析method并生成一个HttpServiceMethod对象
        return null;
    }

    //框架内部表示一个Http方法调用的类
    class HttpServiceMethod<ResponseT, ReturnT>{
        //arguments
        //解析方法及注解并把信息保存下来,一样的方法就不须要二次解析了


        //args为方法的参数,每次方法调用时只须要处理不一样的参数
        //具体是POST 方法仍是GET方法, callAdapter具体使用哪个 都已经肯定下来了
        final  ReturnT invoke(Object[] args) {
            return null;
        }
    }

复制代码

以上提出的一些优化点,你们能够本身先思考实现并从新阅读写Retrofit源码来加深本身的理解。从整个思考流程及实现上来看Retrofit的实现并不复杂,可是从实现一个简单可用的网络封装库到实现一个拓展性强、职责分离的框架,中间的过程仍是有不少细节的,若是你看完了这篇文章能够再抽1个小时左右的时间从新看下Retorift框架的源码相信从中还会有更多的收获.

欢迎关注个人微信公众号获取更多技术文章

image
相关文章
相关标签/搜索