今天是九月的第四天了,學校也正式開學,趁着大學最後一年的這大好時光,抓緊時間趕快學習新知識吧!今天想要與你們一塊兒分享的是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。從某種意義上來說咱們從源碼中更應該學習其對設計模式的正確運用,使得整個框架的耦合度大大下降,調用者也使用得更加簡潔。最後但願這篇文章可以對你們的面試有所幫助!