网络框架分析 – 全是套路

前言

这几天抽时间啃完了Volley和Picasso的源码,收获颇多,所以在这里跟大家分享一下。

对于网络请求框架或者图片加载框架来说,我们的理想型大体应该是这样的:

  • 简单:框架的出现当然是为了提升我们的开发效率,使我们的开发变得简单,所以在保证质量的情况下简单是第一位的
  • 可配置:天底下没有完全相同的两片树叶,也没有完全相同的两个项目,所以某些差异应该是可配置的,比如缓存位置、缓存大小、缓存策略等等
  • 方便扩展:框架在设计的时候就要考虑到变化,并且封装起来。举个例子,比如有了更好的Http客户端,我们应该能很方便的修改并且不能对我们之前的代码产生太大影响

但万变不离其宗,这些框架的骨架其实基本上都是一样的,今天我们就来讨论下这些框架中的套路。

基本模块

既然我们说这些框架的结构其实基本上都是一样的,那么我们就先来看看它们之间类似的模块结构。

整体流程大概是这样的:

客户端请求->生成框架封装的请求类型->调度器开始处理任务->调用数据获取模块->对获取的数据进行处理->回调给客户端

生产者消费者模型

框架中请求管理和任务调度模块一般会用到生产者消费者模型。

为什么会有生产者消费者模型

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模型。

什么是生产者消费者模型

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

生产者消费者模型的使用场景

Java中的线程池类其实就是一种生产者和消费者模式的实现方式,但是实现方法更高明。生产者把任务丢给线程池,线程池创建线程并处理任务,如果将要运行的任务数大于线程池的基本线程数就把任务扔到阻塞队列里,这种做法比只使用一个阻塞队列来实现生产者和消费者模型显然要高明很多,因为消费者能够处理直接就处理掉了,这样速度更快,而生产者先存,消费者再取这种方式显然慢一些。

框架中的应用

对于上述的使用场景我们分别可以在框架中找到实现。

Volley源码中实现方式是用一个优先级阻塞队列来实现生产者消费者模型。生产者是往队列里添加数据的线程,消费者是一个默认4个元素的线程数组(不包括处理缓存的线程),来不停的取出消息处理。

而Picssso是一个比较典型的线程池实现的生产者消费者模型,这里就不做过多介绍了。

这两个框架使用的数据结构都是PriorityBlockingQueue(优先级阻塞队列),目的是为了做排序,保证优先级高的请求先被处理。

顺便说一下Android的消息处理机制其实也是一个生产者消费者模型。

一个小问题

这里博主当时想到了一个小问题:那就是唤醒消费者的时候唤醒的顺序是怎样的?

这里涉及到一个概念叫公平访问队列,所谓公平访问队列是指所有阻塞的生产者线程或者消费者线程,当队列可用是,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。

缓存

Android缓存分为内存缓存和文件缓存(磁盘缓存)。

一般网络框架是不需要处理内存缓存的,但是图片加载框架需要。在Android3.1以后,Android推出了LruCache这个内存缓存类,LruCache中的对象是强引用的。Picasso的内存缓存就是使用的LruCache实现的。对于磁盘缓存,Google提供的一种解决方案是使用DiskLruCache(DiskLruCache并没有集成到Android源码中,在Android Doc的例子中有讲解)。Picasso的磁盘缓存是基于okhttp的,使用了DiskLruCache。而Volley的磁盘缓存是在DiskBasedCache中实现得,也是基于Lru算法的。

至于其他缓存算法、缓存命中率等等概念这里我就不做过多介绍了。

异步的处理

我们知道Android是单线程模型,我们应该避免在UI线程中进行耗时操作,网络请求算是一个比较典型的耗时操作,所以网络相关的框架中都会对异步操作进行一些封装。

其实这里没什么复杂的地方,无非就是利用Handler进行线程间通信,然后配合回调机制,把结果返回到主线程里。这里可以参考我之前的文章《Android Handler 消息机制(解惑篇)》和《当观察者模式和回调机制遇上Android源码》。

我们以Volley为例来简单看一下,ExecutorDelivery类的职责是分发子线程产生的responses数据或者错误信息。初始化是在RequestQueue类里。


  1. public RequestQueue(Cache cache, Network network, int threadPoolSize) { 
  2.         this(cache, network, threadPoolSize, 
  3.                 new ExecutorDelivery(new Handler(Looper.getMainLooper()))); 
  4.     }  

这里传入的是主线程的Handler对象,而这个ExecutorDelivery对象会被传入到NetworkDispatcher和CacheDispatcher中,这两个类是继承于Thread的,负责处理队列中的请求。所以处理请求的操作是发生在子线程的。

然后我们看下ExecutorDelivery类的构造方法


  1. public ExecutorDelivery(final Handler handler) { 
  2.         // Make an Executor that just wraps the handler. 
  3.         mResponsePoster = new Executor() { 
  4.             @Override 
  5.             public void execute(Runnable command) { 
  6.                 handler.post(command); 
  7.             } 
  8.         }; 
  9.     }  

这里用Executor对Handler进行了一层包装。Volley中的responses数据或者错误信息都会通过Executor发送出去,这样消息就到了主线程中。

Picasso比Volley要稍稍复杂了一点,由Picasso会对图片进行变换等操作,属于耗时操作,所以在Picasso中请求的分发和结果的处理会单独放到一个线程中。这个线程是一个带有消息队列的线程,用来执行循环性任务,即对获取到的数据进行处理。当它对结果处理完成之后,才会通过主线程的Handler把结果发送回主线程进行显示等操作。

设计模式

优秀的框架会合理的利用设计模式,使代码易于扩展和后期的维护。这里有一些出现频率比较高的设计模式。

  • 静态工厂方法:由一个工厂对象决定创建出哪一种产品类的实例
  • 单例模式:确保有且只有一个对象被创建
  • 建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  • 外观模式:简化一群类的接口
  • 命令模式:封装请求成为对象
  • 策略模式:封装可以互选的行为,并使用委托来决定使用哪一个

框架入口

一般框架为了调用简洁,并不会让客户端直接通过new实例化一个入口对象。这里就需要用到创建型模式。

Volley的入口使用的是静态工厂方法,与Android源码中Bitmap的实例化类似,具体可以参考《Android源码中的静态工厂方法》


  1. /** 
  2.   * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. 
  3.   * 
  4.   * @param context A {@link Context} to use for creating the cache dir. 
  5.   * @return A started {@link RequestQueue} instance. 
  6.   */ 
  7. public static RequestQueue newRequestQueue(Context context) { 
  8.   return newRequestQueue(context, null); 
  9. }  

Picasso的入口方法则用到了双重锁的单例模式


  1. static volatile Picasso singleton = null; 
  2. public static Picasso with(Context context) { 
  3.     if (singleton == null) { 
  4.       synchronized (Picasso.class) { 
  5.         if (singleton == null) { 
  6.           singleton = new Builder(context).build(); 
  7.         } 
  8.       } 
  9.     } 
  10.     return singleton; 
  11.   } 

同时由于可配置项太多,所以Picasso还使用了Builder模式。

同时一些框架为了给给客户端提供一个简洁的的API,会使用外观模式定义一个高层接口,使得框架中的各个模块更加容易使用。外观模式是一种结构型模式。

外观模式可以参考《Android源码中的外观模式》

命令模式

命令模式的定义是将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在网络请求框架中都会将请求做一个封装成对象,方便传递和使用。比如Volley中的Request,Picasso中的Request和Action。

命令模式可以参考《Android源码中的命令模式》

策略模式

策略模式也是大部分框架都会用到的一个模式 ,作用是封装可以互选的行为,并使用委托来决定使用哪一个。

Volley中就大量使用了面向接口编程的编程思想。这里我们看下Volley的入口方法


  1. public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { 
  2.   //~省略部分无关代码~ 
  3.   if (stack == null) { 
  4.     if (Build.VERSION.SDK_INT >= 9) { 
  5.       stack = new HurlStack(); 
  6.     } else { 
  7.       // Prior to Gingerbread, HttpUrlConnection was unreliable. 
  8.       // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html 
  9.       stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); 
  10.     } 
  11.   } 
  12.   
  13.   Network network = new BasicNetwork(stack); 
  14.   //~省略部分无关代码~ 
  15. }  

这里会根据API版本选择不同的Http客户端,它们实现了一个共同的接口


  1. /** 
  2.  * An HTTP stack abstraction. 
  3.  */ 
  4. public interface HttpStack { 
  5.   /** 
  6.      * Performs an HTTP request with the given parameters. 
  7.      * 
  8.      * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, 
  9.      * and the Content-Type header is set to request.getPostBodyContentType().</p> 
  10.      * 
  11.      * @param request the request to perform 
  12.      * @param additionalHeaders additional headers to be sent together with 
  13.      *         {@link Request#getHeaders()} 
  14.      * @return the HTTP response 
  15.      */ 
  16.   public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) 
  17.     throws IOException, AuthFailureError; 
  18.   
  19. }  

当然我们也可以自己实现这个接口,然后把Http客户端换成okhttp。

后记

网络相关的框架套路基本上就这些了,具体细节大家可以去自己看下相关源码。如果有什么不完善或者不对的地方也请大家多指教。

本文作者:佚名

来源:51CTO

时间: 2024-09-29 12:15:06

网络框架分析 – 全是套路的相关文章

《CCNP SWITCH (642-813 )学习指南》一1.1 复杂的企业网络框架、架构和模型

1.1 复杂的企业网络框架.架构和模型 CCNP ROUTE (642-902)学习指南 本节介绍融合网络(converged network)及其中的各种数据流.为满足这种网络的需求,Cisco制定了智能信息网(Intelligent Information Network,IIN)策略,并开发了面向服务的网络架构(Service-Oriented Network Architecture,SONA)以引导企业网转向IIN.本节将介绍这两个主题. 本节还将概述Cisco企业级架构,并介绍传统的

Google官方网络框架Volley实战——QQ吉凶测试,南无阿弥陀佛!

Google官方网络框架Volley实战--QQ吉凶测试,南无阿弥陀佛! 这次我们用第三方的接口来做一个QQ吉凶的测试项目,代码依然是比较的简单 无图无真相 直接撸代码了,详细解释都已经写在注释里了 activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools&

网络流量分析神器:SecBI

本文讲的是网络流量分析神器:SecBI ,网络流量分析工具早已广为使用,可提升企业网络效率,定位未使用的容量和带宽,消除瓶颈.最近此类工具也被列入了网络安全工具范畴.这很容易理解,因为除了内部人威胁,攻击都是由外部因素发起并最终控制的.内部的恶意软件与其外部控制者之间的通信,可被流量分析工具捕获. 问题在于,虽然网络安全领域使用流量分析的逻辑很坚实,现实中却有点不一样.首先,即便是中小企业,每月产生的流量日志也有30-40亿条.没有计算机辅助的话,谁都筛不了这庞大的数据量,找不出任何有用的东西.

针对美国联邦机构的NIST网络框架将出台

3月30日讯 美国官员周三表示,美国政府的网络标准机构即将完成"NIST部门间互通报告8170",这份文件将帮助联邦机构将行业网络安全框架与政府自身特定的网络安全要求相结合. 美国国家标准和技术研究所(NIST)网络安全框架的项目经理马修-巴雷特表示,预计这份文件将在接下来两个月或更短的时间内完成. 特朗普政府目前正考虑要求联邦机构遵守NIST的网络安全框架,将其作为网络安全行政令的一部分. 针对美国联邦机构的NIST网络框架将出台-E安全 NIST网络框架于2014年第一次发布,它源

省局部署网络流量分析系统提高网络管理水平

为了加强全省地税http://www.aliyun.com/zixun/aggregation/32416.html">网络系统的监控与管理,省局经过省市两级信息部门近三个月的紧张实施,在全省地税系统部署了网络流量分析系统. 该系统包括网络管理平台.流量分析组件以及探针服务器三部分,主要有三大功能:一是可以对网络系统的运行状况进行实时监控,准确了解全省700多条广域网络线路的通断情况,以便及时解决故障和问题:二是可以对全省广域网流量进行收集和分析,对网络带宽的占用情况进行预警,防止网络拥塞

网络营销实战操盘:广州搬家公司的网络营销分析

中介交易 SEO诊断 淘宝客 云主机 技术大厅 最近在操作广州吉顺搬家公司这个案子的时候发现无意中进入了一个典型的网络营销"红海市场".原以为是最初级的网络营销市场,经过仔细考察后却发现原来是一个竞争白热化的红海市场.面对这样的"超级红海",作为策划&执行者应该如何操作呢?下面我说一下我们的经验.在接到这个案子的时候,首先查了一下这个行业在网络上的基本表现状况.主要有以下一些特征: 一.GOOGLE太垃圾咱暂且不管,我们只查百度.百度搜索核心关键词"

谈谈网站与网络营销分析的三大方面

网络|网络营销 越来越多的企业与个人认识到了开展网络营销的必要性,越来越多的网络营销服务机构也认识到,在开展网络营销之前,进行针对性的网络营销分析是很有必要的.通过网络营销分析,用数据来指导网络营销的开展步骤,能够有效提高网络营销的效率.这篇文章就是向大家简单介绍网站与网络营销分析的三大方面,从这三大方面着手能够很快的把握网站营销分析的总体轮廓. 一是网站基础分析: ① 域名定位策略分析: 俗话说"名不正,言不顺".建立网站品牌就要从选择域名开始.域名选定后要修改,就要"伤筋

Linux下的网络协议分析工具:TCPDUMP入门

TCPDUMP简介 在传统的网络分析和测试技术中,嗅探器(sniffer)是最常见,也是最重要的技术之一.sniffer工具首先是为网络管理员和网络程序员进行网络分析而设计的.对于网络管理人员来说,使用嗅探器可以随时掌握网络的实际情况,在网络性能急剧下降的时候,可以通过sniffer工具来分析原因,找出造成网络阻塞的来源.对于网络程序员来说,通过sniffer工具来调试程序. 用过windows平台上的sniffer工具(例如,netxray和sniffer pro软件)的朋友可能都知道,在共享

linux网络实现分析(2)——数据包的接收(从链路层到ip层)

linux网络实现分析(2)--数据包的接收(从链路层到ip层) --lvyilong316 任何数据包在由驱动接收进入协议栈都会经过netif_receive_skb函数,可以说这个函数是协议栈的入口.在分析这个函数前,首先介绍下三层协议在内核中的组织方式.     在Linux内核中,有两种不同目的的3层协议: (1) ptype_all管理的协议主要用于分析目的,它接收所有到达第3层协议的数据包. (2) ptype_base管理正常的3层协议,仅接收具有正确协议标志符的数据包,例如,In