用了Retrofit这么久 ,感觉Retrofit的架构设计牛逼,是学习设计思想和设计模式的不二典范。但今天不是来详细分析这个框架的原理,主要简单了解下Retrofit的CallAdapter.Factory及怎么通过CallAdapter.Factory兼容OkHttp和RxJava。
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
.baseUrl("https://api.douban.com/v2/").client(httpClient.build())
.build();
BookService service = retrofit.create(BookService.class);
为了使Retrofit支持数据返回自动解析成实体,添加:
addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
为了使Retrofit支持Rxjava使用,添加:
addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
本次框架版本是V2.0以上,众所周知,Retrofit的底层网络请求使用就是OkHttp,而OkHttp的底层是基于线程池和Sokcet实现,并不是基于HttpURLConnection,对于这些框架的的详细使用、优缺点、底层原理这里就不多说了。
OkHttp的简单用法
异步请求--对应enqueue方法
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient build = builder.build();
Request.Builder builder1 = new Request.Builder();
builder1.url("https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1");
Request request = builder1.build();
// build.newCall(request).execute();
build.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
//运行在子线程
}
@Override
public void onResponse(okhttp3.Call call, final okhttp3.Response response) throws IOException {
//运行在子线程
}
});
注意:callback是运行在子线程,是因为使用了线程池执行异步调用 ,不能更新UI
同步请求--对应execute方法
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient build = builder.build();
Request.Builder builder1 = new Request.Builder();
builder1.url("https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1");
Request request = builder1.build();
try {
okhttp3.Response response = build.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
上面由于是在主线程执行网络 请求,会报如下错误:
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.ja
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:86)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
//省略...
Retrofit的简单用法
同okhttp一样也有异步和同步的操作
先定义一个数据接口
public interface BookService {
//https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1
/**
* Retrofit网络请求测试
* @param name
* @param tag
* @param start
* @param count
* @return
*/
@GET("book/search")
Call getSearchBook(@Query("q") String name,
@Query("tag") String tag,
@Query("start") int start,
@Query("count") int count);
/**
* Retrofit和Rxjava结合网络请求测试
* @param name
* @param tag
* @param start
* @param count
* @return
*/
@GET("book/search")
Observable getSearchBook2(@Query("q") String name,
@Query("tag") String tag, @Query("start") int start,
@Query("count") int count);
}
异步请求--对应enqueue方法
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.build();
BookService service = retrofit.create(BookService.class);
Call call = service.getSearchBook("金瓶梅", null, 0, 1);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
//运行在主线程
}
@Override
public void onFailure(Call call, Throwable t) {
//运行主线程
}
});
通过上面知道,使用Retrofit 不需要考虑线程调度及数据解析的问题,Retrofit已经给我们做好了,真是省事的框架。
注意:callback是运行在主线程(最后使用了Handler切换到了主线程) ,可以能更新UI
同步请求--对应execute方法
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.build();
BookService service = retrofit.create(BookService.class);
//同步
Call call = service.getSearchBook("金瓶梅", null, 0, 1);
try {
Response execute = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
和OkHttp一样在主线程执行网络请求也会报错NetworkOnMainThreadException,为了使同步请求不报错,只能使用子线程执行上面代码。
Retrofit和Rxjava组合的简单用法
如果你熟练使用Rxjava,又想用Rxjava进行网络请求,Retrofit已经提供了完美的支持,只需要添加一行代码就支持了,只不过接口返回类型不是Call,而是被观察者Observable。
由于Rxjava是异步的,它又是怎么完美结合Retrofit的同步和异步请求的呢?后面会说到,这可是本篇的重点。我们先来看看Retrofit和Rxjava组合的常规用法:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl("https://api.douban.com/v2/").client(httpClient.build())
.build();
BookService service = retrofit.create(BookService.class);
io.reactivex.Observable observable = service.getSearchBook2("金瓶梅", null, 0, 1);//创建观察者
observable.subscribeOn(Schedulers.io())//请求数据的事件发生在io线程
.observeOn(AndroidSchedulers.mainThread())//请求完成后在主线程更显UI
.subscribe(new io.reactivex.Observer() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Book book) {
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
到这里我先简单说下RxJava的线程调度:
create() , just() , from() 等 -- 事件产生
map() , flapMap() , scan() , filter() 等 -- 事件加工
subscribe() -- 事件消费
总之:
这里就不举例说明了,可以自行写个demo体会。
回归正题,我们看下创建Retrofit对象时,addCallAdapterFactory方法的参数类型是CallAdapter.Factory,它的作用:网络执行适配器工厂类,会根据接口里面定义的类型来找到合适的适配器。
接口类型:
不论是接口返回返回类型为Observable还是Call,最终都使用了okhttp3.Call执行网络请求,而OkHttpCall(在retrofit2包下)封装了okhttp3.Call的相关方法。特别说明这里的Call就是OkHttpCall(实现Call接口),而OkHttpCall分为同步请求和异步请求,分别对应的是execute方法和enqueue方法。
上面两种接口是怎么区分?
创建Retrofit对象时,通过调用Retrofit.Builder的addCallAdapterFactory方法添加了平台适配器的工厂类CallAdapter.Factory,它的两个子类是ExecutorCallAdapterFactory和RxJava2CallAdapterFactory。
默认是使用ExecutorCallAdapterFactory。看源码:
Retrofit类下:
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 adapterFactories = new ArrayList<>(this.adapterFactories);
//添加默认的适配器
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
Platform类下:
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();
//根据主线程Executor实例化了ExecutorCallAdapterFactory
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);
}
}
}
上面的callbackExecutor主要作用是
将当前的数据调度到主线程去。
最终在接口调用时,通过根据接口返回类型returnType和annotations(这个参数实际没用上)返回指定的CallAdapter.Factory,看源码:
ServiceMethod类下:
private CallAdapter createCallAdapter() {
//省略...
try {
//noinspection unchecked
return (CallAdapter) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
Retrofit类下:
/**
* Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
* #callAdapterFactories() factories}.
*
* @throws IllegalArgumentException if no call adapter available for {@code type}.
*/
public CallAdapter, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
/**
* Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
* #callAdapterFactories() factories} except {@code skipPast}.
*
* @throws IllegalArgumentException if no call adapter available for {@code type}.
*/
public CallAdapter, ?> nextCallAdapter(@Nullable 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;
}
}
//省略...
}
所以,
当接口返回类型是Observable时,使用RxJava2CallAdapterFactory执行网络请求,必须通过addCallAdapterFactory方法添加该适配器;
当接口返回类型是Call时,使用ExecutorCallAdapterFactory执行网络请求;
addCallAdapterFactory及RxJava2CallAdapterFactory的使用理解
1.当addCallAdapterFactory的值是RxJava2CallAdapterFactory.createAsync(),由于isAsync是true,看源码:
Observable> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
故创建的是CallEnqueueObservable,这时候我们可以不指定Observable的事件发生的线程,平时都是通过如下代码指定:
observable.subscribeOn(Schedulers.io())
之所以可以不指定是因为而CallEnqueueObservable是异步的(CallExecuteObservable是同步请求使用),底层使用了线程池执行进行异步执行,相当在子线程执行了网络请求。
事件消费所在线程(观察者所在线程)可以不指定,默认就是事件产生线程。如果需要进行ui更新必须指定main线程,
AndroidSchedulers.mainThread()。
实例代码:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())//createAsync
.baseUrl("https://api.douban.com/v2/").client(httpClient.build())
.build();
BookService service = retrofit.create(BookService.class);
io.reactivex.Observable observable = service.getAppType();//创建观察者
observable.observeOn(AndroidSchedulers.mainThread())//请求完成后在主线程更显UI
//省略...
2.当addCallAdapterFactory的值是RxJava2CallAdapterFactory.create(),由于isAsync是false
故创建的是CallExecuteObservable,这时候必须指定网络请求Observable的事件发生的IO线程,即:
observable.subscribeOn(Schedulers.io())
如果不指定IO线程,会出现NetworkOnMainThreadException,这是因为主线程执行了网络请求。为什么会出现这种异常?容我先吸口烟,再慢慢道来。上面说了CallExecuteObservable是同步请求的,底层没有开辟线程池进行异步调用,简单说就是没有在子线程执行网络请求。
实例代码:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//create
.baseUrl("https://api.douban.com/v2/").client(httpClient.build())
.build();
BookService service = retrofit.create(BookService.class);
io.reactivex.Observable observable = service.getAppType();//创建观察者
observable.subscribeOn(Schedulers.io())//请求数据的事件发生在io线程
observable.observeOn(AndroidSchedulers.mainThread())//请求完成后在主线程更显UI
//省略...
平时开发中默认都是使用 RxJava2CallAdapterFactory.create()多,也就是说Retrofit结合Rxjava使用,实际使用的是Retrofit的同步请求,这时候是需要指定事件消费的线程就行了即io线程
通过上述简单描述,可以看出Retrofit的设计非常强大。本篇文章中贴出的框架源码比较少,可能不能一下子看懂,可以自己实践操作,翻源码理解一下来加深印象。如果看一遍不懂,看两遍,反复循环,直到看懂为止。以前觉得熟练使用框架就行,现在觉得非常有必要阅读框架源码,理解别人的设计思想,学以致用,提高自己。