Retrofit的使用与深入学习(上)
来源:互联网 发布:免费学编程的中文网站 编辑:程序博客网 时间:2024/05/18 03:39
注意:以下分析都是基于Retrofit2
转载请注明出处:http://blog.csdn.net/evan_man/article/details/51320408
简单介绍
Retrofit发送网络请求通过okHttp,okhttp的诸多好处与内部实现机制,已经在之前的博客《OkHttp深入学习(一)——初探》进行了介绍。相对于直接使用okhttp的好处在于,它支持对Response接收数据进行解析,支持RxJava。
Retrofit和Volley一样,网络请求任务在背后线程中进行,返回结果的处理(或者说回调方法)在UI线程中执行。
此外这里给出Retrofit2相对于Retrofit1的改进
- 支持了在一个类型中的同步和异步,同时,一个请求也可以被真正地终止;Retrofit1之前是分开的,即需要定义两个方法
- 每一个 call 对象实例只能被调用一次,request 和 response 都是一一对应的;若需多次重复请求,则建议每次请求前clone一个call对象
- Response 对象增加了:响应码(the reponse code),响应消息(the response message),以及读取相应头(headers)
- @Url ,允许你直接传入一个请求的 URL,定义如下方法
- @GET
- Call<List<Contributor>> repoContributorsPaginate( @Url String url);
- 动态 URL Parameter
- String links = response.headers().get("Link");
- Call<List<Contributor>> nextCall = gitHubService.repoContributorsPaginate(nextLink);
基本使用
一、引入依赖
compile 'com.google.code.gson:gson:2.6.2' compile 'com.squareup.retrofit2:retrofit:2.0.2' compile 'com.squareup.retrofit2:converter-gson:2.0.2' compile 'com.squareup.okhttp3:okhttp:3.2.0' 一般使用Retrofit还会使用它和RxJava配套使用,因此还需要添加如下依赖 compile 'io.reactivex:rxjava:1.1.3' compile 'io.reactivex:rxandroid:1.1.0' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2' 为了使用更多的java注解添加下面的依赖 provided 'org.glassfish:javax.annotation:10.0-b28'
二、定义网络业务接口
Retrofit的网络请求都是写在一个接口中,并使用一定的注释如下面一组请求接口:注意接口中的每个方法的参数都需要使用标注,不采用标注将会报错。
public interface MyApiEndpointInterface { @GET("users/{username}") //note1 Call<User> getUser(@Path("username") String username,@Header("Cache-Control") int maxAge); @Headers({"Cache-Control: max-age=640000", "User-Agent: My-App-Name"})//note2 @POST("users/new") Call<User> createUser(@Body User user,@Query("sort") String sort); //user支持被gson序列化的一个类,如JavaBean @FormUrlEncoded @POST("some/endpoint") Call<SomeResponse> someEndpoint(@FieldMap Map<String, String> names); @POST("https://blog.csdn.net/") //note3 Call<Response<User>> getUser(@Query("name") String name); @Multipart //note4 @POST("some/endpoint") Call<Response> uploadImage(@Part("description") String description, @Part("image") RequestBody image) @GET("users/{username}") //note5 void getUser(@Path("username") String username, Callback<User> cb);}
1、Retrofit的网络请求返回的结果都是call<?>的形式,?代表希望对Response body解析得到的数据类型;如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
2、Retrofit的注解有如下几种常见注解
@GET 发送get方法的请求,@POST发送post方法的请求,@Header 网络请求的Header部分的键值对
@Body Post请求报文的Body内容,内容往往是一个对Java Bean解析后得到的JSON String,@Path网络路径的缺省值,@Query网络请求地址后面的查询键值对,如www.baidu.com/s?wd=REST&rsv_spt=1.
@FormUrlEncoded 和 @FieldMap配套使用,用于向Post表单传入键值对
3、一旦创建一个Retrofit实例就意味着网络请求根目录已经确定,但是Retrofit支持动态改变网络请求根目录。
4、使用@Multipart 标注的网络请求,用于上传文件;同时该请求必须在请求方法的参数中有一个使用@Part注解的参数。下面给出一个简单的使用例子
MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");file = new File("/storage/emulated/0/Pictures/MyApp/test.png");RequestBody requestBody = RequestBody.create(MEDIA_TYPE_PNG, file);Call<Response> call = apiService.uploadImage("test", requestBody);
三、得到网络业务接口的实体
public static final String BASE_URL = "http://api.myservice.com/";Retrofit retrofit = new Retrofit.Builder() //note1 .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) //gson负责对Response数据进行解析 .build();MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class); //note2
1、获得一个Retrofit对象
2、由retrofit得到一个网络请求接口的实现类
四、使用接口实体获取Response
Call<User> call = apiService.getUser("evan",5000);同步请求:Response<User> response = call.execute();异步请求:call.enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { .... }});
call只能使用一次,调用第二次的时候,就会出现失败的错误。当你想要多次请求一个接口的时候,直接用 clone 的方法来生产一个新的,相同的可用对象,clone代价很低。
Retrofit2 中 Response 对象增加了曾经一直被我们忽略掉的重要元数据:响应码(the reponse code),响应消息(the response message),以及读取相应头(headers){cookies}。
五、关于授权
//note1Interceptor interceptor = new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request newRequest = chain.request().newBuilder().addHeader("User-Agent", "Retrofit-Sample-App").build(); return chain.proceed(newRequest); }};//note2OkHttpClient.Builder builder = new OkHttpClient.Builder();builder.interceptors().add(interceptor);builder.cache(new Cache(new File("C:\\okhttp"),10*1024*1024)) ;OkHttpClient client = builder.build();//note3Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create()) .client(client) .build();
1、创建拦截器,拦截器的工作就是在发送请求前在请求报文的header中添加一些键值对
2、添加前面的拦截器到client中,为了提高网络访问效率我们还为OkHttp设置了缓存,到此为止跟okhttp的操作都是一样的
3、为Retrofit设置个性化OkHttpClient对象
六、Retrofit和RxJava的配套使用
PartA 定义网络业务接口
public interface MyApiEndpointInterface { @GET("/users/{username}") Observable<User> getUser(@Path("username") String username); @POST("/users/new") Observable<User> createUser(@Body User user);}
网络请求方法的返回值由之前的call<?> 转变成Observable<?>, ?类支持使用gson对其进行序列化
PartB 得到网络业务接口的实体
RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.create(); //note1RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()); //note2Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create()); .addCallAdapterFactory(rxAdapter) .build();MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class); //note3
1、默认发送同步的网络请求
2、默认发送异步的网络请求
3、获取网络请求实体
PartC 使用示例
String username = "sarahjean";Observable<User> call = apiService.getUser(username); //note1Subscription subscription = call //note2.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<User>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { if (e instanceof HttpException) { HttpException response = (HttpException)e; int code = response.code(); } } @Override public void onNext(User user) { }});
1、获取到Observable<User>之后的使用与RxJava一样的使用流程
2、这里要十分注意的是在Android的Activity和Fragment中使用Rxjava需要在对应的onDestroy方法中调用subscription.unsubscribe()方法,防止OOM
源码学习
在正式学习源码之前首先看看我们想要了解的内容有哪些?
- Retrofit对象的创建需要哪些元素;该对象内部保存有哪些元素
- Retrofit对象的create方法返回的对象和传进去的接口有何种关联
- 业务接口使用各种注解对方法会产生何种影响?常用注解@GET、@POST、@MultiPart、@FieldMap等作用效果
- Retrofit的ConverterFactory; CallAdapterFactory方法设置的Converter.Factory和CallAdapter.Factory对象何时何地起作用
- Call<?> 对象及其execute和queue方法的内部逻辑
- Observable<?>相对于RxJava有什么特殊的地方
- Response<?>类型内部结构,有哪些数据可以访问
Retrofit.class
Retrofit类中存储有如下的对象
Fields
private final HttpUrl baseUrl; //网络请求基地址private final List<Converter.Factory> converterFactories; //数据转换器工厂集合private final List<CallAdapter.Factory> adapterFactories; //网络请求适配器工厂集合private final okhttp3.Call.Factory callFactory; //底层进行网络请求工厂private final Executor callbackExecutor; //回调方法执行器private final boolean validateEagerly; //是否提前对业务接口中的注解进行验证转换的标志位private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>(); //ServiceMethod是对业务接口中方法的注解进行解析之后得到的对象,该对象包含了访问网络的除了方法参数值之外的所有必要信息;如数据转换器、网络请求适配器、网络请求工厂、基地址、Http方法等等。
Retrofit()@Retrofit.class
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl, List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories, Executor callbackExecutor, boolean validateEagerly) { this.callFactory = callFactory; this.baseUrl = baseUrl; this.converterFactories = unmodifiableList(converterFactories); //note1 this.adapterFactories = unmodifiableList(adapterFactories); this.callbackExecutor = callbackExecutor; this.validateEagerly = validateEagerly;}
1、unmodifiableList(list)方法近似于UnmodifiableList<E>(list);这样做的好处在于创建的新对象能够对list数据进行访问,但是不可通过该对象对list集合中的元素进行任何修改
Builder.class@Retrofit.class
该类中的域基本与Retrofit一样,但是多了一个如下的域
private Platform platform;
Builder()@Builder.class
public Builder() { this(Platform.get()); } //note1Builder(Platform platform) { this.platform = platform; converterFactories.add(new BuiltInConverters()); //note2}
1、如果是安卓系统则Platform.get()获取到的是一个如下的对象;也可以获得IOS、Java对应的类,很好很强大。
static class Android extends Platform { @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); //note3 } @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { return new ExecutorCallAdapterFactory(callbackExecutor); //note4 } static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); //获取一个和Android UI线程绑定的Handler @Override public void execute(Runnable r) { handler.post(r); } //异步任务在UI线程中执行 }}
2、添加一个内置的转换器工厂到converterFactories中
3、返回一个默认的回调方法执行器,该执行器负责在指定的线程中执行回调方法,Android就是将回调方法在UI线程中执行
4、如果我们不准备将RxJava和Retrofit一起使用,一般都是使用的这个默认CallAdapter.Factory,因此我们在后面对ExecutorCallAdapterFactory.class源码进行解析,先不急着看等遇到了再去看。
所以到此为止Builder默认创建了Converter.Factory、CallAdapter.Factory,Executor三个系统默认的数据转换器工厂、网络请求适配器工厂、回调执行器。可能大伙儿有点陌生,这些个工厂究竟是干嘛的?特此先跟大伙儿说几句,本节的末尾还会详谈:
Converter.Factory
- public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) //对响应数据的解析
- public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) //对请求数据的解析
- public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) //普通对象转件String 如JSON
CallAdapter.Factory
- public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) //获取网络请求适配器
- protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); }
- protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); }
CallAdapter<?>
- Type responseType(); //该请求适配器返回的数据类型
- <R> T adapt(Call<R> call); //该请求适配器对原始Call的再次封装,如Call<R>到Observable<R>
Build()@Builder.class
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required.");} //note1 okhttp3.Call.Factory callFactory = this.callFactory; //note2 if (callFactory == null) { callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; //note3 if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); //note4 adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); //note5 return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
1、基地址检测
2、网络请求执行器检测,若为空直接设置为OkHttpClient
3、回调方法执行器检测,若为空直接设置为Buidler构造器中创建的Executor
4、复制Builder中的List<CallAdapter.Factory>,并向该集合中添加Buidler构造器中创建的CallAdapter.Factory请求适配器,添加在集合器末尾
5、复制Builder中的List<Converter.Factory>,虽然该集合没有像前面第四步那样添加默认转换器,但是别忘了其实在Builder的构造器中已经向Builder中的List<Converter.Factory>集合的第一个位置插入了一个默认的转换器。
请求适配器工厂集合存储的是:自定义1适配器工厂、自定义2适配器工厂...默认适配器工厂
数据转换器工厂集合存储的是:默认数据转换器工厂、自定义1数据转换器工厂、自定义2数据转换器工厂....
create()@Retrofit.class
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); //note1 if (validateEagerly) { eagerlyValidateMethods(service); //note2 } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, //note3 new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);} ServiceMethod serviceMethod = loadServiceMethod(method); //note4 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //note5 return serviceMethod.callAdapter.adapt(okHttpCall); //note6 } });}
1、判断该参数Service是不是一个接口,接口中是否有定义方法;否则抛出异常
2、判断是否需要提前验证,方法内容就是给接口中的定义的每个方法的注解进行解析并得到一个ServiceMethod对象,并以Method为键将该对象存入LinkedHashMap集合中。如果不是提前验证则在第四步的时候会动态解析对应的方法,得到一个ServiceMethod对象,最后存入到LinkedHashMap集合中,有延迟加载的意思。默认都是延迟加载。eagerlyValidateMethods方法后面还有更为详细的介绍。
3、创建一个代理——java.lang.reflect.Proxy对象;Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)方法近似于getProxyClass(loader, interfaces) .getConstructor(InvocationHandler.class).newInstance(invocationHandler);从字面就可以知道,通过这个代理类,调用interfaces接口的方法实际上是通过调用InvocationHandler类型对象的invoke方法来完成指定的功能。
4、如果代理类传进来的方法是一般方法则不会执行到这里,执行到这里表明调用的方法是我们使用了符合Retrofit规则的标注的方法。该行语句是从serviceMethodCache集合中获取一个对应的ServiceMethod对象。如果没有会临时创建一个ServiceMethod对象,再返回。loadServiceMethod方法后面也有更为详细的介绍。
5、利用上面得到的ServiceMethod对象和方法参数创建一个OkHttpCall对象,该对象相对于okhttp3.Call会在网络请求前后对数据利用该方法对应的数据转换器进行一定的转换,之后内部再通过okhttp3.Call发送请求。OkHttpCall.class的相关方法在后面也会给出详细的介绍。
6、将上面得到的OkHttpCall对象传给ServiceMethod中对应的网络请求适配器工厂的adapt方法中,返回的对象类型就是ServiceMethod对应的方法的返回值类型,如Call<?>;
eagerlyValidateMethods()@@Retrofit.class
private void eagerlyValidateMethods(Class<?> service) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method)) { loadServiceMethod(method); } //note1 }}
1、非平台默认方法则加载解析该方法,并将得到的ServiceMethod对象加入到LinkedHashMap<Method, ServiceMethod>集合中。使用LinkedHashMap该集合的好处就是lruEntries.values().iterator().next()获取到的是集合最不经常用到的元素,提供了一种Lru算法的实现。
loadServiceMethod()@Retrofit.class
ServiceMethod loadServiceMethod(Method method) { ServiceMethod result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); //note1 if (result == null) { result = new ServiceMethod.Builder(this, method).build(); //note2 serviceMethodCache.put(method, result); } } return result;}
1、尝试从集合中获取该方法
2、方法在集合中不存在,创建一个ServiceMethod对象,以method为键存入集合中。往下我们看看如何构建一个ServiceMethod对象
ServiceMethod.class
final okhttp3.Call.Factory callFactory; //方法使用的网络请求工厂 final CallAdapter<?> callAdapter; //方法使用的网络请求适配器 private final Converter<ResponseBody, T> responseConverter; //方法的Response内容转换器 private final HttpUrl baseUrl; //方法的基地址 private final String relativeUrl; //方法的相对地址 private final String httpMethod; //方法的Http方法 private final Headers headers; //方法的http请求头 键值对 private final MediaType contentType; //方法的http报文body的类型 private final boolean hasBody; //方法的http是否有bod private final boolean isFormEncoded; private final boolean isMultipart; private final ParameterHandler<?>[] parameterHandlers; //队方法参数的处理器,用于解析采用了标注的参数
ServiceMethod()@ServiceMethod.class
ServiceMethod(Builder<T> builder) { this.callFactory = builder.retrofit.callFactory(); this.callAdapter = builder.callAdapter; this.responseConverter = builder.responseConverter; this.baseUrl = builder.retrofit.baseUrl(); this.relativeUrl = builder.relativeUrl; this.httpMethod = builder.httpMethod; this.headers = builder.headers; this.contentType = builder.contentType; . this.hasBody = builder.hasBody; y this.isFormEncoded = builder.isFormEncoded; this.isMultipart = builder.isMultipart; this.parameterHandlers = builder.parameterHandlers;}
该对象包含了访问网络的所有基本的信息。
内部类Builder<T>.class@ServiceMethod<T>.class
Fields
final Retrofit retrofit; //当前方法所属的Retrofitfinal Method method; //当前方法final Annotation[] methodAnnotations; //当前方法的注解final Annotation[][] parameterAnnotationsArray; //当前方法参数的注解ParameterHandler<?>[] parameterHandlers; //当前方法参数的处理器final Type[] parameterTypes; //当前方法参数的数据类型CallAdapter<?> callAdapter; //请求适配器Type responseType; //请求适配器的返回值类型Converter<ResponseBody, T> responseConverter; //网络请求内容转换器String httpMethod; //当前方法对应的http请求方法boolean hasBody; //当前请求是否有bodyString relativeUrl; //当前方法的相对URL地址Set<String> relativeUrlParamNames; //当前方法的URL参数Headers headers; //当前请求的header键值对boolean isMultipart; //当前请求是否是Multipartboolean isFormEncoded; // 一般设置了Multipart标签不能使用FormEncoded标签,该标签的使用参考前面的使用说明
Builder()@Builder.class
public Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); //获取方法的注解 this.parameterTypes = method.getGenericParameterTypes(); //获取方法的参数类型 this.parameterAnnotationsArray = method.getParameterAnnotations(); //获取方法参数的注解}
build()@Builder.class
public ServiceMethod build() { callAdapter = createCallAdapter(); //note1 responseType = callAdapter.responseType(); //note2 if (responseType == Response.class || responseType == okhttp3.Response.class) { throw ...} //note3 responseConverter = createResponseConverter(); //note4 for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); }//note5 if (httpMethod == null) { throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } ·····if (!hasBody) {//note6 if (isMultipart) {throw methodError( "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) {throw methodError("FormUrlEncoded can only be specified on HTTP methods with "+ "request body (e.g., @POST)."); } } int parameterCount = parameterAnnotationsArray.length; //note7 parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; if (Utils.hasUnresolvableType(parameterType)) {throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",parameterType); } Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) {throw parameterError(p, "No Retrofit annotation found.");} parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); //note8 } if (relativeUrl == null && !gotUrl) {throw methodError("Missing either @%s URL or @Url parameter.", httpMethod); } //note9 if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body."); } if (isFormEncoded && !gotField) { throw methodError("Form-encoded method must contain at least one @Field.");} if (isMultipart && !gotPart) { throw methodError("Multipart method must contain at least one @Part.");} return new ServiceMethod<>(this);}
1、近似于 callAdapter = retrofit.callAdapter(returnType, annotations);即根据方法返回值类型和注释从retrofit中获取对应的网络请求适配器。callAdapter方法是从Retrofit的adapterFactories集合中从0开始遍历,找到第一个满足要求的网络请求适配器。
2、从前面的网络适配器(callAdapter)中获取该网络适配器返回的数据类型
3、返回数据类型不可以为retrofit2.Response.class和okhttp3.Response.class类型,Response<T>和Response是有区别的。
4、近似于 responseConverter = retrofit.responseBodyConverter(responseType, annotations);即根据方法返回值类型和注释从retrofit中获取对应的转换器。responseBodyConverter方法是从Retrofit的converterFactories集合中从0开始遍历,找到第一个满足要求的内容转换器。
5、对方法中的DELETE、GET、POST、HEAD、PATCH、PUT、OPTIONS、HTTP、retrofit2.http.Headers、Multipart、FormUrlEncoded几个标注进行处理,大多数标签都会调用方法parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody),该方法对ServiceMethod中的httpMethod、hasBody、relativeUrl、relativeUrlParamNames域进行赋值。
6、请求报文中body中没有内容,但是使用了Multipart或者FormUrlEncode标注则抛出异常
7、当前方法中参数个数,为每个参数创建一个arameterHandler
8、为方法中的每个参数创建一个ParameterHandler<?>对象,该对象的创建过程就对方法参数中的Body、PartMap、Part、FieldMap、Field、Header、QueryMap、Query、Path、Url标注进行解析
9、后面就是一些判断了,最后创建ServiceMethod对象
该方法内容比较多我们再次梳理一下:
首先根据返回值类型和方法标注从Retrofit的网络请求适配器工厂集合和内容转换器工厂集合中分别获取到该方法对应的网络请求适配器和Response内容转换器;根据方法的标注对ServiceMethod的域进行赋值;最后为每个方法的参数的标注进行解析,获得一个ParameterHandler<?>对象,该对象保存有一个Request内容转换器——根据参数的类型从Retrofit的内容转换器工厂集合中获取一个Request内容转换器或者一个String内容转换器。
往下我们接着对create()@Retrofit.class中OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);利用ServiceMethod和方法参数args创建的OkHttpCall类进行分析。
Call.class
首先简单看一下retrofit.Call接口的定义
Response<T> execute() throws IOException; //执行同步请求void enqueue(Callback<T> callback); //执行异步请求boolean isExecuted(); //当前请求是否执行完毕void cancel(); //取消当前请求boolean isCanceled(); //当前请求是否被取消Call<T> clone(); //clone一个当前线程Request request(); //返回当前请求的请求对象
OkHttpCall.class
如果我们没有特别给Retrofit设置一个请求适配器工厂,则Retrofit使用默认的请求适配器工厂——ExecutorCallAdapterFactory.class,默认请求适配器工厂是存放在Retrofit的请求适配器工厂集合的末尾。通过ExecutorCallAdapterFactory得到Call<?> 对接收的网络请求没有经过任何特殊的处理直接交给OkHttpCall.class进行处理。证明分析见后面的ExecutorCallAdapterFactory.class源码部分。对于OkHttpCall类我们就分析下它的execute方法和enqueue方法。final class OkHttpCall<T> implements Call<T>
OkHttpCall()@OkHttpCall.class
Fields
private final ServiceMethod<T> serviceMethod; //构造器接收的参数private final Object[] args; //构造器接收的参数private okhttp3.Call rawCall; //实际进行网络访问的类private Throwable creationFailure; //几个状态标志位private boolean executed;private volatile boolean canceled;OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) { this.serviceMethod = serviceMethod; this.args = args;}
execute()@OkHttpCall.class
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { throw...} call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } //note1 catch (IOException | RuntimeException e) { creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } return parseResponse(call.execute()); //note2 }
1、创建一个okhttp3.Call请求
Request request = serviceMethod.toRequest(args); 该方法会对使用对应的ParameterHandler进行解析,并利用ServiceMethod中存储的headers等数据构造一个okhttp请求
return serviceMethod.callFactory.newCall(request);近似于OkHttpClient.newCall(request)
2、对请求结果解析,方法内部逻辑如下:
Response<T> parseResponse(okhttp3.Response rawResponse) { ResponseBody rawBody = rawResponse.body(); int code = rawResponse.code(); if (code < 200 || code >= 300) { //响应执行失败 ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } if (code == 204 || code == 205) { //响应执行成功 但是没有返回数据body为空 return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); T body = serviceMethod.toResponse(catchingBody); //近似于serviceMethod.responseConverter.convert(body);//即使用ServiceMethod对应的Response内容转换器对接收到的数据进行转换。 return Response.success(body, rawResponse); //近似于return new Response<>(rawResponse, body, null);}
以上过程就是整个网络请求的过程,首先对方法中每个参数利用对应ParameterHandler进行解析,再加上ServiceMethod中存储的Headers等数据创建一个okhttp的Request;使用okhttp发送这个请求;再对接收到的请求利用ServiceMethod存储的内容转换器对响应内容进行转换,最终得到一个Response<T>对象。
enqueue()@OkHttpCall.class
@Override public void enqueue(final Callback<T> callback) { if (callback == null) throw new NullPointerException("callback == null"); okhttp3.Call call; Throwable failure; 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) {failure = creationFailure = t;} } } if (failure != null) { callback.onFailure(this, failure); return;} if (canceled) {call.cancel();} call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException { Response<T> response; try { response = parseResponse(rawResponse); //对响应数据进行解析 } catch (Throwable e) { callFailure(e); return; } callSuccess(response); }//end of onResponse @Override public void onFailure(okhttp3.Call call, IOException e) { try { callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();} }//end of onFailure private void callFailure(Throwable e) { try {callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace();} } private void callSuccess(Response<T> response) { try {callback.onResponse(OkHttpCall.this, response);} catch (Throwable t) {t.printStackTrace();} } });}
异步请求跟同步请求类似,也有创建okhttp请求,并对okhttp请求响应结果进行转换的过程;不同之处在于异步请求会将回调方法交给回调执行器在指定的线程中执行。对于okhttp的Call对象的enqueue、execute方法这里不再往下分析了,感兴趣的同学参考博客
到此为止,我们学习到的有:
一、通过Retrofit的Builder内部类如何创建一个Retrofit对象,通过buidler设置baseUrl和可选的Converter.Factory、CallAdapter.Factory。
二、调用Retrofit的create方法,根据create方法参数(接口类对象)构造一个代理,代理的执行实体是一个InnovationHandler对象。
三、调用接口的所有方法最终都是通过调用InnovationHandler的invoke方法,invoke方法接收的参数主要有Method和args,invoke方法内部逻辑为:
- 将Method对象转为一个ServiceMethod对象,构造过程会根据方法的标注从Retrofit的ArrayList<Converter.Factory> converterFactories和ArrayList<CallAdapter.Factory> adapterFactories集合中获取该方法对应的Converter<ResponBody,T> responseConverters和CallAdapter<?> callAdapter,同时根据方法参数的标注为每个参数创建一个ParameterHandler对象,该对象包含有一个Converter<?,RequestBody> requestConverter对象或者一个Converter<?,String> stringConverter对象;用于对每个参数进行转换处理。
- 利用前面的ServiceMethod对象和args创建一个OkHttpCall对象,该对象内部会包含一个okhttp3.Call对象,OkHttpCall会在okhttp3.Call对象进行网络访问前后分别利用对应的requestConverter和responseConverter对数据进行相应的转换。
- 通过ServiceMethod的callAdapter对象的adapter方法(参数OkHttpCall)创建一个本Method预期的返回值对象;
- 如果在构造Retrofit的时候没有设置CallAdapter.Factory则都是返回的Call<?>对象,该对象的execute方法和enqueue方法实际上都是直接调用OkHttpCall的同名方法,返回一个Response<T>对象
- 如果在构造Retrofit的时候设置了RxJavaCallAdapterFactory 那么可能返回对象可以定义为Observable<?> 类型,对于该类型我们的使用可以跟RxJava的使用一样,但是内部具体如何实现,我们还未曾介绍过。
上面我们对学过的内容进行了总结,Retrofit基本上也了解的差不多了。往下深入分析就是对Converter.Factory和CallAdapter.Factory的分析,如果童鞋们对这部分不感兴趣那么可以到此打住了,都散了吧。感兴趣同学请看下节内容。
reference:
http://square.github.io/retrofit/
https://realm.io/cn/news/droidcon-jake-wharton-simple-http-retrofit-2/
0 0
- Retrofit的使用与深入学习(上)
- Retrofit的使用与深入学习(下)
- Retrofit的学习与资料
- RxAndroid 与 Retrofit的使用
- Retrofit的详解与使用(多文件上传)
- Retrofit的使用与基本的封装
- RxJava 与 Retrofit 参考学习的资料
- RxJava的使用与深入学习
- EventBus的使用与深入学习
- EventBus的使用与深入学习
- EventBus的使用与深入学习
- EventBus的使用与深入学习
- EventBus的使用与深入学习
- RxJava的使用与深入学习
- RxJava的使用与深入学习
- RxJava的使用与深入学习
- Retrofit的使用(1)
- Retrofit的使用(2)
- 直接设置媒体音量大小
- mysql索引总结----mysql 索引类型以及创建
- Perl读取文件的两种常用方式
- 为什么要用webservice?
- 《VR入门系列教程》之12---转换矩阵
- Retrofit的使用与深入学习(上)
- Javaweb学习之关于分页
- 使用Gson解析复杂的json数据
- EasyUI Tabs全攻略
- 美国白宫将探讨人工智能 概念股分析
- Java 正则表达式详解
- 关于“工人犯错”话题的个人感触
- 开源集锦(三)GitHub开源控件(一)
- 《VR入门系列教程》之13---相机与立体渲染