標籤(空格分隔): Retrofit RxJavajava
Android 6.0(SDK 23)推出後,Apache的HttpClient被棄用。以前一些經常使用的框架如xUtils,android-async-http都須要依賴HttpClient,如今用起來都不方便了。幸虧有Retrofit補上這個漏,並且還被得更好。 Retrofit是Square公司出口的一個網絡請求框架,本質上是對OkHttp的包裝,很是適合Android平臺使用。目前最新版本是2.0 beta2,因此下面所說的Retrofit都是指Retrofit 2.0。react
在看源碼以前,咱們先熟悉一下Retrofit的使用方式。下面介紹兩種使用Retrofit的方式,分別是Call<T>方式和RxJava方式。都是基於Gradle。在項目中建立項目以後,android
在項目的build.gradle文件中添加Retrofit的依賴,git
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2' compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
建立實體類和一個接口,以下github
public class GithubUser { public String login; public String avatar_url; public String company; } public interface GithubApi { @GET("users/{user}") Call<GithubUser> getUserInfoByName(@Path("user") String name); }
初創化Retrofit對象,以及實例化GithubApi編程
Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") // 這裏有一個坑,若是項目不是部署在域名根目錄下,則必需要以「/」結尾,不然出錯。在1.9以及以前,url是不能以「/」結尾的。 .addConverterFactory(GsonConverterFactory.create(gson)) .client(mClient) .build(); GithubApi api = mRetrofit.create(GithubApi.class);
而後就能夠請求數據了api
api.getUserInfoByName("atanl").enqueue(new Callback<GithubUser>() { @Override public void onResponse(Response<GithubUser> response, Retrofit retrofit) { Toast.makeText(getApplicationContext(), response.body().login, Toast.LENGTH_SHORT).show(); } @Override public void onFailure(Throwable t) { t.printStackTrace(); } });
上面代碼涉及了兩個步驟,獲取到一個Call<GithubUser>實例,而後調用它的enqueue方法。其實在前一個步驟http請求是尚未發送的,只有調用的enqueue方法以後纔開始請求。這和java基礎多線程編程中的Call或者Future實際上是一個概念。關於Call<T>下面會有更多說明。緩存
和RxJava配合使用時,本質是對Call<T>這種方式的一個包裝,就是把Call<T>實例轉變成Observable<T>對象。等下作更多的說明,先看一下樣例代碼。ps,若是不知道RxJava是什麼鬼的童鞋,能夠參考給 Android 開發者的 RxJava 詳解。網絡
在上面的build.gradle的基礎上再添加下面依賴,多線程
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2' compile 'io.reactivex:rxandroid:1.0.1'
在生成Retrofit實例的時候,添加一個RxJava的CallAdapterFactory。以下,
Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") // 這裏有一個坑,若是項目不是部署在域名根目錄下,則必需要以「/」結尾,不然出錯。在1.9以及以前,url是不能以「/」結尾的。 .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(mClient) .build();
向GithubApi接口添加一個方法。以下,
public interface GithubApi { @GET("users/{user}") Call<GithubUser> getUserInfoByName(@Path("user") String name); GET("users/{user}") Observable<GithubUser> getUserInfoObservable(@Path("user") String name); }
接下下來就是使用這個接口獲取數據了,
api.getUserInfoObservable("atanl") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> { Toast.make(getApplicationContext(), user.login + "", Toast.LENGHT_SHORT).show(); });
上面代碼中經過getUserInfoObservable獲取獲得的Observable<GithubUser>其實就是經過RxJavaCallAdapterFactory對Call<GithubUser>的一個轉換。
看過上面的樣例,不論是直接經過Call<T>方式仍是和RxJava配合使用,一個必經的步驟應是經過Retrofit的create方法來實例化一個接口實例(如GithubApi)。那麼咱們就一直看一下create方法是怎麼工做的。
public <T> T create(final Class<T> service) { ... 這裏省略了若干行代碼 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { // 留意這個代理實例 ... 這裏省略了若干行代碼 @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { ... 這裏省略了若干行代碼 return loadMethodHandler(method).invoke(args); } }); }
上面代碼說白了就是經過Java的動態代理機制,實例化一個實現了GithubApi的的代理客戶端(即Proxy對象),而後調用GithubApi的方法時,操做會被代理到一個InvocationHandler中。如上面代碼,對GithubApi中方法的訪問其實都是代理到了最後一行代碼
return loadMethodHandler(method).invoke(args);
很簡單,意思就是去某個地方獲取一個Handler出來,而後調用它來產生結果,而後做爲接口方法的返回值。那麼咱們繼續跟進loadMethodHandler,看一下load出來的MethodHandler是長什麼樣子的。
MethodHandler<?> loadMethodHandler(Method method) { MethodHandler<?> handler; synchronized (methodHandlerCache) { handler = methodHandlerCache.get(method); if (handler == null) { handler = MethodHandler.create(this, method); // 這是是關鍵 methodHandlerCache.put(method, handler); } } return handler; }
這個方法中能夠看出,Retrofit會先在緩存中找下是否是已經有如今的MethodHandler實例能夠獲取,若是有就直接拿出來用。不然,create一個。咱們先不用管它的緩存,只看create。因此繼續,跟進create方法中,
static MethodHandler<?> create(Retrofit retrofit, Method method) { CallAdapter<Object> callAdapter = (CallAdapter<Object>) createCallAdapter(method, retrofit); Type responseType = callAdapter.responseType(); Converter<ResponseBody, Object> responseConverter = (Converter<ResponseBody, Object>) createResponseConverter(method, retrofit, responseType); RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit); return new MethodHandler<>(retrofit, requestFactory, callAdapter, responseConverter); }
這裏有五行代碼,每行都作了一件事。 第一,建立一個CallAdapter,這裏是重點,等下詳述。 第二,獲取到接口(GithubApi)方法的返回值的泛型類型(兩個方法都是GithubUser)。 第三,實例化一個數據結果的轉換器,把Http請求所得的response轉換成POJO。 第四,實例化一個請求工廠類,用於把帶有@GET,@POST等註解的接口方法生成對的Http請求實例。 第五,返回咱們想要的MethodHandler對象。
當咱們拿到MethodHandler對象以後,就能夠調用它的invoke方法來生成結果,而且接口方法的返回值。那麼咱們再看MethodHandler的invoke方法,
Object invoke(Object... args) { return callAdapter.adapt(new OkHttpCall<>(retrofit, requestFactory, responseConverter, args)); // 記住這個OkHttpCall類 }
只是很簡單的調用以前第一步獲取到的CallAdapter實例的adapt方法。方法名很樸實,也很應景,意思就是適配,把一個Call<T>實例(即OkHttpCall<T>對象)適配成一個Call<T>實例(這種狀況下,其實適配器就是直接返回Call<T>實例,沒有作什麼轉換),或者一個Observale<T>實例(這種狀況下,下面細說)。
這裏涉及到兩個關鍵點,一個是CallAdapater,一個是OkHttpCall<T>。後者咱們等下再說,先說CallAdapter。
CallAdapter只是一個接口,不能直接實例化。在Retrofit的體系中,全部的CallAdapter實例都是經過相應的CallAdapterFactory來產生的,如上面提到的RxCallAdapterFactory。
咱們先回到MethodHandler.create方法的第一行代碼,即建立一個CallAdapter。跟進代碼,看一下CallAdapterFactory是怎麼生產出一個CallAdapter來。
private static CallAdapter<?> createCallAdapter(Method method, Retrofit retrofit) { Type returnType = method.getGenericReturnType(); ... // 這裏省略了若干行代碼。 Annotation[] annotations = method.getAnnotations(); try { return retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { ... // 這裏省略了若干行代碼。 } } ////// public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); } ////// public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { ... // 這裏省略了若干行代碼。 int start = adapterFactories.indexOf(skipPast) + 1; for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } ... // 這裏省略了若干行代碼。 }
從上面代碼最後的循環中能夠看出,CallAdapter實際上是經過遍歷各個AdapterFactory來,而後根據返回接口方法的返回類型(如上面GithubApi的第二個方法的返回類型是Observable<T>)來決定由哪一個Factory來實例化CallAdapter。若是是返回類型是Observable<T>,那麼就由RxCallAdapterFactory(在建立Retrofit時向其中註冊)來代勞。
RxCallAdapterFactory實現了CallAdapter.Factory接口,因此咱們從get方法切入,
@Override public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... // 這裏省略若干行代碼 CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType); ... // 這裏省略若干行代碼 return callAdapter; } private CallAdapter<Observable<?>> getCallAdapter(Type returnType) { Type observableType = Utils.getSingleParameterUpperBound((ParameterizedType) returnType); ... // 這裏省略若干行代碼 return new SimpleCallAdapter(observableType); }
上面代碼中,咱們能夠看到,RxCallAdapterFactory最後產生的CallAdapter其實就是一個SimpleCallAdapter實例。它實現了CallAdapter接口,因此咱們也來看一下他的adapt方法。
@Override public <R> Observable<R> adapt(Call<R> call) { return Observable.create(new CallOnSubscribe<>(call)) // .flatMap(new Func1<Response<R>, Observable<R>>() { @Override public Observable<R> call(Response<R> response) { if (response.isSuccess()) { return Observable.just(response.body()); } return Observable.error(new HttpException(response)); } }); }
上面代碼涉及到了一個CallOnSubscribe類,它是Observable.OnSubscribe接口的一個實現。因此咱們來看下它的call方法。
@Override public void call(final Subscriber<? super Response<T>> subscriber) { ... // 這裏省略若干行代碼 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(); } }
上面代碼意思很明顯,調用Call<T>的execute方法訪問網絡數據,而且把結果轉化爲Observable的數據源。
好,到這裏RxCallAdapterFactory的工做原理已經知道了,可能有童鞋會對Call<T>接口還有疑問。沒有問題,下面就來看一個Call<T>的實現類——OkHttpCall<T>。
以前有說過Retrofit其實就是對OkHttp的一個包裝。空穴來風,從這個OkHttpCall類就能夠緣由。OkHttpCall類是Call接口一個實現,採起了委派模式,調用它的execute方法或者enqueue方法其實都是委派到com.squareup.okhttp.Call實例來實現,而後再把結果包裝成POJO形式的Response。如execute方法的源碼,
public Response<T> execute() throws IOException { synchronized (this) { ... // 這裏省略若干行代碼 com.squareup.okhttp.Call rawCall = createRawCall(); ... // 這裏省略若干行代碼 return parseResponse(rawCall.execute()); } private Response<T> parseResponse(com.squareup.okhttp.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); ... // 這裏省略若干行代碼 ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = responseConverter.convert(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { ... // 這裏省略若干行代碼 } }
另外,Call<T>接口的enqueue方法其實和execute是差很少的,只不過是多線程方式運行。源碼本身去看,要跟蹤到OkHttp的Call<T>中,我懶得寫了。
打完收工。