Android之全面解析Retrofit网络框架封装库
转载请标明出处:【顾林海的博客】
前言
Retrofit是Square公司推出的一个HTTP的框架,主要用于Android和Java,Retrofit会将每一个HTTP的API请求变成一个Java的接口,使用起来非常方便,同时Retrofit又是一个Restful HTTP的网络请求框架的封装。
介绍与使用
在上图中,我们会在应用层发起网络请求,同时请求会经过Retrofit这层,Retrofit会将我们的网络请求的参数进行封装,最后通过OkHttp层向服务端发起网络,服务端会将响应返回给OkHttp层,OkHttp层再将请求结果交给Retrofit层,最后再返回给应用层。
总结来说:也就是说App应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数,之后交由OkHttp完成后续的请求操作,在服务端返回数据后,OkHttp将原始数据的结果交给Retrofit,Reftrofit将根据用户的需求对结果进行解析。
接下来讲解Retrofit的用法,首先添加网络访问权限以及Retrofit库的依赖:
权限:
<uses-permission android:name="android.permission.INTERNET" />
Retrofit库的依赖:
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
创建服务端返回的数据类型:
public class MyResponse { String title; String imgUrl; int collectNum;}
创建一个用于描述网络请求的接口:
public interface NetworkInterface { @GET("news/newsDetail") Call<MyResponse> getNewsDetails(@QueryMap Map<String,String> map);}
Retrofit将每一个HTTP请求抽象成了Java接口类,同时采用注解方式来描述并配置网络的请求参数,内部通过动态代理将接口的注解转换成HTTP请求,上面通过注解@GET表示本次请求网络的方式是GET请求,括号内部定义了请求的部分URL ,getNewsDetail方法内的参数使用了@QueryMap注解,说明我们可以传入任何键值对集合Map。
接着创建Retrofit并进行网络请求:
private void initRetrofit() { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); NetworkInterface networkInterface = retrofit.create(NetworkInterface.class); Map<String, String> params = new HashMap<>(); params.put("newsId", "1"); params.put("token", "yud133f"); Call call = networkInterface.getNewsDetails(params); call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { System.out.println(response.body()); } @Override public void onFailure(Call call, Throwable t) { System.out.println("请求错误"); } }); }
通过Retrofit的Builder模式创建Retrofit对象,通过baseUrl来指定网络请求的URL,这个URL加上网络请求接口类中通过注解定义的URL,就凑成了完整的URL;通过addConverterFactory方法设置服务返回后数据的解析器,Retrofit可以指定任意的数据解析器,这里采用的是GSON数据解析器,需要在gradle中引入它的依赖库。
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
通过addCallAdapterFactory方法设置网络请求的适配器,Retrofit可以支持多种的网络请求适配器方式,比如Java8和RxJava,这里采用RxJava,通过gradle引入它的依赖。
implementation 'com.squareup.retrofit2:adapter-rxjava:2.4.0'
通过Retrofit的create方法创建网络请求接口实例networkInterface,接着调用网络请求接口实例中的getNewsDetails方法并传入相关的键值对集合。最后通过Call对象执行异步或同步请求,这里通过enqueue方法执行异步请求并传入Callback实现两个回调方法。
总结:
-
添加Retrofit库的依赖以及网络权限。
-
创建服务器返回的数据类型的类。
-
创建用于描述网络请求的接口。
-
通过建造者模式创建Retrofit实例,并设置URL、数据解析器、网络请求适配器等等。
-
通过Retrofit的create方法创建网络请求的实例。
-
调用网络请求实例中的方法获取Call并发送网络请求。
关于Retrofit的使用流程已经介绍完毕,下面大致讲讲Retrofit内部请求的一个流程,为后面源码分析做准备。
Retrofit不是网络请求框架,它只是对网络请求框架的封装,在Retrofit中首先创建Retrofit这个类,它是Retrofit框架统一的一个入口,通过Retrofit的内部类Builder对网络请求的参数进行配置,Builder内部的ServiceMethod对象非常非常的重要,它对应着我们写好的接口类中的一个方法,通过动态代理的方式将我们自己定义好的接口以及接口中的参数、方法统一转换成一个个的HTTP请求,ServiceMethod对应的就是我们接口类当中定义好的方法,通过ServiceMethod可以解析方法中的各种注解,最后生成Request对象。创建完ServiceMethod对象之后,就可以创建OkHttpCall请求,通过这个OkHttpCall可以进行同步请求或异步请求。
在ServiceMethod当中生成了三个非常重要的工厂类,分别是CallAdapter工厂、Converter工厂以及CallFactory。CallAdapter工厂用于生产CallAdatper,在Retrofit中默认会将我们的请求封装成OkHttp的Call对象, CallAdapter的作用就是将我们的OkHttp的Call转换成被不同平台调用的适配器。Converter工厂用于生产数据转换器Converter,使用Converter可以将返回的Response转换成我们能使用的Java对象。CallFactory是用于创建Call请求类的,在Retrofit中一个个HTTP请求被封装成Call类。
有了OkHttpCall以及ServiceMethod中的三个工厂,就可以将网络请求OkHttpCall通过CallAdapter适配不同平台,在通过Converter解析返回的数据Response,如果是异步请求在拿到Response后会通过callbackExecutor进行线程切换来传递数据。
静态代理与动态代理
代理设计模式主要分为静态代理与动态代理,代理模式的定义是为其他对象提供一种代理,用以控制对这个对象的访问。打个比方:你人在国内,但是想要购买国外的某件商品,这时候你可以找朋友帮你买,你朋友就是代理,至于你朋友怎么买你不关心,你只要拿到商品就行。
静态代理
看上面这张类图,静态代理需要三个角色,一个是抽象对象角色(AbstractObject),一个是目标对象角色(RealObject),最后是代理对象角色(ProxyObject),在抽象对象角色类中定义了目标对象和代理对象的共同接口,RealObject是代理对象所代理的目标对象,ProxyObject是代理对象角色,内部会持有目标对象角色的引用,这样的话可以通过代理对象执行目标对象内的方法,接下来看具体的代码实现。
抽象对象角色:
public abstract class AbstractObject { protected abstract void operation();}
目标对象角色:
public class RealObject extends AbstractObject { @Override protected void operation() { System.out.println("目标对象的方法"); }}
代理对象角色:
public class ProxyObject extends AbstractObject { private AbstractObject mRealObject; public ProxyObject(AbstractObject realObject){ this.mRealObject=realObject; } @Override protected void operation() { System.out.println("执行目标对象前的工作...."); this.mRealObject.operation(); System.out.println("执行目标对象后的工作...."); }}
在代理对象的operation方法中我们可以进行一些额外的操作,比如日志的统计或者业务逻辑处理等。在日常开发中对已有的代码做改进,这时可以使用一个代理类对原有的结果进行控制。
动态代理
在Retrofit中使用到了动态代理,使用动态代理,可以无侵入式的扩展代码,在不改动原有代码的情况下,增强一些方法或功能,它的定义是:在程序运行时创建的代理方式。
接下来通过一个实例来了解动态代理:
public interface Subject { void operation();}
创建了一个Subject接口,它是代理类和被代理类共同实现的一个接口。
public class RealObject implements Subject { @Override public void operation() { System.out.println("目标对象的方法"); }}
RealObject是被代理对象,内部operation方法执行被代理类原有的方法。
public class ProxyObject implements InvocationHandler { private Object mRealObject; public ProxyObject(Object realObject){ this.mRealObject=realObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("operation".equals(method.getName())){ System.out.println("哈哈,捕捉到operation方法,我要做点其他事情..."); return method.invoke(mRealObject,args); } return null; }}
ProxyObject就是代理类,mRealObject代表真实的对象,ProxyObject需要实现InvocationHandler接口,并实现invoke方法,方法内部第一个参数proxy表示被代理的真实对象,第二个参数method表示真实对象的方法,第三个参数args表示真实对象的方法的参数。
public class Test { public static void main(String[] args) { Subject subject=new RealObject(); Subject proxyObject= (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), new Class<?>[]{Subject.class}, new ProxyObject(subject)); proxyObject.operation(); }}
通过Proxy的newProxyInstance方法创建代理类,传入第一个参数是被代理类的ClassLoader,第二个参数是被代理实现的接口,第三个参数就是实现了InvocationHandler的对象。
执行结果:
哈哈,捕捉到operation方法,我要做点其他事情...目标对象的方法
在代理类ProxyObject的invoke方法中拦截到了operation方法,这时可以添加上我们需要的代码或功能。
网络请求流程以及相关参数
在使用Retrofit时,需要创建Retrofit的实例,定义一个网络请求接口并为接口中的方法添加注解,接着通过动态代理生成网络请求对象,在Retrofit中会去解析在网络请求接口中的注解,并配置网络请求参数,通过动态代理拦截生成网络请求对象。
内部通过网络请求适配器将网络请求对象进行平台适配,比如Android或Java8, 接着通过网络请求执行器发送网络请求,这个网络请求执行器就是Call,这个Call就是对OkHttp的一个封装,底层还是使用OkHttp来发送请求的,服务端返回的数据会经过数据转换器进行解析,使用的是GsonConverter来解析数据,最终转换成Java对象,接着通过回调执行器切换线程,最后客户端在主线程处理返回的结果。
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
创建Retrofit实例采用的是构建者模式,构建者模式可以将一个对象的复杂构建与表示进行分离,我们进入Retrofit的源码。
public final class Retrofit { 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的几个成员变量。
serviceMethodeCache是一个LinkedHashMap对象,它的key值是Method,对应的就是Http的请求方法,value是ServiceMethod,ServiceMethod是对网络请求接口内的方法的注解进行解析封装后的对象。
callFactory是网络请求工厂,在Retrofit内默认的网络请求工厂采用的就是OkHttpClient。
baseUrl就是网络请求的URL的基地址,baseUrl加上网络请求接口中通过注解定义的相对地址,就组成了一个完整的请求地址。
converterFactories是数据转换器工厂集合,内部存放的是数据转换器工厂,而数据转换器工厂是用于生产数据转换器,数据转换器的作用就是对请求网络之后的Response进行转换,通过GsonConverter转换成Java对象。
adapterFactories是网络请求适配器工厂集合,内部存放的是网络请求适配器工厂,网络适配器工厂是用生产网络适配器,网络请求适配器的作用就是将Call对象转换成其他类型,如果需要支持RxJava,就可以将Call对象转换成RxJava平台的Call。
callbackExecutor是一个线程池,用于执行回调的线程池。
validateEagerly是一个标志位,是否需要立即解析接口中的方法,具体后面进行讲解,这里先有个印象。
Retrofit的7个成员变量介绍完了,接着进入Retrofit的Builder类:
public final class Retrofit { ... public static final class Builder { private final Platform platform; private @Nullable okhttp3.Call.Factory callFactory; private HttpUrl baseUrl; private final List<Converter.Factory> converterFactories = new ArrayList<>(); private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(); private @Nullable Executor callbackExecutor; private boolean validateEagerly; ... } ...}
Builder类是Retrofit的静态内部类,成员变量platform表示Retrofit适配的平台,主要包括Android和Java 8,默认情况下使用的是Android平台;callFactory表示请求网络的OkHttp的工厂,默认情况下使用OkHttp来完成请求;baseUrl表示网络请求的URL,通过Builder的baseUrl方法传入一个String类型的url,再将String类型的url转换成HttpUrl;converterFactories是数据转换器工厂的集合;callAdapterFactories是网络请求适配器工厂的集合;callbackExecutor用执行异步回调;validateEagerly是一个标志位,表示是否需要立即解析接口中的方法,在使用动态代理时对网络请求接口中方法的注解进行解析时会用到这个标志位。
public final class Retrofit { ... public static final class Builder { public Builder() { this(Platform.get()); } ... } ...}
在Builder的构造函数中通过this传入一个Platform类型,也就是适配的平台,进入Platform的get方法:
private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } return new Platform(); }
Platform是一个单例类,通过findPlatform方法来初始化实例,在静态方法get中返回了PLATFORM静态变量,而PLATFORM静态变量的初始化是在findPlatform方法中完成的,在findPlatform方法中先是通过反射加载"android.os.Build"类,接着返回一个Android平台的Platform对象,我们看一下这个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); } } }
在defaultCallbackExecutor中返回一个MainThreadExecutor对象,表示默认的回调方法执行器,这个执行器的作用是从子线程切换到主线程,看下面MainThreadExecutor实现了Executor,是一个线程池,并且内部在创建Handler时传入的是主线程的Looper,通过Looper.getMainLooper()获取主线程Looper,也就是说这个Executor和主线程已经绑定了。继续看Android类中的defaultCallAdapterFactory方法,它表示创建一个默认的网络请求适配器工厂的执行器,在Call对象执行异步请求时,需要一个执行器将异步请求后的数据传递到主线程,defaultCallAdapterFactory放内传入的就是异步请求时进行数据传递所需要的执行器。
在Builder的无参构造方法内调用了有参构造方法:
Builder(Platform platform) { this.platform = platform; }
将创建好的Android平台Platform对象赋值给Builder类的成员变量platform。
Builder的相关成员变量的初始化已经介绍完毕,接着看看它的build方法:
public Retrofit build() { ... //创建OkHttpClient的工厂 okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } //创建主线程执行器 Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } //添加网络请求适配器工厂 List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); //添加数据转换器工厂 List<Converter.Factory> converterFactories = new ArrayList<>(1 + this.converterFactories.size()); converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); //创建Retrofit实例 return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }
build方法中的注释已经很详细了,判断callFactory是否为空,如果为空,默认创建OkHttpClient,这也说明了Retrofit内部默认是使用OkHttp来进行网络请求的;callbackExecutor默认通过platform的defaultCallbackExecutor方法获取执行器,这里的platform默认是Android平台,在前面讲解Android平台类时,就已经知道了它的defaultCallbackExecutor方法返回的是主线程的执行器,最终数据就是通过主线程的执行器发送到主线程的。
创建完主线程的执行器后,添加网络请求适配器工厂,这里添加了Android平台的defaultCallAdapterFactory方法传入主线程的执行器,这样在进行异步请求后可以通过主线程的执行器进行数据传递。
最后就是添加数据转换器工厂,默认添加了一个BuiltInConverters对象,BuiltInConverters表示的是内置的默认的数据转换器工厂。
Retrofit 静态内部类Builder的三个方法
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
通过Builder的baseUrl方法来设置http的基地址,先进入baseUrl方法。
public Builder baseUrl(String baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); HttpUrl httpUrl = HttpUrl.parse(baseUrl); if (httpUrl == null) { throw new IllegalArgumentException("Illegal URL: " + baseUrl); } return baseUrl(httpUrl); }
在baseUrl方法中,将传入的String类型的baseUrl通过HttpUrl的parse方法转换成HttpUrl对象,将转换后的httpUrl实例传入baseUrl方法,注意这里传入baseUrl方法的是HttpUrl对象,我们继续看baseUrl(HttpUrl)方法。
public Builder baseUrl(HttpUrl baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); List<String> pathSegments = baseUrl.pathSegments(); if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl); } this.baseUrl = baseUrl; return this; }
通过checkNotNull方法判断HttpUrl对象是否为空,接着通过HttpUrl的pathSegments()方法将url拆分成多个独立的碎片,为了方便比较,将创建Retrofit实例贴出来:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/")
通过baseUrl方法设置http的url时,在最后是以’/‘反斜杠结尾的,下面的if语句中判断拆分后的最后字符串是否为空,拆分后的数组最后一个为空,说明http的url是以’/‘结尾,反之http的url不是以’/'结尾,就会抛出异常,最后将baseUrl赋值给Builder的成员变量baseUrl。
介绍完baseUrl方法,继续看下一个方法addConverterFactory方法:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
addConverterFactory方法是用于设置数据解析器,进入addConverterFactory方法看看到底做了哪些操作。
public Builder addConverterFactory(Converter.Factory factory) { converterFactories.add(checkNotNull(factory, "factory == null")); return this; }
addConverterFactory方法所做的工作很简单,就是将factory添加到数据解析器工厂的集合中。回到前面addConverterFactory方法,看到传入的是GsonConverterFactory对象,而GsonConverterFactory对象是通过GsonConverterFactory的get()方法创建的,点进去看下。
public static GsonConverterFactory create() { return create(new Gson()); }
create方法内部先是创建了Gson对象,这个Gson就是goole提供的Gson,用于解析json数据用的,创建完Gson对象后调用create方法并传入刚创建后的Gson对象。
public static GsonConverterFactory create(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); return new GsonConverterFactory(gson); }
方法很简单,创建一个GsonConverterFactory对象并返回,我们进入GsonConverterFactory的构造函数中。
private final Gson gson; private GsonConverterFactory(Gson gson) { this.gson = gson; }
GsonConverterFactory的构造函数只做了赋值操作,将创建好的Gson对象赋值给GsonConverterFactory的成员变量gson。
介绍完addConverterFactory方法后,接着看addCallAdapterFactory方法:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
addCallAdapterFactory方法用于设置适配的平台,这里使用的是RxJava平台,我们看下addCallAdapterFactory的具体操作。
public Builder addCallAdapterFactory(CallAdapter.Factory factory) { callAdapterFactories.add(checkNotNull(factory, "factory == null")); return this; }
代码还是很简单,就是将factory添加到适配器工厂的集合中去。回到前面,看看addCallAdapterFactory方法传入的这个Factory,是通过RxJavaCallAdapterFactory工厂类的create()方法来创建的。
public static RxJavaCallAdapterFactory create() { return new RxJavaCallAdapterFactory(null, false); }
create方法内部直接通过new关键字创建了RxJavaCallAdapterFactory对象。
public final class RxJavaCallAdapterFactory extends CallAdapter.Factory { ...}
RxJavaCallAdapterFactory继承了CallAdapter的内部类Factory。
先看CallAdapter的作用,CallAdapter作用就是通过Retrofit中Call转换成Java对象,Retrofit中的Call对象和OkHttp中的Call对象是不一样的,Retrofit中的Call是对OkHttp中的Call进行了封装,也就是说通过Retrofit来进行网络请求,最终都是通过OkHttp来进行请求的。在转换Java对象前,需要先创建Retrofit中的Call对象,然后通过Call对象发送http请求,服务器会返回响应的数据,这个时候通过converter数据转换器,将服务器返回的Response转换成我们需要的Java对象。
public interface CallAdapter<R, T> { Type responseType(); T adapt(Call<R> call);}
在CallAdapter接口中定义了一个responseType()方法并返回Type类型,这个方法的作用就是返回解析后的类型。看下面网络请求接口:
public interface NetworkInterface { @GET("news/newsDetail") Call<MyResponse> getNewsDetails(@QueryMap Map<String,String> map);}
CallAdapter接口中的responseType方法返回的就是MyResponse这个类型的对象。
public interface CallAdapter<R, T> { Type responseType(); T adapt(Call<R> call);}
继续看第二个方法adapt,这里的泛型T是指需要转换接口的返回类型,adapt方法传入一个Call对象,这个Call对象就是OkHttp的Call对象,如果对应的是RxJava的话,这里的T对应的就是RxJava当中的类型。
继续看CallAdapter内部类Factory:
public interface CallAdapter<R, T> { ... abstract class Factory { public abstract @Nullable retrofit2.CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); ... protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); } }}
get方法的作用是根据接口的返回类型以及注解类型来得到实际需要的CallAdapter;getRawType方法返回的是原始的类型。
RxJavaCallAdapterFactory实现Factory抽象类,用来提供具体的适配逻辑,回到RxJavaCallAdapterFactory,先看get方法的实现:
@Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<?> rawType = getRawType(returnType); ... return new RxJavaCallAdapter(responseType, scheduler, isAsync, isResult, isBody, isSingle, false); }
在get方法中,先通过getRawType拿到原始数据类型,通过这个原始数据类型进行各种设置,最后创建RxJavaCallAdapter对象并返回。创建完RxJavaCallAdapter对象后,最终调用adapt方法将我们的Call请求转换成每一个平台所适用的类型。adapt方法在接口CallAdapter中定义的,在源码中找到RxJavaCallAdapter实现了CallAdapter接口,我们看看RxJavaCallAdapter中的adapt实现。
@Override public Object adapt(Call<R> call) { Observable.OnSubscribe<Response<R>> callFunc = isAsync ? new CallEnqueueOnSubscribe<>(call) : new CallExecuteOnSubscribe<>(call); Observable.OnSubscribe<?> func; if (isResult) { func = new ResultOnSubscribe<>(callFunc); } else if (isBody) { func = new BodyOnSubscribe<>(callFunc); } else { func = callFunc; } Observable<?> observable = Observable.create(func); if (scheduler != null) { observable = observable.subscribeOn(scheduler); } if (isSingle) { return observable.toSingle(); } if (isCompletable) { return observable.toCompletable(); } return observable; }
使用过RxJava的同学应该对上面的代码非常熟悉了,将我们传入的OkHttp的Call对象设置给OnSubscribe对象,接着创建Observable被观察者的实例,并将OnSubscribe与被观察者绑定,最后判断scheduler调度器是否为空,如果不为空,就调用observable的subscribeOn方法在指定的调度器执行操作。关于RxJava的相关知识后面会另开文章进行讲解,这里大家先有个印象,知道整体的流程即可。
到这里baseUrl、addConverterFactory以及addCallAdapterFactory方法就介绍完毕。
网络请求接口
public interface NetworkInterface { @GET("news/newsDetail") Call<MyResponse> getNewsDetails(@QueryMap Map<String,String> map);}
进行网络请求前需要创建网络请求接口类,在上面定义为NetworkInterface接口,其中定义了一个接受网络请求数据的方法叫做getNewsDetail,返回类型是一个泛型Call,Call里面的类型是MyResponse,MyResponse就是我们请求网络返回的数据类型,在getNewsDetail方法上添加了注解GET,表示网络请求是用GET方式来请求的,在注解GET中定义相对url地址,在Retrofit中,网络请求的URL分为两部分,第一部分在创建Retrofit时通过baseUrl传入http请求的基地址,第二部分就是上面注解GET后面定义的http请求的相对地址,两者加起来就是完整的http请求地址。
private void initRetrofit() { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); NetworkInterface networkInterface = retrofit.create(NetworkInterface.class);}
创建完网络请求接口后就可以通过Retrofit的create方法,把网络请求接口的Class对象传递进去,返回一个网络请求接口的代理类。
进入create方法:
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } ...}
通过工具类Utils的validateServiceInterface方法对接口的字节码进行验证,下面判断validateEagerly标志位,表示是否提前解析网络请求接口中的方法,如果validateEagerly为true,就会执行eagerlyValidateMethods方法。
进入eagerlyValidateMethods方法:
private void eagerlyValidateMethods(Class<?> service) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method)) { loadServiceMethod(method); } } }
在该方法先是获取具体的平台Platform,往下看是一个for循环,通过反射获取网络请求接口中的方法并遍历,在遍历时判断isDefaultMethod方法。
boolean isDefaultMethod(Method method) { return false; }
发现isDefaultMethod方法固定返回false,那么上面的if判断语句中就会执行loadServiceMethod方法。
进入loadServiceMethod方法:
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>(); ... }
loadServiceMethod方法返回一个ServiceMethod对象,在Retrofit中通过动态代理的方式将网络请求接口中的方法转换成一个个http请求,这个ServiceMethod对象就是对应接口中的一个方法,对接口中的方法进行封装。
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>(); ServiceMethod<?, ?> loadServiceMethod(Method method) { ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; ... }
在loadServiceMethod方法中一开始从serviceMethodCache集合获取请求方法对应的ServiceMethod对象,serviceMethodCache是ConcurrentHashMap集合类型,是线程安全的Map集合,如果serviceMethodCache当中存在对应请求方法的ServiceMethod对象,就直接返回该对象。
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>(); ServiceMethod<?, ?> loadServiceMethod(Method method) { ... synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; }
如果serviceMethodCache这个缓存集合中没有对应方法的ServiceMethod对象,那么就会利用Builder模式重新创建ServiceMethod对象,并将这个ServiceMethod对象保存在serviceMethodCache这个缓存集合中。
继续回到Retrofit的create方法中:
public <T> T create(final Class<T> 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 { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall); } }); }
可以看到create方法返回的是网络请求接口的动态代理对象,通过Proxy的newProxyInstance方法创建动态代理对象,当我们调用代理类方法时,就会执行InvocationHandler的invoke方法。
整个Retrofit的核心就是InvocationHandler的invoke方法中的三行代码,后面一节会对这三个核心代码进行讲解。
ServiceMethod相关参数以及创建过程
public <T> T create(final Class<T> 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 { //标记1 ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall); } }); }
在标记1处调用了loadServiceMethod方法,这个方法在上一小节已经介绍过,从缓存集合serviceMethodCache获取对应方法的ServiceMethod,缓存集合中存在就直接返回,否则创建ServiceMethod对象,这个ServiceMethod对象对应的是网络请求接口中的一个方法相关参数的封装。
进入ServiceMethod类:
final class ServiceMethod<R, T> { ... 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 ParameterHandler<?>[] parameterHandlers; ...}
这个ServiceMethod是个泛型类,内部定义了很多成员变量,callFactory是我们的网络请求工厂,用于生产网络请求的Call对象,这个Call在Retrofit中是对OkHttp Call的封装;callAdapter是网络请求适配器,主要将网络请求适用于不同的平台,比如RxJava;baseUrl是网络基地址;responseConverter是数据转换器,将服务器返回的数据,比如json格式或者是xml格式等等,转换成Java对象;httpMethod是网络请求的方法,比如GET、POST等等;relativeUrl是网络请求的相对地址,比如网络请求接口中注解GET后的http请求地址;headers表示Http的请求头;contentType表示网络请求的body类型;parameterHandlers是方法参数的处理器。
ServiceMethod的实例是通过Builder来创建,进入ServiceMethod的Builder类:
Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); this.parameterTypes = method.getGenericParameterTypes(); this.parameterAnnotationsArray = method.getParameterAnnotations(); }
Builder构造函数中对成员变量进行赋值,传入retrofit和method分别赋值给Builder的成员变量retrofit和method,method表示网络请求方式,比如GET、POST等等,接着获取方法当中的注解赋值给methodAnnotations,parameterTypes获取的是网络请求接口中方法参数的类型,parameterAnnotationsArray获取的是网络请求接口方法中注解的内容。
Builder的相关初始化完毕后,通过build方法创建ServiceMethod对象:
public ServiceMethod build() { callAdapter = createCallAdapter(); ... }
在build方法中通过createCallAdapter方法创建CallAdapter对象,createCallAdapter方法根据网络请求接口方法返回值和它的注解类型,从Retrofit中获取对应网络请求适配器。
createCallAdapter方法:
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); } if (returnType == void.class) { throw methodError("Service methods cannot return void."); } //标记1 Annotation[] annotations = method.getAnnotations(); try { //noinspection unchecked 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); } }
通过method.getGenericReturnType()获取网络请求接口方法返回的类型,之后在标记2处,通过method.getAnnotations()获取网络请求接口方法中的注解,最后调用retrofit的callAdater方法返回CallAdapter对象,传入的就是前面获取的网络请求接口方法的返回类型和方法内的注解类型。
进入Retrofit的callAdapter方法:
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); }
继续进入nextCallAdapter方法:
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { checkNotNull(returnType, "returnType == null"); checkNotNull(annotations, "annotations == null"); int start = callAdapterFactories.indexOf(skipPast) + 1; for (int i = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } ... throw new IllegalArgumentException(builder.toString()); }
方法内前两行做非空判断,接着通过for循环遍历callAdapterFactories网络请求适配器工厂集合,看有没有合适的网络请求适配器工厂,如果没有抛出异常。
回到ServiceMethod的Builder类的build方法:
public ServiceMethod build() { callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); ... }
根据前面获取到的网络请求适配器之后,通过responseType方法获取网络适配器返回的数据类型。
public ServiceMethod build() { callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); ... responseConverter = createResponseConverter(); ... }
接着通过createResponseConverter方法获取数据解析器。
进入createResponseConverter方法:
private Converter<ResponseBody, T> createResponseConverter() { Annotation[] annotations = method.getAnnotations(); try { return retrofit.responseBodyConverter(responseType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(e, "Unable to create converter for %s", responseType); } }
通过method的getAnnotations方法获取网络请求接口中方法的注解,接着通过retrofit的responseBodyConverter方法获取Converter对象。
responseBodyConverter方法:
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) { return nextResponseBodyConverter(null, type, annotations); }
继续点进nextResponseBodyConverter方法:
public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { ... int start = converterFactories.indexOf(skipPast) + 1; for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { return (Converter<ResponseBody, T>) converter; } } ... throw new IllegalArgumentException(builder.toString()); }
nextResponseBodyConverter方法内部遍历数据转换器工厂集合,从中获取合适的数据转换器,这里默认获取的是Gson的数据转换器。
回到ServiceMethod的Builder类的build方法:
public ServiceMethod build() { callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); ... responseConverter = createResponseConverter(); for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } ... }
拿到数据转换器后,通过遍历网络请求接口方法的注解,parseMethodAnnotation方法主要是对网络请求接口方法上面的注解类型进行判断,同时根据注解类型对Builder的相关成员变量进行赋值。
回到ServiceMethod的Builder类的build方法:
public ServiceMethod build() { callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); ... responseConverter = createResponseConverter(); for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } ... //标记1 int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { ... Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; ... parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } ... return new ServiceMethod<>(this); }
在标记1处,获取方法内注解的长度,从而创建参数解析器数组,接着遍历网络请求接口方法内的注解,并通过parseParameter方法来解析参数,最后通过new创建ServiceMethod对象并将配置好参数的Builder对象传递过去。
最后总结下这个build方法,主要根据网络请求接口内方法的返回值类型和方法中的注解,来从网络请求适配器工厂和数据转换器工厂,分别获取我们需要的网络请求适配器和数据转换器,然后根据参数上的注解获取到网络请求所需要的相关参数。
同步与异步请求
public <T> T create(final Class<T> 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 { ... //核心代码1 ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); //核心代码2 OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); //核心代码3 return serviceMethod.adapt(okHttpCall); } }); }
在核心代码2处创建OkHttpCall,OkHttpCall是Call的实现类,在Retrofit中内部是通过OkHttp来进行网络的请求,这个OkHttpCall就是对OkHttp请求的封装。
final class OkHttpCall<T> implements Call<T> { .... private @Nullable okhttp3.Call rawCall; OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) { this.serviceMethod = serviceMethod; this.args = args; }}
在OkHttpCall中可以看到rawCall,它是OkHttp的Call,这也验证之前所说的内部会通过OkHttp来实现网络请求,OkHttpCall构造函数传入两个参数,serviceMethod对象和args网络请求参数,接着看核心代码3。
return serviceMethod.adapt(okHttpCall);
serviceMethod的adapt方法中会调用callAdatper的adapter方法,通过适配器的adapt方法来将OkHttpCall转换成其他平台使用的对象,这个callAdapter是在创建serviceMethod时通过构建者模式创建的,它代表网络请求的适配器,这里使用的RxJava平台。
回到一开始的实例代码:
private void initRetrofit() { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://icould.glh/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); NetworkInterface networkInterface = retrofit.create(NetworkInterface.class); Map<String, String> params = new HashMap<>(); params.put("newsId", "1"); params.put("token", "yud133f"); Call call = networkInterface.getNewsDetails(params); call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { System.out.println(response.body()); } @Override public void onFailure(Call call, Throwable t) { System.out.println("请求错误"); } }); }
通过networkInterface接口调用getNewsDetails是不行的,因此在Retrofit的create获取网络接口的动态代理,在执行networkInterface的getNewDetails方法时,通过动态代理拦截,并执行动态代理对象内InvocationHandler中的invoke方法,将OkHttpCall转换成RxJava平台的适用的Call,而这个OkHttpCall对象是对OkHttp网络库的封装,最后返回OkHttpCall类型的Call对象,有了这个Call对象就可以进行同步或异步请求,OkHttpCall内提供了同步请求方法execute和异步请求方法enqueue,接着下来重点分析这两个方法。
在Retrofit同步请求流程中,首先需要对网络请求接口中方法以及参数进行解析,通过ParameterHandler进行解析,然后根据ServiceMethod对象创建OkHttp的Request对象,ServiceMethod对象内部包含了网络请求的所有信息,它是对网络接口方法的封装,有了Request对象后就可以通过OkHttp这个库来进行网络请求,最后解析服务端给客户端返回的数据,通过converter数据转换器来完成数据的转换。
OkHttpCall的同步请求execute方法:
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } 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()); }
下面贴出execute局部代码,方便分析。
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } ... } ... }
上面代码中一开始创建了一个OkHttp的call对象,下面是一个同步代码块,通过判断executed是否执行过通过请求,如果执行过就会抛出异常,接着判断creationFailure,不为null时,判断异常类型并抛出异常,execute方法的前段部分就是对异常的判断。
@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; } } } ... }
当没有任何异常时,将rawCall也就是OkHttp的原生call赋值给局部变量call,当call为null时,通过createRawCall方法创建OkHttp的Call对象以及Request。
进入createRawCall方法:
private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = serviceMethod.toCall(args); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
内部通过serviceMethod的toCall方法将传入的请求参数转换成Call对象。
进入serviceMethod的toCall方法:
okhttp3.Call toCall(@Nullable Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; ... for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } return callFactory.newCall(requestBuilder.build()); }
RequestBuilder内部保存着网络请求的相关参数,接着在for循环中通过ParameterHandler对参数进行解析,最后通过callFactory的newCall创建OkHttp的Call对象,newCall内部传入的是Request对象,通过requestBuilder.build()创建Request对象,到这里将OkHttp的Call对象返回给execute方法内部的成员变量call以及OkHttpCall的成员变量rawCall。
@Override public Response<T> execute() throws IOException { okhttp3.Call call; ... if (canceled) { call.cancel(); } return parseResponse(call.execute()); }
有了OkHttp的Call之后,就通过call.execute()进行阻塞式的同步请求,并将返回的Response传入parseResponse方法中。
进入parseResponse方法:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ... try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { ... } }
只看核心代码,通过调用serviceMethod的toResponse方法返回body,进入toResponse方法,看它到底做了哪些操作。
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
原来是调用了数据转换器将OkHttp返回的Response转换成Java对象,这里我们使用的Gson,也就是通过Gson将服务器返回的数据转换成我们需要的Java对象,最后通过Response的success方法将返回的Java对象封装成Response。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ... return Response.success(body, rawResponse);}
进入Response的success方法:
public static <T> Response<T> success(@Nullable T body, okhttp3.Response rawResponse) { ... return new Response<>(rawResponse, body, null); }
返回创建好的Response,将body也就是我们的Java对象传过去。
Response的构造函数:
private Response(okhttp3.Response rawResponse, @Nullable T body, @Nullable ResponseBody errorBody) { this.rawResponse = rawResponse; this.body = body; this.errorBody = errorBody; }
到这里大家应该很熟悉了,我们利用Retrofit进行网络的同步或异步请求,最终会返回一个Response对象并通过response.body来获取结果,这个body就是通过转换器转换好的Java对象。
接下来分析异步请求:
call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { System.out.println(response.body()); } @Override public void onFailure(Call call, Throwable t) { System.out.println("请求错误"); } });
执行异步请求的流程和同步类似,只不过异步请求的结果是通过回调来传递的,异步是通过enqueue方法来执行的,而这个Call的实现类是OkHttpCall,进入OkHttpCall的enqueue方法。
OkHttpCall的enqueue方法(上半部分):
@Override public void enqueue(final Callback<T> callback) { ... okhttp3.Call call; ... synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } ... }
可以发现enqueue的上半部分与上面介绍同步请求时是一样的,创建OkHttp的Call,并检查相关异常,如果call为null,就通过createRawCall方法创建OkHttp的Call以及请求所需要的Request。
OkHttpCall的enqueue方法(下半部分):
@Override public void enqueue(final Callback<T> callback) { ... call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response<T> response; try { response = parseResponse(rawResponse); } catch (Throwable e) { callFailure(e); return; } try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } ... }); }
上面这段代码不用我讲,大家也应该知道就是通过OkHttp的Call的enqueue方法进行异步请求,关于OkHttp相关知识可以阅读之前写的OkHttp分析的相关系列教程,在OkHttp的Call的enqueue方法的回调方法onResponse方法中,将返回的Response通过parseResponse方法转换成Java对象并返回Retrofit的Response对象,通过前面传入的Callback对象将Response回调给客户端。
到这里关于Retrofit网络请求框架的封装就讲解完毕了!
更多相关文章
- Nginx系列教程(六)| 手把手教你搭建 LNMP 架构并部署天空网络电影
- Android:回调函数
- Android定时的基本实现
- 多媒体之播放音乐——MediaPlayer
- android中常见的设计模式有哪些?
- 关于Android(安卓)Wifi NetworkId的一些理解。
- android使用AIDL实现跨进程通讯(IPC)
- Android用SQLite存储数据详解
- Android(安卓)截图功能源码的分析