Android Retrofit 2.0(三)从源码分析原理

Retrofit·特

  1. 性能最好,处理最快
  2. 使用REST API时非常方便;
  3. 传输层默认就使用OkHttp;
  4. 支持NIO;
  5. 拥有出色的API文档和社区支持
  6. 速度上比volley更快;
  7. 如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求。
  8. 默认使用Gson

系列文章推荐:

Android 必须知道的网络请求框架库,你不可错过的框架介绍篇

Android Retrofit 2.0(一)初次见面请多多关照

Android Retrofit 2.0(二)使用教程OkHttp3 + Gson + RxJava


基础使用

以下就是用Retrofit 实现一个登录Login接口的小功能,基本用法如下:
private  void getLogin() {  
    Retrofit retrofit = new Retrofit.Builder()  //1、创建Retrofit对象指定域名
            .baseUrl("http://localhost:8080/")  
            .addConverterFactory(GsonConverterFactory.create())  
            .build();  
    ApiManager apiService = retrofit.create(ApiManager.class); //2、对象创建一个API接口对象:
   Call<LoginResult> call = apiService.getData("lyk", "1234");  
   call.enqueue(new Callback<LoginResult>() {  //3、call对象发生同步/异步请求
       @Override  
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {  
           if (response.isSuccess()) {  
               // 请求成功  
           } else {  
              //直接操作UI 或弹框提示请求失败  
           }  
       }  
       @Override  
       public void onFailure(Call<LoginResult> call, Throwable t) {  
           //错误处理代码  
       }  
   });  
}  

ApiManager接口

public interface ApiManager {  
 //根据api新建一个Java接口,用Java注解来描述这个api
 @GET("login/")  
 Call<LoginResult> getData(@Query("name") String name, @Query("password") String pw);  

原理分析

    通过以上案例可见,创建了一个Java 接口来描述 Http请求方法的行为,即APIManager。Retrofit就像一个适配器(Adapter)的角色,将一个Java接口转换成一个Http请求并返回一个Call对象,简单的调用接口方法就可以发送API请求,如此精简!非常的神奇!相比Volley,Retrofit完全隐藏了Request 的请求体,并使用okhttp执行请求。因为,Volley 在描述一个HTTP请求是需要创建一个Request对象,然后将这个请求对象放到一个请求队列中,在子线程中用HttpUrlConnection去执行请求。

问题来了:Retrofit 是怎么实现的呢?
答案就是Java的动态代理 

Java动态代理,是一种结构性设计模式,可以在要调用的Class方法前或后,插入想要执行的代码进行改造。例如猪八戒娶媳妇,剧情中娶的媳妇并非高翠兰本人,而是一个代理人,即孙悟空。

案例中关键两行代码:

ApiManager apiService = retrofit.create(ApiManager.class); //2、retrofit对象创建一个API接口对象
  
Call<LoginResult> call = apiService.getData("lyk", "1234"); //返回响应接口回调

这简短的两行代码,隐藏了Request请求体并拿到Response返回Call对象。跟着好奇心去打开源码一探究竟吧!进入 create 方法一看发现代码很少,但这几行代码才是 Retrofit 精妙之处。

/** Create an implementation of the API defined by the {@code service} interface. */
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, 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 serviceMethod = loadServiceMethod(method);
        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
        return serviceMethod.callAdapter.adapt(okHttpCall);
      }
    });
}

源码分析:

    案例中 Retrofit 创建了一个 apiService 对象的实例 ,其实是得到的 apiService 是一个动态代理对象,并不是 APIManger 本尊实例化对象(哈哈~高翠兰是孙悟空变的!)而当 apiService 对象调用 getData方法时,就会被这个动态代理拦截并在内部做些小动作,它会调用 Proxy.newProxyInstance方法 中的 InvocationHandler 对象,它的 invoke方法 会传入3个参数:

  1. Object proxy     :代理对象 ,即APIManner.class
  2. Method method :调用方法,即getData方法
  3. Object... args    : 参数对象,即 "lyk","1234"

    Retrofit 得到了 method 和 参数args 。接下去 Retrofit 就会用 Java反射 获取到 getData方法 的注解信息,配合args参数,创建一个ServiceMethod对象。关键就在于 ServiceMethod 对象,Retrofit 费心费力就为了创建它到底是想做什么呢?

    ServiceMethod 是服务于请求方法的,服务于传入Retrofit的proxy对象的method方法,即getData方法。如何服务呢?它可以将method通过各种内部接口解析器进行组装拼凑,最终生成一个Request请求体。这个Request 包含 api域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client。一句话就是:Retrofit 使用Java动态代理就是要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送Http请求。


细节分析

想要弄清楚Retrofit的细节,先来简单了解一下Retrofit源码组成结构:


  • 一个retrofit2.http包,里面全部是定义HTTP请求的Java注解,比如GET、POST、PUT、DELETE、Headers、Path、Query等;
  • 余下的retrofit2包中,几个类和接口retrofit的代码真的很少很简单,因为retrofit把网络请求这部分功能全部交给了OkHttp。

(1)Retrofit接口


Retrofit的设计使用插件化而且轻量级,高内聚而且低耦合,这都和它的接口设计有关。Re
trofit中定义了四个接口:
  1. Callback<T>
  2. Converter<F, T>
  3. Call<T>
  4. CallAdapter<T>
1、Callback<T>
这个接口就是retrofit请求数据返回的接口,只有两个方法:
void onResponse(Response<T> response);
void onFailure(Throwable t);
2、Converter<F, T>
这个接口主要的作用就是将HTTP返回的数据解析成Java对象,主要有Xml、Gson、protobuf等。你可以在创建Retrofit对象时添加你需要使用的Converter实现。

3、Call<T>
这个接口主要的作用就是发送一个HTTP请求,Retrofit默认的实现是OkHttpCall<T>,你可以根据实际情况实现你自己的Call类。这个设计和Volley的HttpStack接口设计的思想非常相似,子类可以实现基于HttpClient或HttpUrlConnetction的HTTP请求工具。

4、CallAdapter<T>
这个借口的属性只有responseType一个;这个接口的实现类也只有DefaultCallAdapter一个。这个方法的主要作用就是将Call对象转换成另一个对象,为了支持RxJava才设计这个类的吧。

(2)Retrofit的运行过程

    上面讲的案例代码,返回了一个动态代理对象。而执行这段代码时,返回了一个OkHttpCall对象,拿到这个 Call 对象才能真正执行 HTTP 请求。

ApiManager apiService = retrofit.create(ApiManager.class); //2、retrofit对象创建一个API接口对象
  
Call<LoginResult> call = apiService.getData("lyk", "1234"); //返回响应接口回调
上面代码中 apiService 对象其实是一个动态代理对象,并不是通过 implements 实现 ApiManager接口产生的对象。当 apiService 对象调用 getData方法 时会被动态代理拦截,然后调用 Proxy.newProxyInstance 方法中的 InvocationHandler 对象, 创建一个  ServiceMethod对象:
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

(3)创建ServiceMethod

刚才说到 ServiceMethod 是服务于方法的,具体来看一下创建这个ServiceMethod的过程是怎么样的:
首先,获取到上面说到的 Retrofit的接口:
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
responseConverter = createResponseConverter();
然后,解析Method方法的注解,其实就是想获取Http请求的方法。比如请求方法是GET还是POST形式,如果没有程序就会报错。还会做一系列的检查,比如在方法上注解了@Multipart,但是Http请求方法是GET,同样也会报错。
for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
}

if (httpMethod == null) {
   throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
其次,比如上面 apiService 接口的方法中带有参数{name,password},这都占位符,而参数值是在Java方法调用中传入的。
那么 Retrofit 会使用一个 ParameterHandler 来进行替换:
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
最后,ServiceMethod 还会做其他的检查。
比如用了 @FormUrlEncoded 注解,那么方法参数中必须至少有一个 @Field 或 @FieldMap。

(4)执行Http请求

    之前讲到,OkHttpCall是实现了Call接口的,并且是真正调用 OkHttp3 发送Http请求的类。OkHttp3发送一个Http请求需要一个Request对象,而这个Request对象就是从 ServiceMethod 的 toRequest 返回的。

    总之,OkHttpCall 就是调用 ServiceMethod 获得一个可以执行的 Request 对象,然后等到 Http 请求返回后,再将 response body 传入 ServiceMethod 中,ServiceMethod 就可以调用 Converter 接口将 response body 转成一个Java对象。

    综上所述,ServiceMethod 中几乎保存了一个api请求所有需要的数据,OkHttpCall需要从ServiceMethod中获得一个Request对象,然后得到response后,还需要传入 ServiceMethod 用 Converter 转换成Java对象。


Retrofit中使用RxJava


由于Retrofit设计的扩展性非常强,你只需要添加一个 CallAdapter 就可以了

Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("https://api.github.com")
  .addConverterFactory(ProtoConverterFactory.create())
  .addConverterFactory(GsonConverterFactory.create())
  .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  .build();
上面代码创建了一个Retrofit对象,支持Proto和Gson两种数据格式,并且还支持RxJava。


  • Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求。
  • Retrofit的功能非常多的依赖Java反射,代码中其实还有很多细节,比如异常的捕获、抛出和处理,大量的Factory设计模式。
  • Retrofit中接口设计的恰到好处,在你创建Retrofit对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能。

参考连接:
https://blog.csdn.net/jiankeufo/article/details/73186929

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Android 中使用 Retrofit 上传图片可以通过 `@Multipart` 和 `@Part` 注解实现。以下是 Retrofit 2.0 实现图文上传的方法总结: 1. 添加依赖库 在项目的 `build.gradle` 文件中添加以下依赖库: ```groovy implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' ``` 2. 创建 Retrofit 实例 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); ``` 3. 创建 API 接口 ```java public interface ApiService { @Multipart @POST("upload") Call<ResponseBody> upload( @Part("description") RequestBody description, @Part MultipartBody.Part file); } ``` 其中,`@Multipart` 注解表示这是一个包含文本和文件的表单;`@POST("upload")` 表示上传的 URL 地址;`@Part("description") RequestBody description` 表示上传的文本参数,`description` 是参数名,可以自己定义;`@Part MultipartBody.Part file` 表示上传的文件参数。 4. 创建请求参数 ```java File file = new File(filePath); RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile); RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), "description"); ``` 5. 发送网络请求 ```java ApiService apiService = retrofit.create(ApiService.class); Call<ResponseBody> call = apiService.upload(description, body); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { // 成功上传后的处理 } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { // 上传失败后的处理 } }); ``` 以上就是 Retrofit 2.0 实现图文上传的方法总结。注意,在 `AndroidManifest.xml` 文件中需要添加网络权限: ```xml <uses-permission android:name="android.permission.INTERNET" /> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾阳Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值