Retrofit的本质是RESTful的HTTP网络请求框架的封装,网络请求的工作本质是OkHttp完成,而Retrofit只是负责网络请求接口的封装。但是现在这么多人用,也看得出这个框架封装的十分受程序员喜爱。
现在先看目录:
一、Retrofit的使用
二、源码解析
一、Retrofit的使用
我们先来看步骤:
1)创建描述网络请求的接口
2)创建Retrofit实例
3)创建网络请求接口实例并配置网络请求参数
4)发送网络请求
5)处理服务器返回的数据
先看网络请求的接口
public interface retrofit_interface {
@GET("部分URL")
Call<String> getCall();
}
这就是第一步,这个接口类,@GET注解方法和Call都是属于 Retrofit的类。注解方法也是Retrofit方便使用的体现。
@GET代表使用GET请求。Call是个泛型,返回的是string。
我们看下后面的4步:
public class RetrofitDemo {
//创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.baidu.com/")//设置网络请求基础url地址
.addConverterFactory(GsonConverterFactory.create())//设置数据解析器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
.build();
//创建 网络请求接口的实例
retrofit_interface request = retrofit.create(retrofit_interface.class);
//对 发送请求 进行封装
Call<String> call = request.getCall();
public void callRequest(){
//发送网络请求
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});
}
}
通过builder创建实例并且赋值。这里我们可以看到有个baseurl的设置,这和接口注解@Get(“部分url”)里的部分url是分开的,实际请求则是两个url拼接起来。
而后通过retrofit实例创建网络请求实例。再通过网络请求实例去进行网络请求。
二、源码解析
解析的主要内容:
2.1、如何创建我们的Retrofit实例
2.2、网络请求接口实例创建
2.3、发送网络请求
2.1、如何创建我们的Retrofit实例:
我们看到上面创建Retrofit的时候使用的是builder模式:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.baidu.com/")//设置网络请求基础url地址
.addConverterFactory(GsonConverterFactory.create())//设置数据解析器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
.build();
builder模式简单说就是将复杂对象的创建和表示相分离。就说Retrofit的创建实际上很复杂,但是它将大部分的重要内容都予以默认值,如果你不去改变就不需要去设置。
我们先来看下Retrofit内部的一些变量:
public final class Retrofit {
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
final List<Converter.Factory> converterFactories;
final List<CallAdapter.Factory> adapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
我们看到第一个变量就是一个Map,这个map是用来放置我们网络请求的配置对象。这个配置对象里面有网络请求的方法、数据转换器、网络请求适配器、网络请求工厂、url等。
再看第二个变量callFactory,这个Call代表的是我们的网络请求器,同步异步都是通过这个call调用,而且我们可以看到这个类是属于okhttp3的。
第三个变量 baseUrl,网络请求的基础地址。
第四个converterFactories,数据转换器工厂的集合,数据转换器工厂:用于生产我们数据转换器converter。如json,服务返回数据给我们json类型,然后通过这个转换器解析,然后在主线程中显示。
第五个adapterFactories,网络请求适配器的集合,网络请求适配器的工厂:用于生成我们的CallAdapter网络请求适配器。
第六个callbackExecutor,回调方法的执行器,主线程切换到子线程、子线程切换到主线程都是用这个方法执行的。
第七个validateEagerly,用于判断是否提前对我们接口当中的注解进行转换的标志位。
这里我们可以看到,成功建立Retrofit的标准就是这么几个变量。
我们先看下关于CallAdapter.Factory:
我们可以看到Factory是在CallAdapter里的抽象类,而这个抽象类的实现类有三种,Default、Executor、rxjava2。
用个例子说明下:假如我们一开始想用ExecutorCall来替换线程,然后我们发现rxjava2更加方便,不需要用handler来替换线程。那么我们只需要加上RxJava2CallAdapterFactory就可以完成我们okhttpCall到Rxjava间的转换。
现在我们回到前面
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.baidu.com/")//设置网络请求基础url地址
.addConverterFactory(GsonConverterFactory.create())//设置数据解析器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
.build();
我们对Retrofit的内部变量有了些了解后,我们再来下build()方法做了什么:
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
第一步就baseUrl的非空判断。
第二步将成员变量callFactory赋给我们的callFactory对象,然后做非空判断。为空就新建个OkhttpClient。这里我们可以看出retrofit本质使用okhttp进行请求。
第三步将成员变量callbackExecutor赋给我们的callbackExecutor对象,然后做非空,为空则新建defaultCallbackExecutor。callbackExecutor实际上就是主线程切换到子线程的执行器。
第四步向集合中添加我们这个的请求适配器adapterFactories。
第五步新建一个数据转换器的集合converterFactories ,在这个存储集合中存储的就是我们默认的转换器工厂,以及我们可以自定我们的转换器工厂,比如前面写的RxJava2CallAdapterFactory。
这里的adapterFactories和converterFactories它们的存储方式都是从首位到末尾遍历的。在集合当做位置越靠前就有越高的使用权限。
第六步创建Retrofit并且将刚刚的对象放入,然后返回。
看到这里我们就已经看完了关于Retrofit的接口实例创建。这个是上面retrofit使用的第二部,下面讲解的是第三步网络请求接口实例创建。
2.2、网络请求接口实例创建:
public interface RetrofitService {
String BASE_URL = "http://IP:port/api/";
@GET("self/getUserSelfInfo")
Observable<LoginResponse> login(@Body String request);
}
//创建 网络请求接口的实例
retrofit_interface request = retrofit.create(retrofit_interface.class);
第一步的网络请求接口就是在第三步这使用retrofit实例create出网络请求的实例。那么现在来看下create方法:
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() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
首先会根据前面说到validateEagerly标识位来判断是否需要进行验证。我们来看下eagerlyValidateMethods方法
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
这个方法显示遍历我们所声明好的方法,然后依次去调用loadServiceMethod方法。loadServiceMethod使用的是一个单例的形式去获取serviceMethodCache,serviceMethodCache就是Retrofit内的第一个变量。然后通过get方法去获取相应的ServiceMethod。如果为空则创建个新的Method。这里可以看到采用的也是builder模式:ServiceMethod.Builder<>(this, method).build()。
那我们就需要来看下builder和build到底做了什么:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
getAnnotations()方法:获取我们网络请求方法里的注释
getGenericParameterTypes():获取网络请求的参数类型
getParameterAnnotations():获取网络请求的注解内容
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
......
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
......
return new ServiceMethod<>(this);
}
第一创建了callAdapter,作用是回调传递到UI线程。这个创建是根据Retrofit创建里设置的callAdapter来判断创建的。
第二callAdapter.responseType()。返回我们想要的类型。如我上面写的是Call<String>,则返回的是String,然后对这个类型进行判断。
第三createResponseConverter()。创建我们的数据类型转换器。即Retrofit创建里设置的GsonConverterFactory.create()。如服务器返回的是json类型的String,我们想要拿到的是User类型,转换器就会将String转成User返回。
第四遍历我们所有方法的注解去调用 parseMethodAnnotation(annotation);方法去解析网络请求的注解。
第五 parameterAnnotationsArray.length获取我们当前方法的参数数量。然后通过parseParameter(p, parameterType, parameterAnnotations);去解析我们的方法参数和注解。
我们看回网络请求接口的实例创建下一步,Proxy.newProxyInstance:
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler()
一看 Proxy就知道是用了动态代理,最终执行方法在InvocationHandler里的invoke(Object proxy, Method method, @Nullable Object[] args)方法中。这个方法调用的其实就是我们定义的接口方法getCall()。
而在这个invoke方法里面有个最核心的代码需要注意下:
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
loadServiceMethod(method)方法会将我们前面配置好的属性去配置我们的ServiceMethod这个对象。也就是我们的请求信息都在里面。然后再去创建okHttpCall对象,最后调用 okHttpCall,并且根据callAdapter去转换成我们的网络请求对象。
loadServiceMethod这个方法上面有讲解过。
new OkHttpCall<>(serviceMethod, args);直接根据我们的serviceMethod和args的参数进行创建。我们来看下创建的OkhttpCall:
final class OkHttpCall<T> implements Call<T> {
private final ServiceMethod<T, ?> serviceMethod;
private final @Nullable Object[] args;
private volatile boolean canceled;
@GuardedBy("this")
private @Nullable okhttp3.Call rawCall;
@GuardedBy("this")
private @Nullable Throwable creationFailure; // Either a RuntimeException or IOException.
@GuardedBy("this")
private boolean executed;
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
这里是变量和构造方法
第一个和第二个就是我们传的serviceMethod和args参数。一个代表网络请求的所有请求数值,args则表示请求的参数。
第四个参数是个okhttp3.call的对象,这个就是实际进行网络访问的类。
构造函数就比较简单,传入参数赋值。
我们下面看下第三个重要的代码:return serviceMethod.callAdapter.adapt(okHttpCall);
这里用到callAdapter,之前说过callAdapter有三种类型,DefaultCallAdapter、ExecutorCallAdapter、rxjava2CallAdapter三种。之类看下rxjava2的:
@Override public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
......
return observable;
}
这个就是通过scheduler这个线程控制的管理工具来完成线程和主线程中的切换。
那么可以看出后面这两行重要代码实现的内容也比较简单。下面来总结下网络请求接口实例创建:
1.动态创建网络请求接口的实例
2.创建serviceMethod对象(包含网络请求的所有注解属性等,通过建造者模式和单例模式完成-对应loadServiceMethod方法)
3.对serviceMethod对象进行网络请求参数配置(即解析网络请求的方法参数如url、请求类型、网络请求适配器、转换器等信息)
4.对serviceMethod对象假如线程切换的操作
5.最终创建并返回一个OkHttpCall类型的网络请求对象
2.3、发送网络请求:
下面以异步请求作为例子:
//对 发送请求 进行封装
Call<String> call = request.getCall();
public void callRequest(){
//发送网络请求
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});
}
创建好Call对象后,调用enqueue方法来发送异步的网络请求。我们来看下实现:
void enqueue(Callback<T> callback);
一个接口,我们来看下实现类ExecutorCallAdapterFactory :
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
这里用了delegate调用了enqueue方法。这个delegate就是个代理的作用,调用的其实是Okhttp的enqueue方法,我们来看下okHttpCall的enqueue方法:
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
......
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
......
});
}
首先是看createRawCall方法:创建OKhttp Request对象和封装我们okhttp的call对象:
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
代码比较简单:就是创建request和call对象。但是就是用这个两个去进行网络请求。我们看回去直接就是调用了call的enqueue方法。然后就是通过callbackExecutor 切换主线程返回解析数据。
总结下流程:
1.对网络请求接口的方法中的每个参数利用对应ParameterHandler进行解析
2.使用OkHttp的Request发送网络请求
3.对返回的数据使用之前设置的数据转换器(GsonConverterFactory)解析返回的数据
4.进行现场奇幻从而在主线程处理返回的数据结果
写到这里Retrofit基本也写完了,写起来还是比较长的,但是如果自己去看这些代码就会快很多。主要还是理解了解为何内部这么写。写的内容可能会有些理解上的错误或者笔误,还望看出端的能够不惜指点。