技术标签: android
目前打算选用Retrofit2+RxJava2作为网络请求框架,下面从源码角度分析下这两个框架是怎么粘合在一起的。
看完本文,你将会看到:
- 动态代理模式在Retrofit中的运用。
- 抽象工厂模式在Retrofit中的运用。
- RxJava2如何和Retrofit结合实现网络请求模块。
//https://api.douban.com/v2/book/1220562
public interface IDoubanService {
@GET("/v2/{book}/{bookId}")
Call<Book> getBookInfo(@Path("book") String book, @Path("bookId") String bookId);
@GET("/v2/{book}/{bookId}")
Observable<Book> getBookInfoObservable(@Path("book") String book, @Path("bookId") String bookId);
}
接口中的方法就是我们实际要执行的网络请求,方法的返回值就是CallAdapter的返回值,如果没有的话,默认返回Call对象。
如果我们自定义了一个返回类型,并且没有加上对应的CallAdapterFactory的话,那么抛出异常,如下:
Caused by: java.lang.IllegalArgumentException: Unable to create call adapter for class java.lang.String
final String bookId = "1220562";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/")
// 将Response通过GSON转成POJO
.addConverterFactory(GsonConverterFactory.create())
// 将接口方法根据类型返回
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(new OkHttpClient())
.build();
// 使用Retrofit创建接口的代理对象
IDoubanService service = retrofit.create(IDoubanService.class);
Call<Book> call = service.getBookInfo("book", bookId);
// 使用Call对象获取网络请求结果
Response<Book> response = call.execute();
Book book = response.body();
上面的示例中使用的Call进行获取的网络请求,实际上我们可以通过CallAdapterFactory这种抽象工厂来创建我们自己的返回类型,稍后详细分析。
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);
}
// Platform这个类用来区分平台,如果是Java8的话,接口中会有default方法
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 将这个反射的方法封装成ServiceMethod
// ServiceMethod会将接口中的注解进行解析
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
// 创建真实的Call对象
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
private CallAdapter<T, R> createCallAdapter() {
// 获取接口方法的返回值
Type returnType = method.getGenericReturnType();
// 检查返回类型
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
// 返回值不能是void
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
// 最终会从Retrofit的CallAdapter中进行查找,还记得之前的addCallAdapterFactroy()抽象工厂么?
return (CallAdapter<T, R>) 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#nextCallAdapter(),在这个方法中,会进行遍历,将Annotion和returnType传入,直到有一个Factroy能够返回非空的CallAdapter
/**
* Returns a call adapter for interface methods that return {@code returnType}, or null if it
* cannot be handled by this factory.
*/
// 这个方法需要返回非空的CallAdapter
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
核心的get()
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
if (rawType == Completable.class) {
// Completable is not parameterized (which is what the rest of this method deals with) so it
// can only be created with a single configuration.
return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,
false, true);
}
boolean isFlowable = rawType == Flowable.class;
boolean isSingle = rawType == Single.class;
boolean isMaybe = rawType == Maybe.class;
if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
return null;
}
boolean isResult = false;
boolean isBody = false;
Type responseType;
if (!(returnType instanceof ParameterizedType)) {
String name = isFlowable ? "Flowable"
: isSingle ? "Single"
: isMaybe ? "Maybe" : "Observable";
throw new IllegalStateException(name + " return type must be parameterized"
+ " as " + name + "<Foo> or " + name + "<? extends Foo>");
}
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
Class<?> rawObservableType = getRawType(observableType);
if (rawObservableType == Response.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException("Response must be parameterized"
+ " as Response<Foo> or Response<? extends Foo>");
}
responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
} else if (rawObservableType == Result.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException("Result must be parameterized"
+ " as Result<Foo> or Result<? extends Foo>");
}
responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
isResult = true;
} else {
responseType = observableType;
isBody = true;
}
// 一般情况返回一个 RxJava2CallAdapter
return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
isSingle, isMaybe, false);
}
CallAdapter中有一个核心方法是adapt(),作用是将Call对象转化成任意的类型
/**
* Returns an instance of {
@code T} which delegates to {
@code call}.
* <p>
* For example, given an instance for a hypothetical utility, {
@code Async}, this instance would
* return a new {
@code Async<R>} which invoked {
@code call} when run.
* <pre><code>
* @Override
* public <R> Async<R> adapt(final Call<R> call) {
* return Async.create(new Callable<Response<R>>() {
* @Override
* public Response<R> call() throws Exception {
* return call.execute();
* }
* });
* }
* </code></pre>
*/
T adapt(Call<R> call);
RxJava2CallAdapter中的实现
// 此处就将Call对象转换成Observable了
@Override public Object adapt(Call<R> call) {
// 先创建一个Observable对象,根据是否是异步,默认情况下不是异步
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;
}
RxJava接受网络请求的过程如下
observable
// 网络请求在IO线程执行
.subscribeOn(Schedulers.io())
// 回调到主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe((book) -> {
mTvText.setText(String.valueOf(book));
});
Retrofit+Factory将网络请求抽象成了一个Observable对象,那么我们可以通过RxJava这样的方法操作网络请求了。
Retrofit返回Observable的实现是CallExecuteObservable或者CallEnqueueObservable,我们看下这个Observable的实现。
final class CallExecuteObservable<T> extends Observable<Response<T>> {
private final Call<T> originalCall;
CallExecuteObservable(Call<T> originalCall) {
this.originalCall = originalCall;
}
// 如果在Observeable中进行订阅操作,那么实际会调用到这个方法
@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 {
// 会调用Call执行网络请求,获取Response对象
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();
}
}
}
文章浏览阅读884次,点赞25次,收藏20次。在Java中,全局变量和局部变量的概念通常与类变量(有时被认为是全局变量)和方法内的变量(局部变量)相关联。虽然Java本身没有全局变量的概念,但类的静态变量经常被用作全局变量。
文章浏览阅读1.1k次。配置过程分析:1: . ./build/envsetup.shincluding device/samsung/smdkv210/vendorsetup.sh------------------------------------------------------------------build/envsetup.sh末尾有:# Execute the contents o_export build_target 還原
文章浏览阅读450次。第四章 插入、更新与删除 4.1插入新纪录4.2插入默认值_insert into default
文章浏览阅读594次。ESD9B3.3ST5G是一款 双向ESD保护 TVS二极管,设计用于保护电压敏感型来自ESD的组件。良好的夹紧能力,低泄漏,而且,快速响应时间可为设计提供一流的保护:暴露在静电放电下。反应速度快,电容值低,体积小,集成度高,封装多样化,漏电流低,电压值低有助于保护敏感的电子电路。ESD9B3.3ST5G ESD静电保护二极管应用于手机和配件、便携式电子产品、工业控制设备、机顶盒、电子仪器仪表、服务器,笔记本电脑和台式机、显示端口等。
文章浏览阅读807次,点赞2次,收藏2次。宁波中软国际实习日记第一天:搭建开发环境1.0 JDK安装2.0 IDEA安装3.0 Tomcat安装、部署4.0 Maven安装、部署5.0 MySQL安装6.0 Notepad++安装1.0 JDK安装实习所用JDK版本是JDK8,在官网的下载页面找到Java SE 8u151/ 8u152的JDK download 按钮。点进去。双击安装程序后,一直点next就行。接下来是环境变..._中软国际实习日记
文章浏览阅读231次。要修改针对首页中的POST请求的测试。希望视图把新添加的待办事项存入数据库,而不是直接传给响应。为了测试这个操作,要在现有的测试方法test_can_save_a_post_request中添加3行新代码# lists/tests.pydeftest_can_save_a_post_request(self):response= self.client.post(‘/‘, data={‘item_..._django http post mysql
文章浏览阅读1.5k次,点赞2次,收藏7次。vertical-align的可选值为:1. bottom: 图片的底线和文字的底线对齐,2. baseline:默认,图片的底线和文字的基线对齐,3. middle: 图片的中线和文字的中线对齐,4. top:图片的顶线和文字的顶线对齐。不同浏览器对有些标签的默认值是不同的,为了消除不同浏览器对HTML文本呈现的差异,所以需要进行CSS初始化。当我们选择input输入框,进行文字输入的时候,边框会改变颜色。textarea默认可以在右下角进行拖拽,改变输入框的大小。CSS初始化参考如下。_html css input::cue
文章浏览阅读84次。一、疑问二、知识点1. 白化 白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对数值范围进行归一化。该变换的几何解释是:如果数据服从多变量的高斯分布,那么经过白化后,数据的分布将会是一个均值为零,且协方差相等的矩阵。该操作的代码如下:# 对数据进行白化操作:# 除以特征值 Xwhite = Xrot / np.sqrt(S + 1e-5) 警告:夸大的噪声。注意分母..._人工神经网络系统中的静态数据
文章浏览阅读81次。MyEclipse 在线订购年终抄底促销!火爆开抢>>MyEclipse最新版下载使用REST Web Services来管理JPA实体。在逆向工程数据库表后生成REST Web服务,下面的示例创建用于管理博客条目的简单Web服务。你将学会:利用数据库逆向工程开发REST Web服务部署到Tomcat服务器使用REST Web服务资源管理器进行测试没有MyEcli..._myeclipse项目中不能选add rest web service compatibility
文章浏览阅读854次,点赞10次,收藏17次。校验的内容就是此对象是否重写了 finalize() 方法,如果该对象重写了 finalize() 方法,那么这个对象将会被存入到 F-Queue 队列中,等待 JVM 的 Finalizer 线程去执行重写的 finalize() 方法,在这个方法中如果此对象将自己赋值给某个类变量时,则表示此对象已经被引用了。它是指将内存分为大小相同的两块区域,每次只使用其中的一块区域,这样在进行垃圾回收时就可以直接将存活的东西复制到新的内存上,然后再把另一块内存全部清理掉。// 等待 finalize() 执行。
文章浏览阅读246次。LeetCode 1427. 字符串的左右移文章目录LeetCode 1427. 字符串的左右移题目描述一、解题关键词二、解题报告1.思路分析2.时间复杂度3.代码示例2.知识点总结相同题目题目描述给定一个包含小写英文字母的字符串 s 以及一个矩阵 shift,其中 shift[i] = [direction, amount]: direction 可以为 0 (表示左移)或 1 (表示右移)。 amount 表示 s 左右移的位数。 左移 1 位表示移除 s 的第一个字符,并_leetcode 1427 python
文章浏览阅读2k次。在北半球,3月是春季的第一个月,春天象征着希望和美好。关注我的读者大多数都是(程序)猿,所以好用的键盘必不可少!今天为了感谢大家对本公众号的大力支持我联合了10个号主送11个炫酷键盘,不..._cole mak键盘