對okhttp3進(jìn)行封裝的網(wǎng)絡(luò)框架
github
1. 該框架用到的東西
該框架用了大量的注解
該框架依賴于okhttp3
2. 常用注解
1、@GET GET網(wǎng)絡(luò)請求方式
2、@POST POST網(wǎng)絡(luò)請求方式
3、@Headers() 頭信息參數(shù)
4、@Path() 路徑參數(shù),替換url地址中 { } 所括的部分
5、@Query() 查詢參數(shù),將在url地址中追加類似“page=1”的字符串,形成提交給服務(wù)端的請求參數(shù)
6、@QueryMap 查詢參數(shù)集合,將在url地址中追加類似
“type=text&username=abc&password=123”的字符串
7、@FormUrlEncoded 對表單域中填寫的內(nèi)容進(jìn)行編碼處理,避免亂碼
8、@Field() 指定form表單域中每個空間的額name以及相應(yīng)的數(shù)值
9、@FieldMap 表單域集合
10、@Multipart Post提交分塊請求,如果上傳文件,必須指定Multipart
11、@Body Post提交分塊請求
12、@Url 替換包含baseUrl的請求地址
3. 用法
假設(shè)有一個接口
http://www.wanandroid.com/banner/json
3.1創(chuàng)建一個retrofit實(shí)例
Retrofit retrofit= new Retrofit.Builder()
.client(mClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
.build();
參數(shù)說明
mClient
OkHttpClient,配置參考OkHttp
RxJavaCallAdapterFactory.create()
將Call轉(zhuǎn)化為Observable
GsonConverterFactory.create()
將ResponseBody轉(zhuǎn)化為具體的gson解析對應(yīng)的實(shí)體類
baseUrl
接口的公共scheme
如上述接口,可以抽象為http://www.wanandroid.com/
3.2定義接口
public interface RetrofitHttpService {
@GET()
Call<ResponseBody> executGet(@Url String url);
@GET()
Call<ResponseBody> executGet(@Url String url, @QueryMap Map<String, String> maps);
@FormUrlEncoded
@POST()
Call<ResponseBody> executePost(@Url String url, @FieldMap Map<String, String> map);
@POST()
Call<ResponseBody> executeJsonPost(@Url String url, @Body RequestBody body);
@GET
Call<ResponseBody> loadBitmap(@Url String url);
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String url);
}
}
還有一種寫法
public interface WeatherService {
@GET()
Call<ResponseBody> getWeatherData(@Url String url);
}
第二種寫法強(qiáng)調(diào)的是獲取天氣數(shù)據(jù)這個接口,如果需要登錄,則又需要新定義一個接口。第一種則是通用寫法,所有的接口都可以用
3.3 接口調(diào)用
get請求
public void requestGetAsyn(String url, Map<String, String> params, final StringCallback callback) {
RetrofitHttpService service = RetrofitManager.getInstance().create(RetrofitHttpService.class);
Call<ResponseBody> call = service.executGet(url, params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
callback.OnSuccess(response.body().string());
} catch (IOException e) {
e.printStackTrace();
callback.OnError("異常信息:" + e.getMessage());
}
} else {
callback.OnError("錯誤碼:" + response.code());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if (t != null && t.getMessage() != null) {
callback.OnError(t.getMessage());
} else {
callback.OnError(ERROR);
}
}
});
}
post表單提交
public void requestPostAsyn(String url, Map<String, String> params, final StringCallback callback) {
RetrofitHttpService service = RetrofitManager.getInstance().create(RetrofitHttpService.class);
Call<ResponseBody> call = service.executePost(url, params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
callback.OnSuccess(response.body().string());
} catch (IOException e) {
e.printStackTrace();
callback.OnError("異常信息:" + e.getMessage());
}
} else {
callback.OnError("錯誤碼:" + response.code());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if (t != null && t.getMessage() != null) {
callback.OnError(t.getMessage());
} else {
callback.OnError(ERROR);
}
}
});
}
post提交json格式參數(shù)
public void requestPostJsonAsyn(String url, Map<String, String> params, final StringCallback callback) {
RetrofitHttpService service = RetrofitManager.getInstance().create(RetrofitHttpService.class);
MediaType JSON = MediaType.parse("application/json");
JSONObject jsonObject = new JSONObject(params);
RequestBody requestBody = RequestBody.create(JSON, jsonObject.toString());
Call<ResponseBody> call = service.executeJsonPost(url, requestBody);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
callback.OnSuccess(response.body().string());
} catch (IOException e) {
e.printStackTrace();
callback.OnError("異常信息:" + e.getMessage());
}
} else {
callback.OnError("錯誤碼:" + response.code());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if (t != null && t.getMessage() != null) {
callback.OnError(t.getMessage());
} else {
callback.OnError(ERROR);
}
}
});
}
至此,retrofit的基本使用已經(jīng)完事了。
3. 核心類分析
Retrofit
該類包含了下面這些參數(shù)
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> callAdapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
我們先來看看Retrofit的Builder這個內(nèi)部類,這里面提供了許多設(shè)置上述參數(shù)的方法,就不說了,主要來看看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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
分析(自上而下看代碼)
當(dāng)baseUrl為null時,會拋異常
callFactory這個其實(shí)指的就是OkHttpClient(繼承自Call.Factory),當(dāng)我們沒有顯示設(shè)置時,則會使用默認(rèn)的client,為了更加方便的控制網(wǎng)絡(luò),建議設(shè)置一個自定義的client,比如設(shè)置超時時間,攔截器,緩存等。
callbackExecutor,我們來看一下默認(rèn)的執(zhí)行器
Platform
該類主要是獲取當(dāng)前程序是java還是android
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
明顯看出默認(rèn)的Executor,在主線程中。這就是為什么retrofit不用切換到主線程去返回報文,因為報文的回調(diào)方法已經(jīng)在主線程了。
callAdapterFactories
看代碼,CallAdapter是接口,F(xiàn)actory是該接口的一個內(nèi)部抽象類
不論我們有木有addCallAdapterFactory,都會增加一個默認(rèn)的實(shí)現(xiàn)
ExecutorCallAdapterFactory,該類繼承CallAdapter.Factory,該實(shí)現(xiàn)放在自定義CallAdapter之后
ExecutorCallAdapterFactory
我們只看關(guān)鍵代碼
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);
}
});
}
});
從這里就看出了報文是從哪個地方返回的
converterFactories
看代碼,Converter是接口,F(xiàn)actory是該接口的一個內(nèi)部抽象類
無論我們有木有addConverterFactory,都會有一個默認(rèn)的實(shí)現(xiàn)
BuiltInConverters,該類繼承Converter. Factory,該實(shí)現(xiàn)添加在自定義的Converter之前。
BuiltInConverters
第一次看這個類的時候不知道它有何用,稍后在來解析。
在來看下mRetrofit.create(service)這個方法
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.adapt(okHttpCall);
}
});
}
這里用到了jdk動態(tài)代理方法,根據(jù)傳遞的接口返回一個動態(tài)代理,當(dāng)我們調(diào)用接口的方法時,就會調(diào)用invoke方法
從前面我們知道Retrofit有一個serviceMethodCache,里面保存了一系列的serviceMethod,而serviceMethod包含了http請求相關(guān)的信息
ServiceMethod
static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);
private final okhttp3.Call.Factory callFactory;
private final CallAdapter<R, T> callAdapter;
private final HttpUrl baseUrl;
private final Converter<ResponseBody, R> responseConverter;
private final String httpMethod;
private final String relativeUrl;
private final Headers headers;
private final MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
private final ParameterHandler<?>[] parameterHandlers;
再看invoke方法的最后三行代碼
每調(diào)用接口中的一個方法,如果是第一次調(diào)用該方法會根據(jù)當(dāng)前的retrofit和method構(gòu)建出一個serviceMethod對象,并將其保存在緩存中,下次就直接從緩存中區(qū)。
根據(jù)這個serviceMethod構(gòu)建出一個okHttpCall。
最后一句代碼,把一個okHttpCall 類型適配為用戶定義的 service method 的 return type
T adapt(Call<R> call) {
return callAdapter.adapt(call);
}
這個callAdapter是一個接口,最終會調(diào)用它的實(shí)現(xiàn)類的adapt方法,如RxJavaCallAdapterFactory,它的adapt方法
public <R> Observable<Result<R>> adapt(Call<R> call) {
Observable<Result<R>> observable = Observable.create(new CallOnSubscribe<>(call)) //
.map(new Func1<Response<R>, Result<R>>() {
@Override public Result<R> call(Response<R> response) {
return Result.response(response);
}
}).onErrorReturn(new Func1<Throwable, Result<R>>() {
@Override public Result<R> call(Throwable throwable) {
return Result.error(throwable);
}
});
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
在看ExecutorCallAdapterFactory(默認(rèn)的callAdapter)的adapt方法
public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
我們這里只探討ExecutorCallAdapterFactory方式的返回類型Call,調(diào)動call.enqueue
@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是Call這個接口,它的實(shí)現(xiàn)類是OkHttpCall,因此最終調(diào)用的就是OkHttpCall的enqueue方法,該方法最終調(diào)用的OkHttpClient的Call.enqueue方法,至此就將交易發(fā)了出去。
4. 總結(jié)
主要關(guān)注2和3
Retrofit的回調(diào)已經(jīng)放在UI線程,無需再轉(zhuǎn)化線程
圖片請求和文件下載
一般我們會選擇一個圖片框架
如果用Retrofit請求圖片和下載文件,只需要將response轉(zhuǎn)化為流即可
response.body().byteStream() //文件流
responseBody.contentLength() //總長度
對于圖片,通過Bitmap工具類可以直接將流轉(zhuǎn)化為bitmap
對于文件,將流保存在對應(yīng)的文件目錄即可,同時通過已下載的文件流字節(jié)數(shù)/總長度來計算進(jìn)度
InputStream is = response.body().byteStream();
byte[] buf = new byte[1024];
long total = responseBody.contentLength();
while ((len = is.read(buf)) != -1) {
sum += len;
fos.write(buf, 0, len);
callback.Progress(sum * 1.0f / total * 100);
}