今天是九月的第四天了,学校也正式开学,趁着大学最后一年的这大好时光,抓紧时间赶快学习新知识吧!今天想要与你们一块儿分享的是Retrofit,因为网上已经有许多讲解Retrofit使用的文章了,本篇文章只会给一个小小的示例,以这个示例做为入口分析其源码,一样也会贴上流程图,以避免迷路。话很少说,咱们开始吧!!!
html
Retrofit是近来十分火热的一个网络请求开源库,Android开发者使用的网络请求开源库从最先的HttpClient与HttpURLConnection到2013年Google官方推出的Volley,接着就到了如今很火的OKHttp,最后才到了Retrofit。网络请求开源库的演变也正是移动互联网下用户对网络需求的真实写照。有哪一个用户不想使用APP的时候网络加载速度更快,更省流量,更加安全呢?也就是基于用户的这些需求,才有了许多开源库的不断迭代,而Retrofit能够说正是当下最适合开发者使用的网络请求开源库之一。
何出此言呢?首先它是由大名鼎鼎的square公司出品的,或许你不知道square公司,但你应该认识Jake Wharton,不过他最近已经到谷歌去了,假若你连他都不知道,那你应该使用过他开发的这些开源库:OkHttp
,picasso
,butterknife
,RxAndroid
等等,能够说Retrofit是由一个十分厉害的公司开发和维护的,因此你大能够放心地在你的项目中使用。前端
尽管Retrofit十分强大,可是他却不必定适合全部场景,正所谓术业有专攻,咱们也没必要大材小用,若是是一些频繁可是访问量很小的网络请求,那么Volley就足以对付了,接下来我列举一下Retrofit广泛的使用场景。java
RESTful API
的设计风格。若是你对这种风格不熟悉,建议你看看阮一峰大神的这篇文章,或者向你的后台小伙伴请教一番。若是你符合以上三种状况,固然是选择Retrofit啦!
android
说了这么多,咱们就经过下面这个栗子来看看他究竟好在哪里?
须要说明的是:这个例子是用来获取干货集中营API上面的数据
一、首先定义一个常量用来描述要访问的服务器主机的地址ios
public class GankConfig {
public static final String HOST = "http://gank.io/api/";
}复制代码
二、定义返回数据的bean类git
public class GankData {
public List<String> category;
public Result results;
public class Result{
@SerializedName("Android")
public List<Gank> androidList;
@SerializedName("休息视频")
public List<Gank> restVideoList;
@SerializedName("iOS")
public List<Gank> iosList;
@SerializedName("福利")
public List<Gank> meiZiList;
@SerializedName("拓展资源")
public List<Gank> extendResourceList;
@SerializedName("瞎推荐")
public List<Gank> suggestionList;
@SerializedName("App")
public List<Gank> appList;
@SerializedName("前端")
public List<Gank> webList;
}
}复制代码
三、定义要访问的接口github
public interface GankRetrofit {
//这里以获取指定日期的内容为例子
@GET("day/{year}/{month}/{day}")
GankData getDailyData(@Path("year") int year, @Path("month") int month, @Path("day") int day);
}复制代码
四、用单例模式建立一个Retrofit客户端web
public class GankRetrofitClient {
private volatile static GankRetrofit gankRetrofit;
private static Retrofit retrofit;
private GankRetrofitClient(){}
static{
Gson date_gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create();
retrofit = new Retrofit.Builder()
.baseUrl(GankConfig.HOST)
.addConverterFactory(GsonConverterFactory.create(date_gson))//添加一个转换器,将gson数据转换为bean类
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加一个适配器,与RxJava配合使用
.build();
}
public static GankRetrofit getGankRetrofitInstance() {
if (gankRetrofit==null){
synchronized (GankRetrofitClient.class){
if (gankRetrofit==null){
gankRetrofit=retrofit.create(GankRetrofit.class);
}
}
}
return gankRetrofit;
}
}复制代码
五、使用Retrofit进行网络请求面试
GankData data= GankRetrofitClient.getGankRetrofitInstance().getDailyData(2017, 9, 1);复制代码
首先咱们先从上面的第4步开始解析源码,有下面这段代码:设计模式
retrofit = new Retrofit.Builder()
.baseUrl(GankConfig.HOST)
.addConverterFactory(GsonConverterFactory.create(date_gson))//添加一个转换器,将gson数据转换为bean类
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加一个适配器,与RxJava配合使用
.build();复制代码
很明显这个是使用了Builder模式,接下来咱们一步一步来看里面作了什么?首先是Builder()。
public Builder() {
this(Platform.get());
}
Builder(Platform platform) {
this.platform = platform;
//添加转换器,请见下面关于addConverterFactory()的讲解
converterFactories.add(new BuiltInConverters());
}复制代码
构造方法中的参数是Platform的静态方法get(),接下来就看看get()。
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
}复制代码
能够看到,Retrofit支持多平台,包括Android与JAVA8,它会根据不一样的平台设置不一样的线程池。
先来看看到目前为止咱们分析到哪里了
接下来看一下baseUrl()方法。
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}复制代码
很容易理解,baseUrl()是配置服务器的地址的,若是为空,那么就会抛出异常。
接着是addConverterFactory()
private final List<Converter.Factory> converterFactories = new ArrayList<>();
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}复制代码
你们是否是还记得刚才在Builder()方法初始化的时候,有这样一行代码:
converterFactories.add(new BuiltInConverters());复制代码
能够看到,converterFactories在初始化的时候就已经添加了一个默认的Converter,那咱们手动添加的这个GsonConverter是干什么用的呢?
public final class GsonConverterFactory extends Converter.Factory {
public static GsonConverterFactory create() {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
return new GsonConverterFactory(gson);
}
private final Gson gson;
private GsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}复制代码
其实这个Converter主要的做用就是将HTTP返回的数据解析成Java对象,咱们常见的网络传输数据有Xml、Gson、protobuf等等,而GsonConverter就是将Gson数据转换为咱们的Java对象,而不用咱们从新去解析这些Gson数据。
接着看addCallAdapterFactory()
private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}复制代码
能够看到,CallAdapter一样也被一个List维护,也就是说用户能够添加多个CallAdapter,那Retrofit总得有一个默认的吧,默认的是什么呢?请看接下来的build()。
最后看一下build()
public Retrofit build() {
//检验baseUrl
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//建立一个call,默认状况下使用okhttp做为网络请求器
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
//添加一个默认的callAdapter
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}复制代码
首先Retrofit会新建一个call,其实质就是OKHttp,做用就是网络请求器;接着在上一点中咱们困惑的callAdapter也已经可以获得解决了,首先Retrofit有一个默认的callAdapter,请看下面这段代码:
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
if (callbackExecutor != null) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
return DefaultCallAdapterFactory.INSTANCE;
}
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("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);
}
});
}
});
}
@Override public boolean isExecuted() {
return delegate.isExecuted();
}
@Override public Response<T> execute() throws IOException {
return delegate.execute();
}
@Override public void cancel() {
delegate.cancel();
}
@Override public boolean isCanceled() {
return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override public Call<T> clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
@Override public Request request() {
return delegate.request();
}
}
}复制代码
能够看到默认的callAdapter是ExecutorCallAdapterFactory。callAdapter其实也是运用了适配器模式,其实质就是网络请求器Call的适配器,而在Retrofit中Call就是指OKHttp,那么CallAdapter就是用来将OKHttp适配给不一样的平台的,在Retrofit中提供了四种CallAdapter,分别以下:
为何要提供如此多的适配器呢?首先是易于扩展,例如用户习惯使用什么适配器,只须要添加便可使用;再者RxJava如此火热,由于其切换线程十分的方便,不须要手动使用handler切换线程,而Retrofit使用了支持RxJava的适配器以后,功能也会更增强大。
综上咱们已经将使用Builder模式建立出来的Retrofit实例分析完毕了,咱们只须要对相关的功能进行配置便可,Retrofit负责接收咱们配置的功能而后进行对象的初始化,这个也就是Builder模式屏蔽掉建立对象的复杂过程的好处。如今咱们再次用流程图来梳理一下刚才的思路。
我最初使用Retrofit的时候以为有一个地方十分神奇,以下:
GankRetrofit gankRetrofit=retrofit.create(GankRetrofit.class);
GankData data= gankRetrofit.getDailyData(2017, 9, 1);复制代码
要想解惑,首先得对动态代理有所了解,若是你对动态代理还不是很清楚,请点击这里了解动态代理的原理,以后再接着往下看。
咱们就以这里为切入点开始分析吧!首先是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, 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);
}
});
}复制代码
咱们主要看Proxy.newProxyInstance方法,它接收三个参数,第一个是一个类加载器,其实哪一个类的加载器都无所谓,这里为了方便就选择了咱们所定义的借口的类加载器;第二个参数是咱们定义的接口的class对象,第三个则是一个InvocationHandler匿名内部类。
那你们应该会有疑问了,这个newProxyInstance到底有什么用呢?其实他就是经过动态代理生成了网络请求接口的代理类,代理类生成以后,接下来咱们就可使用ankRetrofit.getDailyData(2017, 9, 1);
这样的语句去调用getDailyData方法,当咱们调用这个方法的时候就会被动态代理拦截,直接进入InvocationHandler的invoke方法。下面就来说讲它。
invoke方法
它接收三个参数,第一个是动态代理,第二个是咱们要调用的方法,这里就是指getDailyData
,第三个是一个参数数组,一样的这里就是指2017, 9, 1
,收到方法名和参数以后,紧接着会调用loadServiceMethod方法来生产过一个ServiceMethod对象,这里的一个ServiceMethod对象就对应咱们在网络接口里定义的一个方法,至关于作了一层封装。接下来重点来看loadServiceMethod方法。
loadServiceMethod方法
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;
}复制代码
它调用了ServiceMethod类,而ServiceMethod也使用了Builder模式,直接先看Builder方法。
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
//获取接口中的方法名
this.method = method;
//获取方法里的注解
this.methodAnnotations = method.getAnnotations();
//获取方法里的参数类型
this.parameterTypes = method.getGenericParameterTypes();
//获取接口方法里的注解内容
this.parameterAnnotationsArray = method.getParameterAnnotations();
}复制代码
再来看build方法
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);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
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);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}复制代码
代码稍微有点长,可是思路很清晰,主要的工做有
一、首先对注解的合法性进行检验,例如,HTTP的请求方法是GET仍是POST,若是不是就会抛出异常;
二、根据方法的返回值类型和方法注解从Retrofit对象的的callAdapter列表和Converter列表中分别获取到该方法对应的callAdapter和Converter;
三、将传递进来的参数与注解封装在parameterHandlers中,为后面的网络请求作准备。
先用流程图梳理一下刚才的思路:
回头看一下上一小节讲解create方法时咱们有这一行代码:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);复制代码
他将咱们刚才获得的serviceMethod与咱们实际传入的参数传递给了OkHttpCall,接下来就来瞧瞧这个类作了些什么?
final class OkHttpCall<T> implements Call<T> {
private final ServiceMethod<T, ?> serviceMethod;
private final Object[] args;
private volatile boolean canceled;
// All guarded by this.
private okhttp3.Call rawCall;
private Throwable creationFailure; // Either a RuntimeException or IOException.
private boolean executed;
OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
}复制代码
很惋惜,咱们好像没有看到比较有用的东西,只是将传进来的参数进行了赋值,那咱们就接着看create方法中的最后一行吧!
create方法的最后一行是这样的:
return serviceMethod.callAdapter.adapt(okHttpCall);复制代码
最后是调用了callAdapter的adapt方法,上面咱们讲到Retrofit在决定使用什么callAdapter的时候是看咱们在接口中定义的方法的返回值的,而在咱们的例子中使用的是RxJava2CallAdapter
,所以咱们就直接看该类中的adapt方法吧!
@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);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return observable;
}复制代码
首先在adapt方法中会先判断是同步请求仍是异步请求,这里咱们以同步请求为例,直接看CallExecuteObservable。
final class CallExecuteObservable<T> extends Observable<Response<T>> {
private final Call<T> originalCall;
CallExecuteObservable(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
observer.onSubscribe(new CallDisposable(call));
boolean terminated = false;
try {
//重点看这里
Response<T> response = call.execute();
if (!call.isCanceled()) {
observer.onNext(response);
}
if (!call.isCanceled()) {
terminated = true;
observer.onComplete();
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if (!call.isCanceled()) {
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}
private static final class CallDisposable implements Disposable {
private final Call<?> call;
CallDisposable(Call<?> call) {
this.call = call;
}
@Override public void dispose() {
call.cancel();
}
@Override public boolean isDisposed() {
return call.isCanceled();
}
}
}复制代码
在subscribeActual方法中去调用了OKHttpCall的execute方法开始进行网络请求,网络请求完毕以后,会经过RxJava的操做符对返回来的数据进行转换,并进行线程的切换,至此,Retrofit的一次使用也就结束了。最后咱们再用一张完整的流程图总结上述的几个过程。
相信经过上面的详解,你们对Retrofit应该有了一个比较全面的认识,与其说它是一个网络请求框架不如说他作了一层封装,使得咱们可以更方便的间接使用了RxJava与OkHttp。从某种意义上来说咱们从源码中更应该学习其对设计模式的正确运用,使得整个框架的耦合度大大下降,调用者也使用得更加简洁。最后但愿这篇文章可以对你们的面试有所帮助!