@Awille
2019-01-19T11:47:09.000000Z
字数 12548
阅读 137
Android 网络请求 Retrofit 设计模式
适配器模式用于连接不匹配的二者之间,将不匹配变得匹配,类似于我们生活中使用的转换器、转接头。
这种适配器模式是通过继承来实现的。
假设你目前在做一个项目,其中你设计了一个接口:
public interface Demo {int methodA();}
这个接口的方法会返回你想要的数值。我们随便写一个实现
public class DemoImpl implements Demo {@Overridepublic int myMethod() {return 5;}}
现在来了一个普通的客户,它想调用你这个接口的方法得到那个值
public class NormalClient {public int satisfyMyNormalRequest(Demo demo) {return demo.myMethod();}}
十分简单,在他的方法当中传入我们的接口实例就可以了。
但是客户越来越多,出现了不正常的用户,这个用户说,他需要我们提供那个值,但是他有自己的规范的,比如他的方法是这样的
public class AbNormalClient {public int satisfyMyAbNormalRequest(MyDemo demo) {return demo.methodForMySelf();}}
这是他的接口规范:
public interface MyDemo {int methodForMySelf();}
现在要满足他的需求已经不能传入我们自己的接口实例了,咋办呢?此时类适配器模式就派上用场了。
我们专门为这个客户写一个适配器,该适配器继承我们的接口实现类,并且实现客户的接口规范
public class AdapterForClient extends DemoImpl implements MyDemo{@Overridepublic int methodForMySelf() {return myMethod();}}
这个适配器继承了我们自己的接口实现类,那么这个适配器就可以调用我们实现类中的方法了,而在实现客户自己的接口时,我们就可以调用自己的实现类中的方法返回相应的值。
这个适配器也是MyDemo接口的一个实例,客户可以直接在他们的方法当中建一个改适配器的实例就可以了。
对象适配器模式跟类适配器模式是同一个道理,只是我们不通过继承来实现
public class Adapter2 implements MyDemo {Demo demo;public Adapter2(Demo demo) {this.demo = demo;}@Overridepublic int methodForMySelf() {return demo.myMethod();}}
看到这个实现类就应该知道是怎么回事,对象适配器模式呢,就是把我们自己的接口实现类的实例作为适配器的成员变量,这样适配器中直接可以通过该实例调用相应的方法
接口适配器特别简单,假设目前有一个接口
public interface Test {void test1();void test2();void test3();void test4();void test5();}
这个接口当中有很多的方法,当我想创建一个接口实例的时候,我必须得有个实现类实现所有的方法,但有的时候我并不想实现所有的方法,那么此时,我可以先写个接口适配器
public class InterfaceAdapter implements Test {@Overridepublic void test1() {}@Overridepublic void test2() {}@Overridepublic void test3() {}@Overridepublic void test4() {}@Overridepublic void test5() {}}
这个类啥也不做,调用的时候:
public class Client {public void satisfyRequest(Test test) {test.test1();}public static void main(String[] args) {Client client = new Client();client.satisfyRequest(new InterfaceAdapter(){@Overridepublic void test1() {System.out.println("HELLO WORLD");}});}}
在静态代理当中,代理类是在程序运行前就已经确定下来并且编译完成的,而动态代理是程序运行时根据我们的指示动态生成的。在代理模式当中,代理类与委托类有同样的接口,代理类一般是用来将消息传递给委托类之前做一些消息预处理,以及在委托类处理完消息后,代理类做一些事后的处理。动态代理的实现原理是反射。
java的动态代理实现,需要注意一个接口InvocationHandler 跟 一个类 Proxy。
每一个动态代理类都要实现InvocationHandler接口,当动态代理类调用委托类的方法时,都会通过这个接口的invoke方法来实现。
public interface InvocationHandler {// proxy: 我们代理的真实对象,即我们的委托类// method: 委托类中的某一个方法// args: 委托类中对应方法需要传入的参数public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;}
Proxy类当中有一个newProxyInstance用于创建我们的动态代理对象:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException//loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载//interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了//h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
定义一个接口,该接口可能会有多种实现:
public interface Subject {public void rent();public void hello(String str);}
该接口的实现,同时也是我们实现的代理类之一:
public class RealSubject implements Subject {@Overridepublic void rent() {System.out.println("I want to rent my house");}@Overridepublic void hello(String str) {System.out.println("hello " + str);}}
动态代理类的创建:
public class DynamicProxy implements InvocationHandler {//我们代理的真实对象,即我们的委托类,// 我们暂时不确定他是什么类对象,所暂时用Object来声明private Object subject;public DynamicProxy(Object subject) {this.subject = subject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("消息预处理");method.invoke(subject, args);System.out.println("事后消息处理");return null;}}
测试:
public class ClientTest {public static void main(String[] args) {//代理的真实对象Subject realSubject = new RealSubject();InvocationHandler handler = new DynamicProxy(realSubject);Subject myProxy = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),handler);myProxy.rent();myProxy.hello("world");}}
输出:
消息预处理
I want to rent my house
事后消息处理
消息预处理
hello world
事后消息处理
这个动态代理达到效果,可以考虑那么一种情景,这上面例子中的subject接口当中,存在很多的方法。我们想要计算每个方法调用消耗的时间,如果采用一般的方法,首先持有接口对象,然后通过该接口对象调用相关的方法,那么有n个方法,我们就要写n次计算时间的逻辑。而动态代理当中,所有的委托类的方法调用都是通过委托类中的InvocationHandler的invoke方法实现的,那么我们只需在invoke方法中把计算时间的逻辑填充上去,就能完成对所有方法的时间计算了。
看了下策略模式,感觉跟依赖注入谜之相似,这里就不写了
我们平时创建retrofit对象就是通过retrofit的builder对象来创建的。
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com/").addConverterFactory(GsonConverterFactory.create()).build();
当创建我们的API实例的时候,我们使用了这行代码:
GithubService service = retrofit.create(GithubService.class);
查看其实现方式:
看到这里是使用了动态代理的
public <T> T create(final Class<T> service) {Utils.validateServiceInterface(service);if (validateEagerly) {eagerlyValidateMethods(service);}return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() {//先暂时省略该接口的实现});}
看到在Proxy.newProxyInstance传入的三个对象:
classloader: 传入我们传入的API接口的classloader对象
interfaces: 传入我们API中定义的接口
InvocationHandler: 该接口的实现类
我们看下invocationHandler的实现:
new InvocationHandler() {private final Platform platform = Platform.get();@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {// 如果调用的方法是Object中的方法,如equals, toString等直接用平常我们使用的返回if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}//如果是 default 方法(Java 8 引入),就调用 default 方法if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}//我们实际上的API接口定义的方法都是通过下面的三行代码实现的ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.adapt(okHttpCall);}}
通过以上分析,我们知道了我们如果调用API实例中的方法(一般是返回某种call对象),最终都会通过这三行代码实现:
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.adapt(okHttpCall);
首先创建了一个 ServiceMethod对象,ServiceMethod的作用是这样描述的:
Adapts an invocation of an interface method into an HTTP call.
很清晰,作用就是把我们API接口方法的调用转化成一个HTTP call对象
ServiceMethod#loadServiceMethod:ServiceMethod<?, ?> loadServiceMethod(Method method) {//缓存逻辑,之前获取过的会被缓存下来,下次调用相同的方法会返回缓存中记录的ServiceMethod对象ServiceMethod<?, ?> result = serviceMethodCache.get(method);if (result != null) return result;//锁 缓存在同一时间只允许被一个线程访问synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {//创建servicemethod对象result = new ServiceMethod.Builder<>(this, method).build();//缓存serviceMethodCache.put(method, result);}}return result;}
ServiceMethod的构造方法:
ServiceMethod(Builder<R, T> builder) {//负责创建 HTTP 请求this.callFactory = builder.retrofit.callFactory();//把retrofit2.Call<T> 对象转换成为T;//整个流程当中,首先会发送Http请求,Http请求返回后,//将数据对象转换成T类型实例,通过Converter<R, T>实现this.callAdapter = builder.callAdapter;this.baseUrl = builder.retrofit.baseUrl();//上面提到的将服务端返回数据转换成T对象的转换器,// 石基类型是Converter<Response, T>this.responseConverter = builder.responseConverter;this.httpMethod = builder.httpMethod;this.relativeUrl = builder.relativeUrl;this.headers = builder.headers;this.contentType = builder.contentType;this.hasBody = builder.hasBody;this.isFormEncoded = builder.isFormEncoded;this.isMultipart = builder.isMultipart;//负责将API调用时各种请求参数转换成Http request中传入的参数this.parameterHandlers = builder.parameterHandlers;}
ServiceMethod中有一个parameterHandlers对象,这里就涉及到为什么retrofit要使用动态代理,parameterHandlers是负责网络调用的request创建时各种参数传入的。我们知道,在retrofit当中,参数的标注很多是通过注解来标注的,动态代理的使用对retrofit最大的作用就是,由于所有的方法调用都会转移到到InvocationHandler的invoke中来调用,那么这里就可以统一的来做对注解的处理了。以我目前的理解来看,动态代理模式的使用好处对retrofit来说就是方便统一对注解进行处理。
我们主要关注以下两个:
callFactory: 负责创建Http请求
callAdapter: 负责发送Http请求,并将从服务端返回的数据通过responseConverter转化成为我们定义的T类型实例对象
刚刚说到的三行代码当中,构建完ServiceMethod对象以后:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
okhttpcall对象实现了call接口:
final class OkHttpCall<T> implements Call<T>
call接口的方法跟okhttp一样的
//同步Response<T> execute() throws IOException;//异步void enqueue(Callback<T> callback);boolean isExecuted();void cancel();//返回request对象Request request();
我们先只关注execute方法的实现,execute跟enqueue方法的实现主要原理上应该跟okhttp相同,而且,enqueue方法实质上也就是新开了线程执行execute方法,在执行结束后通过回调返回相应的结果。毕竟retrofit只是在okhttp上做了一层封装,我们直接看一个简单的实现,看看retrofit的差异在哪里就可以了。
@Override public Response<T> execute() throws IOException {okhttp3.Call call;call = rawCall;if (call == null) {try {call = rawCall = createRawCall();} catch (IOException | RuntimeException | Error e) {throwIfFatal(e); // Do not assign a fatal error to creationFailure.creationFailure = e;throw e;}}}return parseResponse(call.execute());}
createRawCall方法:
private okhttp3.Call createRawCall() throws IOException {okhttp3.Call call = serviceMethod.toCall(args);if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;}
可以看到这里的call对象其实调用的就是okhttp的call方法。
整个execute方法差异主要就是在返回的那部分:
parseResponse(call.execute()):
在okhttp中call.execute()返回的是服务端返回的reponse对象,这里函数应该是将该reponse对象通过我们上面提到的servicemethod的converter来实现转换:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body();//省略状态码异常的处理,只关注状态码为200的返回ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);try {//这里是做服务端数据到我们定义的返回类型的转换的:T body = serviceMethod.toResponse(catchingBody);return Response.success(body, rawResponse);} catch (RuntimeException e) {// If the underlying source threw an exception, propagate that rather than indicating it was// a runtime exception.catchingBody.throwIfCaught();throw e;}}
做数据转换的时候用到了servicemethod的toResponse方法:
R toResponse(ResponseBody body) throws IOException {return responseConverter.convert(body);}
这里果然用到的就是我们之前在servicemethod方法当中提到的converter。
在我们一直提到的那三行代码当中,最后一行是:
return serviceMethod.adapt(okHttpCall);
adapt方法的实现为:
T adapt(Call<R> call) {return callAdapter.adapt(call);}
retrofit是支持返回rxjava用到的Obeservable对象的。这里转换用到的就是我们之前早serviceMethod中的callAdapter来实现的,callAdapter专门用来做不同call对象的转换。
在使用之前,我们创建retrofit的时候,要把rxjava的RxJavaCallAdapterFactory添加到retrofit的CallAdapterFactory中,这个添加会被加到retrofit的CallAdapaterfactory列表中:
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
最终这个列表中的callAdapterFactory会根据我们在API实例中定义的返回类型,决定在创建serviceMethod会用哪个具体的CallAdapter
private CallAdapter<T, R> createCallAdapter() {Type returnType = method.getGenericReturnType();//省略部分代码Annotation[] annotations = method.getAnnotations();try {//这里根据类型来决定使用哪个adapterreturn (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);} catch (RuntimeException e) { // Wide exception range because factories are user code.throw methodError(e, "Unable to create call adapter for %s", returnType);}}
假如我们没有传入CallAdapterFactory,那么使用的是DefaultCallAdapterFactory,该adapter直接返回传入的参数啥也不做。ExecutorCallAdapterFactory也是有一个内置的adapter,生产的 adapter 会在异步调用时在指定的 Executor 上执行回调
我们关注下rxjava的adapter,只关注其对Observable对象返回的支持。
以下方式是RxjavaCallAdapter的内部类SimpleAdapter的实现:
@Override public <R> Observable<R> adapt(Call<R> call) {Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //.lift(OperatorMapResponseToBodyOrError.<R>instance());if (scheduler != null) {return observable.subscribeOn(scheduler);}return observable;}
发现往后深入需要对Rxjava的源码也具体的了解才行,所以不继续看实现流程了。探讨一下这里适配器模式的实现。
适配器模式有三种,类适配器模式、对象适配模式、接口适配器模式。粗略的看,这里应该用的不是接口适配器模式,毕竟接口适配器模式不是用于这种场景的。
首先在调用的时候:
T adapt(Call<R> call) {return callAdapter.adapt(call);}
这里的callAdapter的定义是一个接口:
private final CallAdapter<R, T> callAdapter;
该接口的定义为:
//R原始类型,T适配后的类型public interface CallAdapter<R, T> {//泛型包裹的真实对象类型,比如Call<A>则就是A的Type对象Type responseType();//返回一个T类型来代理Call,//这里返回类型是根据我们的接口定义的返回来决定的,可以使okhttpcall,可以是observableT adapt(Call<R> call);abstract class Factory {//根据接口返回特定的CallAdapterpublic abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,Retrofit retrofit);protected static Type getParameterUpperBound(int index, ParameterizedType type) {return Utils.getParameterUpperBound(index, type);}protected static Class<?> getRawType(Type type) {return Utils.getRawType(type);}}}
其内部有个Factory接口,其中的方法决定使用哪个CallAdapter。
CallAdapter的adapt方法决定返回的call对象
假如你想返回一个自定义类型的Call对象,叫做MyCall:
public class MyCallAdapter <T> implements CallAdapter<T, HttpCall<T>> {private final Type responseType;private final Executor callbackExecutor;HttpCallAdapter(Type responseType, Executor callbackExecutor) {this.responseType = responseType;this.callbackExecutor = callbackExecutor;}@Overridepublic Type responseType() {return responseType;}@Overridepublic MyCall<T> adapt(Call<T> call) {return new MyCall<>(call, callbackExecutor);}}
在retrofit中接口中方法定义时,每一个返回对象的都要有一个adapter与之相对应,比如okhhtpcall对象对应defaultCallAdapter, observable对应rxjavaadapterFactory中的simpleAdapter。
小结:
到上面为止,好像都跟类适配模式或者对象适配器模式不对应。感觉这种根据接口方法定义的返回类型选择不同的callAdapter,然后调用它们的adapt得到返回对象,这更像是策略模式。
我想这里的适配器模式,真正的应用应该是在:
(下面说的okhttpcall对象就是retrofit.call对象)
retrofit适配是从okhttpcall对象到我们自定义或者其他call对象的转换。要从okhttpcall对象转换为我们自定义的call对象,那么首先适配器类要持有okhttpcall对象,然后实现我们自定义call对象的接口,在实现自定义call对象接口中使用持有的okhttpcall实例对象进行相关的操作。