Retrofit的使用
我們以網上很流行的天氣預報的接口爲例,模擬一個Retrofit
的使用例子,首先我們需要一個定義請求的接口類WeatherService.java
:
public interface WeatherService {
@GET("/Ecalender/api/v2/weather")
Call<WeatherData> getWeather(@Query("date") String date, @Query("citykey") String citykey);
@GET("/Ecalender/api/v2/weather")
Observable<WeatherData> getWeatherObservable(@Query("date") String date, @Query("citykey") String citykey);
}
WeatherService.java
例舉了兩種不同的返回方式,下面我們使用標準的Call<T>
的返回方式舉例調用過程代碼,
private void getWeather() {
retrofit = new Retrofit.Builder()
.baseUrl("http://v.juhe.cn/calendar/day")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
service = retrofit.create(WeatherService.class);
String nowDate = new SimpleDateFormate("YYYY-MM-DD").format(new Date());
Call<WeatherData> result = service.getWeather(nowDate, "101010100");
try {
Response<WeatherData> r = result.execute();
WeatherData data = r.body();
Log.e("David", "ResponseBody data = " + data);
if (data != null) {
Log.e("David", "RxLoaderCallback data = " + data.weatherinfo.desc);
Log.e("David", "RxLoaderCallback data = " + data.result.lunarYear);
Log.e("David", "RxLoaderCallback data = " + data.result.weekday);
}
} catch (IOException e) {
e.printStackTrace();
}
}
Retrofit
調用步驟:
- 通過
Retrofit.Builder
配置好各項參數,然後調用build()
創建Retrofit
對象。 - 將http接口文件
WeatherService.class
傳遞給Retrofit
對象,然後創建WeatherService
代理對象service
。 - 通過
WeatherService
代理service
對象調用方法getWeather()
得到Call<WeatherData>
對象result
。 - 通過
Call<WeatherData>
對象result
的excute()
方法執行http網絡請求,得到返回結果Response<Weather>
對象r
。 - 通過
Response<Weather>
對象r
的body()
方法得到我們真正需要的WeatherData
對象。
創建Retrofit
首先new
了一個Retrofit.Builder
對象,然後配置各種參數,這裏類似與setter
的功能,其中參數baseUrl
是必須要配置的,不然會報錯。最後調用Retrofit.Builder
的build()
方法生成Retrofit
對象,其內部也是使用了new Retrofit()
的方式創建新對象。。
public static final class Builder {
......
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
......
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
}
創建Http代理對象
得到Retrofit
對象後,通過Retrofit
對象的create()
方法創建Http
的代理對象,
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();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable 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);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
通過代理對象得到Call對象
當我們通過代理對象發起請求的時候會進入上面代理對象的InvocationHandler
對象的invoke()
方法中,然後會進入Retrofit
對象的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 = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
在方法中,會首先判斷是否有緩存ServiceMethod
對象,如果有緩存,則直接返回之前緩存的ServiceMethod
對象,否則調用ServiceMethod.parseAnnotations()
創建新的ServiceMethod
對象,並放入緩存。我們看看ServiceMethod
是怎樣創建新的對象的:
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
.......
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
方法內部是通過HttpServiceMethod
來創建的,我們在進入HttpServiceMethod
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
........
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
........
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
根據我們上面的調用例子,沒有Kotlin
支持,所以直接返回的就是HttpServiceMethod
子類CallAdated
對象,注意此時我們返回的HttpServiceMethod
對象創建時傳入了okhttp3.Call.Factory
對象,CallAdapter
對象因爲沒有配置CallAdapter.Factory
所以使用的默認,直接返回Call<T>
,後面會使用到。
承接上面的代理對象的請求調用其實是調用了我們剛剛生成的HttpServiceMethod
對象的invoke()
方法,
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
在HttpServiceMethod
對象的invoke()
方法中,新建了一個OkHttpCall
對象,然後通過適配方法adapt
轉換成需要的目標接口類型。根據我們上面的demo調用例子,也就是執行了CallAdated
對象的adapt()
,
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
這裏的CallAdapter
對象因爲沒有配置CallAdapter.Factory
所以使用的默認,直接返回Call<T>
,也就是上面第3步得到Call<WeatherData>
對象result
。其實返回的也是一個OkHttpCall
對象。
通過OkHttpCall對象執行網絡請求
上面地4步,Call<WeatherData>
對象result
的excute()
也就是發起網絡請求,實際執行的是OkHttpCall
對象的excute()
方法
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
.......
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
裏面通過createRawCall()
方法調用了OkHttpClient
的newCall()
創建了一個RealCall
對象:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
然後OkHttpCall
對象的excute()
方法最後一行,先調用excute()
方法執行網絡請求。再跟進去就是OkHttp
的網絡請求細節了,所以不再跟進。執行完OkHttp
的網絡請求後,胡調用parseResponse()
方法對返回結果進行處理。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
.......
}
}
可以看到請求成功後,會將返回成功body
轉換成目標類型之後放入Retrofit
中的Response
的body
。所以我們上面在第5步的時候是通過Response<Weather>
對象r
的body()
方法得到我們真正需要的WeatherData
對象。
由於個人水平可能有限,如果上述博文有不正確的地方,歡迎大家指正