我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

2016219163706184.png (716×343)

从上图可知,不管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 {
    
    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的缓存 网络两次响应回调移接过来,这个项目正在测试阶段,待我们项目做完小白鼠,上线稳定之后,我会把代码开源,大家敬请关注。

您可能感兴趣的文章:Android网络请求框架Retrofit详解详解Retrofit2.0 公共参数(固定参数)Retrofit实现图文上传至服务器Android Retrofit的简单介绍和使用Android Retrofit的使用详解Retrofit 源码分析初探Retrofit自定义请求参数注解的实现思路


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

面对一个项目,对于Android应用开发框架的选择,我想过三种方案: 1.使用Loader + HttpClient + GreenDao + Gson + Fragment,优点是可定制性强,由于使用Google家自己的Loader和Lo
2022-06-06

Flex开发环境和应用框架的示例分析

这篇文章将为大家详细讲解有关Flex开发环境和应用框架的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Flex开发环境AdobeFlexBuilder仍然是使用最为广泛的商业FlexIDE。它构建
2023-06-17

Flex技术框架和应用开发步骤的示例分析

这篇文章给大家分享的是有关Flex技术框架和应用开发步骤的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Flex简介Flex通常是指AdobeFlex,是最初由Macromedia公司在2004年3月发布
2023-06-17

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

在前面几篇文章中,我们详细介绍了Android系统进程间通信机制Binder的原理,并且深入分析了系统提供的Binder运行库和驱动程序的源代码。细心的读者会发现,这几篇文章分析的Binder接口都是基于C/C++语言来实现
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录