简略分析Android的Retrofit应用开发框架源码

面对一个项目,对于Android应用开发框架的选择,我想过三种方案:
1.使用Loader + HttpClient + GreenDao + Gson + Fragment,优点是可定制性强,由于使用Google家自己的Loader和LoaderManager,代码健壮性强。
缺点是整套代码学习成本较高,使用过程中样板代码较多,(比如每一个Request都需要产生一个新类)
2.Volley,作为Google在IO大会上得瑟过的一个网络库,其实不算什么新东西(2013 IO发布),使用较为简单,请求可以取消,可以提供优先级请求,看起来还是不错的。
3.Retrofit,一款为了使请求极度简单化的REST API Client,呼声也很高,使用门槛几乎是小白型。
如何选择呢?首先干掉1,因为对新人的学习成本确实太高,如果要快速开发一个项目,高学习成本是致命的,同时使用起来样板代码很多。

那么如何在Volley和Retrofit中选择呢?尽管网上有很多文章在介绍两个框架的使用方法,而对于其原理,特别是对比分析较少,如果你手里有一个项目,如何选择网络模块呢?
首先说明一下这两个网络框架在项目中的层次:

从上图可知,不管Volley还是Retrofit,它们都是对现有各种方案进行整合,并提供一个友好,快速开发的方案,在整合过程中,各个模块都可以自行定制 或者替换。比如反序列化的工作,再比如HttpClient。

而在本文我们将简略地来看一下Retrofit的源码部分。

注意,本文并不是使用Retrofit的帮助文档,建议先看Retrofit的文档和OkHttp的文档,这些对于理解余下部分很重要。

使用Retrofit发送一个请求
假设我们要从这个地址 http://www.exm.com/search.json?key=retrofit中获取如下Json返回:

 {
  "data": [
        {
         "title":"Retrofit使用简介",
         "desc":"Retrofit是一款面向Android和Java的HttpClient",
         "link":"http://www.exm.com/retrofit"
        },
        {
         "title":"Retrofit使用简介",
         "desc":"Retrofit是一款面向Android和Java的HttpClient",
         "link":"http://www.exm.com/retrofit"
        },
        {
         "title":"Retrofit使用简介",
         "desc":"Retrofit是一款面向Android和Java的HttpClient",
         "link":"http://www.exm.com/retrofit"
        }
     ]
 }

1.引入依赖

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
//gson解析
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

2.配置Retrofit

Retrofit retrofit = new Retrofit.Builder()
          .baseUrl("http://www.exm.com")
          .addConverterFactory(GsonConverterFactory.create())
          .client(new OkHttpClient())
          .build();

3.新建Model类 SearchResult来解析结果

4.新建请求接口
Retrofit使用注解来定义一个请求,在方法上面指定请求的方法等信息,在参数中指定参数等信息。

public interface RestApi {
    @GET("/search.json")
    Call<List<SearchResult>> search(
      @Query("key") String key
       );

      //可以定义其它请求
      @GET("/something.json")
      Call<SomeThing> dosomething(
          @Query("params") long params
          .......
          .......
       );

}

5.发送请求,我们可以发送同步请求(阻塞当前线程)和异步请求,并在回调中处理请求结果。

 RestApi restApi = retrofit.create(RestApi.class);
 Call<List<SearchResult>> searchResultsCall = resetApi.search("retrofit");
 //Response<List<SearchResult> searchResults = searchResultsCall.execute();  同步方法
 searchResultsCall.enqueue(new Callback<List<SearchResult>>() {
        @Override
        public void onResponse(Response<List<SearchResult>> response, Retrofit retrofit) {
          content.setText(response.body().toString());
        }

        @Override
        public void onFailure(Throwable t) {
          content.setText("error");
        }
      });

Retrofit源码分析
Retrofit整个项目中使用了动态代理和静态代理,如果你不太清楚代理模式,建议先google一下,如果对于Java的动态代理原理不是太熟悉,强烈建议先看:这篇文章-戏说代理和Java动态代理
ok,下面按照我们使用Retrofit发送请求的步骤来:

RestApi restApi = retrofit.create(RestApi.class);  //产生一个RestApi的实例

输入一个接口,直接输出一个实例。

这里岔开说一句,现在随便在百度上搜一下Java动态代理,出来一堆介绍AOP(面向切面编程)和Spring,导致一部分人本末倒置,认为动态代理几乎等于AOP,甚至有些人认为动态代理是专门在一个函数执行前和执行后添加一个操作,比如统计时间(因为现在几乎所有介绍动态代理的地方都有这个例子),害人不浅。实际上动态代理是JDK提供的API,并不是由这些上层建筑决定的,它还可以做很多别的事情,Retrofit中对动态代理的使用就是佐证。
搂一眼这里的源码,再次建议,如果这里代码看不明白,先看看上面提到的那篇文章:

public <T> T create(final Class<T> service) {
 //返回一个动态代理类的实例
 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
  //这个InvocationHandler是关键所在,以后调用restapi接口中的方法都会被发送到这里
  new InvocationHandler() {
   private final Platform platform = Platform.get();

   @Override
   public Object invoke(Object proxy, Method method, Object... args)
     throws Throwable {
    /* 如果是Object的方法,如toString()等,直接调用InvocationHandler的方法,
     *注意,这里其实是没有任何意义的,因为InvocationHandler其实是一个命令传送者
     *在动态代理中,这些方法是没有任何语义的,所以不需要在意
     */
    if (method.getDeclaringClass() == Object.class) {
     return method.invoke(this, args);
    }
    //对于Java8的兼容,在Android中不需要考虑
    if (platform.isDefaultMethod(method)) {
     return platform.invokeDefaultMethod(method, service, proxy, args);
    }
    //返回一个Call对象
    return loadMethodHandler(method).invoke(args);
   }
  });
}

我们可以看到Retrofit.create()之后,返回了一个接口的动态代理类的实例,那么我们调用这个代理类的方法时,调用自然就被发送到我们定义的InvocationHandler中,所以调用

Call<List<SearchResult>> searchResultsCall = resetApi.search("retrofit"); 

时,直接调用到InvocationHandler的invoke方法,下面是invoke此时的上下文:

  @Override
   public Object invoke(Object proxy, Method method, Object... args)
     throws Throwable {
     //proxy对象就是你在外面调用方法的resetApi对象
     //method是RestApi中的函数定义,
     //据此,我们可以获取定义在函数和参数上的注解,比如@GET和注解中的参数
     //args,实际参数,这里传送的就是字符串"retrofit"

    //这里必然是return 一个Call对象
    return loadMethodHandler(method).invoke(args);
   }

此时,invoke的返回必然是一个Call,Call是Retrofit中对一个Request的抽象,由此,大家应该不难想象到loadMethodHandler(method).invoke(args); 这句代码应该就是去解析接口中传进来的注解,并生成一个OkHttpClient中对应的请求,这样我们调用searchResultsCall时,调用OkHttpClient走网络即可。确实,Retrofit的主旋律的确就是这样滴。

注意,实际上Call,CallBack这种描述方式是在OkHttp中引入的,Retrofit底层使用OkHttp所以也是使用这两个类名来抽象一个网络请求和一个请求回来之后的回调。总体来看,Retrofit中的Call Callback持有一个OkHttp的Call Callback,将对Retrofit中的各种调用转发到OkHttp的类库中,实际上这里就是静态代理啦,因为我们会定义各种代理类,比如OkHttpCall

注 下文中如不无明确支出,则所有的Call,CallBack都是Retrofit中的类
MethodHandler类
MethodHandler类,它是Retrofit中最重要的抽象了,每一个MethodHandler对应于本例的RestApi中的一个每个方法代表的请求以及和这个请求相关其它配置,我们来看看吧。

//这个OkHttp的工厂,用于产生一个OkHttp类库中的Call,实际上就是传入配置的Builder的OkHttpClient
private final okhttp3.Call.Factory callFactory;
//通过Method中的注解和传入的参数,组建一个OkHttp的Request
private final RequestFactory requestFactory;
//用于对Retrofit中的Call进行代理
private final CallAdapter<?> callAdapter;
//用于反序列化返回结果
private final Converter<ResponseBody, ?> responseConverter;

// 返回一个Call对象
Object invoke(Object... args) {
  return callAdapter.adapt(new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
}

在Retrofit中通过添加Converter.Factory来为Retrofit添加请求和响应的数据编码和解析。所以我们可以添加多个Converter.Factory为Retrofit提供处理不同数据的功能。

CallAdapter 可以对执行的Call进行代理,这里是静态代理。我们也可以通过添加自己的CallAdapter来作一些操作,比如为请求加上缓存:

 new CallAdapter.Factory {
   @Override
    public <R> Call<R> adapt(Call<R> call) { return call;}
 }

class CacheCall implements Call {
  Call delegate;
   CacheCall(Call call) {
     this.delegate = call;
  }

  @Override
  public void enqueue(Callback<T> callback) {
    //查看是否有缓存,否则直接走网络
    if(cached) {
      return cache;
    }
    this.delegate.enqueue(callback);
  }
}

至此,我们在调用resetApi.search("retrofit");时,实际上调用的层层代理之后的OkHttpCall,它是MethodHandler中invoke的时候塞入的。看看OkHttpCall中的代码吧:

@Override
public void enqueue(final Callback<T> callback) {
 okhttp3.Call rawCall;
 try {
  //创建一个okhttp的Call
  rawCall = createRawCall();
 } catch (Throwable t) {
  callback.onFailure(t);
  return;
 }
 //直接调用okhttp的入队操作
rawCall.enqueue(new okhttp3.Callback() {
 private void callFailure(Throwable e) {
  try {
   callback.onFailure(e);
  } catch (Throwable t) {
   t.printStackTrace();
  }
 }

 private void callSuccess(Response<T> response) {
  try {
   callback.onResponse(response);
  } catch (Throwable t) {
   t.printStackTrace();
  }
 }

 @Override
 public void onFailure(Request request, IOException e) {
  callFailure(e);
 }

 @Override
  public void onResponse(okhttp3.Response rawResponse) {
  Response<T> response;
  try {
   //解析结果
   response = parseResponse(rawResponse);
  } catch (Throwable e) {
   callFailure(e);
   return;
  }
  callSuccess(response);
 }
});
}

如此一来,OkHttpClient的回调也被引导到我们的Callback上来,整个流程就已经走通了。

总结
终于到了总结的时候了,一般来说,这都是干货的时候,哈哈~

Volley对比优势
1.缓存处理;Volley自己就提供了一套完整的缓存处理方案,默认使用文件存储到磁盘中,并且提供了TTL SOFTTTL这么体贴入微的机制;一个Request可能存在两个Response,对于需要显示缓存,再显示网络数据的场景真是爽的不要不要的。而Retrofit中并没有提供任何和缓存相关的方案。
2.代码简单,可读性高。Volley的代码是写的如此的直接了当,让你看起代码来都不需要喝口茶,这样的好处是我们我们需要修改代码时不太容易引入bug....囧
同一个请求如果同时都在发送,那么实际上只会有一个请求真正发出去, 其它的请求会等待这个结果回来,算小小优化吧。实际上这种场景不是很多哈,如果有,可能是你代码有问题...
请求发送的时候提供了优先级的概念,但是是只保证顺序出去,不保证顺序回来,然并卵。
支持不同的Http客户端实现,默认提供了HttpClient和HttpUrlConnection的实现,而Retrofit在2.0版本之后,锁死在OkHttp上了。
3.支持请求取消
Retrofit
1.发送请求真简单,定义一个方法就可以了,这么简单的请求框架还有谁?Volley?
2.较好的可扩展性,Volley中每一个新建一个Request时都需要指定一个父类,告知序列化数据的方式,而Retrofit中只需要在配置时,指定各种转换器即可。CallAdapter的存在,可以使你随意代理调用的Call,不错不错。。。
3.OkHttpClient自带并发光环,而Volley中的工作线程是自己维护的,那么就有可能存在线程由于异常退出之后,没有下一个工作线程补充的风险(线程池可以弥补这个缺陷),这在Retrofit中不存在,因为即使有问题,这个锅也会甩给OkHttp,嘿嘿
4.支持请求取消

再次说明的是,Retrofit没有对缓存提供任何额外支持,也就是说它只能通过HTTP的Cache control做文件存储,这样就会有一些问题:
1.需要Server通过Cache control头部来控制缓存,需要修改后台代码
2.有些地方比较适合使用数据库来存储,比如关系存储,此时,Retrofit就无能为力了
3.缓存不在我们的控制范围之内,而是完全通过OkHttp来管理,多少有些不便,比如我们要删除某一个指定的缓存,或者更新某一个指定缓存,代码写起来很别扭(自己hack请求头里面的cache contrl)

而在我们项目的实际使用过程中,缓存是一个比较重要的角色,Retrofit对缓存的支持度不是很好,真是让人伤心。。。
但是,我们还是觉得在使用中Retrofit真心比较方便,容易上手,通过注解代码可读性和可维护性提升了N个档次,几乎没有样板代码(好吧,如果你觉得每个请求都需要定义一个方法,那这也算。。),所以最后的决定是选择Retrofit。

有人说了,Volley中的两次响应和缓存用起来很happy怎么办?嗯,我们会修改Retrofit,使它支持文件存储和ORM存储,并将Volley的缓存 网络两次响应回调移接过来,这个项目正在测试阶段,待我们项目做完小白鼠,上线稳定之后,我会把代码开源,大家敬请关注。

(0)

相关推荐

  • Android使用Retrofit仿微信多张图片拍照上传

    Android 仿照微信发说说,既能实现拍照,选图库,多图案上传,使用Retrofit技术. 使用方法:详见http://www.jb51.net/article/103009.htm 项目的运行效果: 服务器端接收文件的action UploadFile.java @Controller public class UploadFile extends ActionSupport { /** * */ private static final long serialVersionUID = 1L

  • Android中Retrofit 2.0直接使用JSON进行数据交互

    之前使用Retrofit都是将JSON串转化为POJO对象,针对不同的业务协议,定义相应的接口和参数列表.但是此种方式一般用在自己内部协议基础上,具体大的项目中,有些第三方的集成功能,一般都采用统一的方式即请求JSON和回应JSON进行数据交互,不可能每个第三方协议都会去定义与协议相应的POJO对象. HTTP肯定有GET和POST方法,先定义Retrofit Api的interface: package com.hdnetworklib.network.http; import java.ut

  • Android Retrofit 2.0框架上传图片解决方案

    本文为大家分享了 Android Retrofit 2.0框架上传图片解决方案,具体内容如下 1.单张图片的上传 /** * 上传一张图片 * @param description * @param imgs * @return */ @Multipart @POST("/upload") Call<String> uploadImage(@Part("fileName") String description, @Part("file\&qu

  • android Retrofit2+okHttp3使用总结

    使用前准备 Build.gradle文件配置 dependencies配置 compile 'com.squareup.retrofit2:retrofit:2.0.0' compile 'com.squareup.retrofit2:converter-gson:2.0.0' compile 'com.squareup.okhttp3:logging-interceptor:3.2.0' 网络框架搭建 服务创建类封装(HTTP): public class ServiceGenerator {

  • Android app开发中Retrofit框架的初步上手使用

    Retrofit 2.0 先来说一下Retrofit 2.0版本中一些引人注意的地方. 在Retrofit 2.0中,最大的改动莫过于减小库的体积,首先,Retrofit 2.0去掉了对所有的HTTP客户端的兼容,而钟情于OkHttpClient一个,极大地减少了各种适配代码,原因一会儿说;其次,拆库,比如将对RxJava的支持设置为可选(需要额外引入库):再比如将各个序列化反序列化转换器支持设置为可选(需要额外引入库).于2.0抛弃HttpClient和HttpURLConnection,为了

  • Android Retrofit和Rxjava的网络请求

    Android  Retrofit和Rxjava的网络请求 去年的时候好多公司就已经使用Rxjava和Retrofit了,最近自自己学习了一下,感觉真的是很好用,让自己的网络请求变得更简单了,而且封装性极强. 首先做一下准备工作,导入需要引用的文件 compile 'com.android.support:appcompat-v7:25.1.0' testCompile 'junit:junit:4.12' compile 'io.reactivex:rxjava:1.1.0' compile

  • Android中Retrofit+OkHttp进行HTTP网络编程的使用指南

    Retrofit介绍: Retrofit(GitHub主页https://github.com/square/okhttp)和OkHttp师出同门,也是Square的开源库,它是一个类型安全的网络请求库,Retrofit简化了网络请求流程,基于OkHtttp做了封装,解耦的更彻底:比方说通过注解来配置请求参数,通过工厂来生成CallAdapter,Converter,你可以使用不同的请求适配器(CallAdapter), 比方说RxJava,Java8, Guava.你可以使用不同的反序列化工具

  • Android Retrofit实现多图片/文件、图文上传功能

    什么是 Retrofit ? Retrofit是Square开发的一个Android和Java的REST客户端库.这个库非常简单并且具有很多特性,相比其他的网络库,更容易让初学者快速掌握.它可以处理GET.POST.PUT.DELETE-等请求,还可以使用picasso加载图片. 一.再次膜拜下Retrofit Retrofit无论从性能还是使用方便性上都很屌!!!,本文不去介绍其运作原理(虽然很想搞明白),后面会出专题文章解析Retrofit的内部原理:本文只是从使用上解析Retrofit实现

  • Android Retrofit 中文乱码问题的解决办法

    Android Retrofit 中文乱码问题的解决办法 使用retrofit和rxjava,提交数据时需注意,当数据中有中文时,传到后台,可能会是乱码,需处理: 解决: 1.GET请求改成POST; 2.参数Field改成Query 3.加上@FormUrlEncoded 如下: @FormUrlEncoded @POST("/test/test") Call<Response> register(@Field("name") String name)

  • 简略分析Android的Retrofit应用开发框架源码

    面对一个项目,对于Android应用开发框架的选择,我想过三种方案: 1.使用Loader + HttpClient + GreenDao + Gson + Fragment,优点是可定制性强,由于使用Google家自己的Loader和LoaderManager,代码健壮性强. 缺点是整套代码学习成本较高,使用过程中样板代码较多,(比如每一个Request都需要产生一个新类) 2.Volley,作为Google在IO大会上得瑟过的一个网络库,其实不算什么新东西(2013 IO发布),使用较为简单

  • 详细分析Android中实现Zygote的源码

    概述 在Android系统中,所有的应用程序进程,以及用来运行系统关键服务的System进程都是由zygote进程负责创建的.因此,我们将它称为进程孵化器.zygote进程是通过复制自身的方式来创建System进程和应用程序进程的.由于zygote进程在启动时会在内部创建一个虚拟机实例,因此,通过复制zygote进程而得到的System进程和应用程序进程可以快速地在内部获得一个虚拟机实例拷贝. zygote进程在启动完成之后,会马上将System进程启动起来,以便它可以将系统的关键服务启动起来.

  • Android Handler,Message,MessageQueue,Loper源码解析详解

    本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文< Android中Handler的使用>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能.但

  • Android线程间通信Handler源码详解

    目录 前言 01. 用法 02.源码 03.结语 前言 在[Android]线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤.那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的.本篇是关于创建源码的分析. 01. 用法 先回顾一下,在主线程和非主线程是如何创建 Handler 的. //主线程 private val mHandler: Handler = object : Handler(Looper.getMainLo

  • Android开发数据结构算法ArrayList源码详解

    目录 简介 ArrayList源码讲解 初始化 扩容 增加元素 一个元素 一堆元素 删除元素 一个元素 一堆元素 修改元素 查询元素 总结 ArrayList优点 ArrayList的缺点 简介 ArrayList是List接口的一个实现类,它是一个集合容器,我们通常会通过指定泛型来存储同一类数据,ArrayList默认容器大小为10,自身可以自动扩容,当容量不足时,扩大为原来的1.5倍,和上篇文章的Vector的最大区别应该就是线程安全了,ArrayList不能保证线程安全,但我们也可以通过其

  • 自定义Android六边形进度条(附源码)

    本文实例讲述了Android自定义圆形进度条,分享给大家供大家参考.具体如下: 大家也可以参考这两篇文章进行学习: <自定义Android圆形进度条(附源码)>   <Android带进度的圆形进度条> 运行效果截图如下: 主要代码: package com.sxc.hexagonprogress; import java.util.Random; import android.content.Context; import android.content.res.ColorSta

  • Android Studio实现简易计算器源码

    本文实例为大家分享了Android Studio实现简易计算器的具体代码,供大家参考,具体内容如下 效果图: 源码: 布局样式: <?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="horizontal&

  • Android Jetpack组件库LiveData源码深入探究

    目录 前言 一.LiveData 二.使用案例 三.LiveData 实现原理 四.LiveData 相关源码 五.LiveData分发问题 Android Jetpack之ViewModel.LiveData Android Jetpack之LifeCycle Android Jetpack之DataBinding+ViewModel+LiveData+Room 前言 Jetpack是一个由多个技术库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种Android版本和设备中一致

  • android的消息处理机制(图文+源码分析)—Looper/Handler/Message

    这篇文章写的非常好,深入浅出,关键还是一位大三学生自己剖析的心得.这是我喜欢此文的原因.下面请看正文: 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设 计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android的消息处理机 制,我看了Looper,Handler,Message这几个类的源码,结果又

  • Retrofit之OKHttpCall源码分析

    之前在Retrofit源码初探一文中我们提出了三个问题: 什么时候开始将注解中参数拼装成http请求的信息的? 如何产生发起http请求对象的? 如何将对象转换成我们在接口中指定的返回值的? 其中第一个问题前几篇文章已经做了解答,今天我们探究下第二个问题. 之前也分析过,具体生成这个请求对象的是这句代码: OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); 代码很简单,那我们就来探究下这个Ok

随机推荐