Retrofit-解耦
前言
仅仅查看Retrofit源码,而不去思考其意图,不了解其本质技术原理,是很难提升我们编写优质代码以及我们的架构能力的,这一篇希望能寻找到Retrofit中优雅的地方,解析其原理。
依赖注入(DI),控制反转(IoC)
对于解耦是有很多种方式的,很多博客只介绍了Retrofit中使用的设计模式进行解耦,其实Retrofit也用了很多的依赖注入、控制反转。如果不理解依赖注入、控制反转请先看看这篇文章控制反转和依赖注入的理解(通俗易懂) 。
这篇文章说到:IoC是一种面向对象思想,能指导我们如何设计出松耦合、更优良的程序。在传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
DI(依赖注入)其实就是IOC的另外一种说法,其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”
也就是说,程序A中需要依赖对象B,不再是自己手动的New出B来,是由Ioc容器为我们提供,对象B的创建与销毁不由A去控制
。这样减少对象之间的耦合。
依赖注入只是一个思想如果还不明白再看看Spring的例子
接下里我们再看看Retrofit中使用到的依赖注入,在Retrofit中对象的创建并非在哪里使用就在哪里创建,很多对象的创建都依赖于上一个对象,比如callAdapter
和responseConverter
的创建;以及Request
的创建。在callAdapter
和responseConverter
对象都是从Retrofit类中for循环得到,其两个接口内部实现也非常相似,都是经过Factory去创建,但具体的创建过程并不由依赖对象HttpServiceMethod创建。在HttpServiceMethod中,最后得到的两个对象都是create创建之后在进行构造注入
final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
...
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
...
}
Request使用是在OkHttpCall中,而其创建过程缺是在RequestFactory的create()方法中, 而RequestFactory对象的引用也是使用构造注入的方式传递到OkHttpCall的
final class OkHttpCall<T> implements Call<T> {
...
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
...
}
final class RequestFactory {
...
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args.length;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
headers, contentType, hasBody, isFormEncoded, isMultipart);
List<Object> argumentList = new ArrayList<>(argumentCount);
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.get()
.tag(Invocation.class, new Invocation(method, argumentList))
.build();
}
...
}
在这里可以看到,HttpServiceMethod依赖的callAdapter
和responseConverter
都不是自己直接创建,是由Ioc
容器Retrofit
根据传递过去的参数匹配到后返回,再在HttpServiceMethod
的构造方法中进行构造注入;OkHttpCall
中依赖的Request
也不是直接创建,是由Ioc
容器RequestFactory
创建,RequestFactory
的引用也是经过构造方法引入。
面向接口编程
对于概念不熟悉的同学可以先看看下面的博客。我一直在想,思想的高度才能决定行为的高度,不理解的还是请耐心看完。下面的论述很多从博客中摘录可能枯燥,如有不正确处请多指正。
面向接口是面向对象的抽象,面向对象定义对象的行为。当一组对象具有相同的行为后;对于这一组对象的定义和后续的扩展我们将怎么展开
?是使用基类继承
,还是使用接口
去规范这样的行为?下面有博客说到,使用继承或接口实现需要看其动机,如果是想对代码的复用,可以使用继承;如果是想使用对象向上转型,建议使用接口
。
理解面向对象更加容易让我们理解面向接口,首先面向接口并不浅显的指Java中的interface
,如果使用抽象方法,定义行为规范同样可以解决统一的接口调用问题。那真正的面向对象封装、继承、多态,到底他要表达什么?
封装
:在于明确标识出允许外部使用的所有成员函数和数据项,或者叫接口。有了封装,就可以明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者;而外部调用者也可以知道自己不可以碰哪里。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
继承
:继承同时具有两种含义:其一是继承基类的方法,并做出自己的改变和/或扩展——号称解决了代码重用问题;其二是声明某个子类兼容于某基类(或者说,接口上完全兼容于基类),外部调用者可无需关注其差别(内部机制会自动把请求派发[dispatch]到合适的逻辑)
多态
:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同,也就是向上转型。
对于继承的第一点,其实有时候并没有什么好处,反而会增加子类和基类的耦合;第二点其实为了做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象,在博客中这叫做归一化,归一化的好处大家可以自行看博客。
但很多人可能对于面向对象的继承第2点理解的并不透彻
,大量的使用继承第1点对代码进行复用,增加class对象
,并不对代码进行更高层次的抽象。但基于现实也提到我们的开发大多处于在已有修改旧代码上进行开发,为了复用之前,而不得已创建出更多的class。
对于面向接口编程其实就是制定一个抽象的兼容性接口,实现方式不仅局限于如jiav语言的 interface,但最终的目标就是为实现归一化
接下来我们看看Retrofit中的面向接口编程,在Retrofit中的接口类有
Call
:用于发起请求,在继承CallAdapter.Factory的类中会有;内部类实现
Callback
:默认返回数据回调
CallAdapter
:支持RxJava,java8等做出的适配接口
Converter
:数据解析适配
这里主要讲解CallAdapter,再次回到Retrofit-源码分析中的那张图,前面过了一遍默认的Retrofit请求流程,但适配RxJava的那一部分我们并没有讲解。如果对Retrofit已经和熟悉了,那你一定能猜测出创建Retrofit传递进去的RxjavaCallAdapter大致是这么实现的,接下来先看看两段发起请求的代码。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())//设置 Json 转换器
.build();
RequestService requestService = retrofit.create(RequestService .class);
Call<Data> call = requestService.getMessage("");
call.enqueue(new Callback<Data>() {
@Override
public void onResponse(Call<Data> call, Response<Data> response) {
Data data = response.body();
}
@Override
public void onFailure(Call<Data> call, Throwable t) {
Log.e("TAG", "Throwable : " + t);
}
});
public interface RxRequestService {
@GET("location")
Observable<Data> getMessage(@Query("city") String city);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())//设置 Json 转换器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//RxJava 适配器
.build();
RxRequestService rxjavaService = retrofit.create(RxRequestService.class);
rxjavaService .getMessage("")
.subscribeOn(Schedulers.io())//IO线程加载数据
.observeOn(AndroidSchedulers.mainThread())//主线程显示数据
.subscribe(new Subscriber<Data>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Data data) {
}
});
在 retrofit.create之前其实并没有什么不太一样,起到关键作用的其实是添加的 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
RxJava 适配器,OK,我们看一下com.squareup.retrofit2:adapter-rxjava
包下的RxJavaCallAdapterFactory
类,同时请回忆默认请求的ExecutorCallAdapterFactory
类,相同的都是继承了CallAdapter.Factory,都是要实现Factory中get()方法,返回一个CallAdapter<?>,中间不重要,重要的是CallAdapter中的adapt(Call call)方法,在adapt中得到真正发起请求的call,得到Call后就调用execute(),返回对象了。
在RxJavaCallAdapterFactory类中,get()方法里首先做了一个策略模式确定需要哪一个CallAdapter,如果使用的是 CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
那么再判断是使用 ResultCallAdapter
还是 SimpleCallAdapter
但两个CallAdapter都实现了CallAdapter,最后在RequestArbiter
类中就是调用Response<T> response = call.execute();
得到数据,而在OkHttpCall中已经将其数据解析完成。
public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {
/**
* Returns an instance which creates synchronous observables that do not operate on any scheduler
* by default.
*/
public static RxJavaCallAdapterFactory create() {
return new RxJavaCallAdapterFactory(null);
}
/**
* Returns an instance which creates synchronous observables that
* {@linkplain Observable#subscribeOn(Scheduler) subscribe on} {@code scheduler} by default.
*/
public static RxJavaCallAdapterFactory createWithScheduler(Scheduler scheduler) {
if (scheduler == null) throw new NullPointerException("scheduler == null");
return new RxJavaCallAdapterFactory(scheduler);
}
private final Scheduler scheduler;
private RxJavaCallAdapterFactory(Scheduler scheduler) {
this.scheduler = scheduler;
}
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
String canonicalName = rawType.getCanonicalName();
boolean isSingle = "rx.Single".equals(canonicalName);
boolean isCompletable = "rx.Completable".equals(canonicalName);
if (rawType != Observable.class && !isSingle && !isCompletable) {
return null;
}
if (!isCompletable && !(returnType instanceof ParameterizedType)) {
String name = isSingle ? "Single" : "Observable";
throw new IllegalStateException(name + " return type must be parameterized"
+ " as " + name + "<Foo> or " + name + "<? extends Foo>");
}
if (isCompletable) {
// Add Completable-converter wrapper from a separate class. This defers classloading such that
// regular Observable operation can be leveraged without relying on this unstable RxJava API.
// Note that this has to be done separately since Completable doesn't have a parametrized
// type.
return CompletableHelper.createCallAdapter(scheduler);
}
CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
if (isSingle) {
// Add Single-converter wrapper from a separate class. This defers classloading such that
// regular Observable operation can be leveraged without relying on this unstable RxJava API.
return SingleHelper.makeSingle(callAdapter);
}
return callAdapter;
}
private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
Class<?> rawObservableType = getRawType(observableType);
if (rawObservableType == Response.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException("Response must be parameterized"
+ " as Response<Foo> or Response<? extends Foo>");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
return new ResponseCallAdapter(responseType, scheduler);
}
if (rawObservableType == Result.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException("Result must be parameterized"
+ " as Result<Foo> or Result<? extends Foo>");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
return new ResultCallAdapter(responseType, scheduler);
}
return new SimpleCallAdapter(observableType, scheduler);
}
static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
private final Call<T> originalCall;
CallOnSubscribe(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override public void call(final Subscriber<? super Response<T>> subscriber) {
// Since Call is a one-shot type, clone it for each new subscriber.
Call<T> call = originalCall.clone();
// Wrap the call in a helper which handles both unsubscription and backpressure.
RequestArbiter<T> requestArbiter = new RequestArbiter<>(call, subscriber);
subscriber.add(requestArbiter);
subscriber.setProducer(requestArbiter);
}
}
static final class RequestArbiter<T> extends AtomicBoolean implements Subscription, Producer {
private final Call<T> call;
private final Subscriber<? super Response<T>> subscriber;
RequestArbiter(Call<T> call, Subscriber<? super Response<T>> subscriber) {
this.call = call;
this.subscriber = subscriber;
}
@Override public void request(long n) {
if (n < 0) throw new IllegalArgumentException("n < 0: " + n);
if (n == 0) return; // Nothing to do when requesting 0.
if (!compareAndSet(false, true)) return; // Request was already triggered.
try {
Response<T> response = call.execute();
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(response);
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
return;
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
@Override public void unsubscribe() {
call.cancel();
}
@Override public boolean isUnsubscribed() {
return call.isCanceled();
}
}
static final class ResponseCallAdapter implements CallAdapter<Observable<?>> {
private final Type responseType;
private final Scheduler scheduler;
ResponseCallAdapter(Type responseType, Scheduler scheduler) {
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override public Type responseType() {
return responseType;
}
@Override public <R> Observable<Response<R>> adapt(Call<R> call) {
Observable<Response<R>> observable = Observable.create(new CallOnSubscribe<>(call));
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
private final Type responseType;
private final Scheduler scheduler;
SimpleCallAdapter(Type responseType, Scheduler scheduler) {
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override public Type responseType() {
return responseType;
}
@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;
}
}
static final class ResultCallAdapter implements CallAdapter<Observable<?>> {
private final Type responseType;
private final Scheduler scheduler;
ResultCallAdapter(Type responseType, Scheduler scheduler) {
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override public Type responseType() {
return responseType;
}
@Override public <R> Observable<Result<R>> adapt(Call<R> call) {
Observable<Result<R>> observable = Observable.create(new CallOnSubscribe<>(call)) //
.map(new Func1<Response<R>, Result<R>>() {
@Override public Result<R> call(Response<R> response) {
return Result.response(response);
}
}).onErrorReturn(new Func1<Throwable, Result<R>>() {
@Override public Result<R> call(Throwable throwable) {
return Result.error(throwable);
}
});
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
}
学习完上面的CallAdapter上面的最大的感受是什么?是适配器模式的优秀帮我们扩展了对RxJava等的适配
吗?还是CallAdapter.Factory中的get()
方法,以及CallAdapter中提供的 T adapt(Call<R> call)方法的巧妙
,为我们完成最后的发起请求?这些都是统一的接口实现为我们带来的松耦合可扩展,在上层接口统一的情况下,中间的处理由不同的业务去实现,最后执行请求的还是OkHttpCall,既保证了业务的统一,又对不同框架做了扩展。在Retrofit中很多类都是公用的只是将Converter和CallAdapter给与了充分扩展,永远都是要获取一个ServiceMethod.调用其invoke()方法,总是ServiceMethod的实现类 HttpServiceMethod中传递一个真正发起请求的OkHttpCall,得到call的总是实现CallAdapter. Factory的类,然后再具体去实现业务
。
设计模式
对于Retrofit的设计模式解耦,我在很多博客中都看到过讲的很好的,但设计模式不要死记住其使用方法,写法有很多种,应该明白其精髓;其中适配器模式,动态代理模式Retrofit在里面都有用到,还有其他的一些设计模式;这里我分享我看过的几篇优质博客,我就不做讲述了;后期可能我也会写设计模式博客,到时候再结合Retrofit讲解Retrofit分析-经典设计模式案例 秒懂Java代理与动态代理模式
Android 玩转IOC,Retfotit源码解析,教你徒手实现自定义的Retrofit框架
控制反转和依赖注入的理解
什么叫面向接口编程以及面向接口编程的好处
面向接口编程详解-Java篇
面向接口编程详解(一)——思想基础(转)
面向对象编程的弊端是什么?