自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

網(wǎng)絡(luò)框架分析 – 全是套路

移動開發(fā) Android
這幾天抽時間啃完了Volley和Picasso的源碼,收獲頗多,所以在這里跟大家分享一下。

前言

這幾天抽時間啃完了Volley和Picasso的源碼,收獲頗多,所以在這里跟大家分享一下。

對于網(wǎng)絡(luò)請求框架或者圖片加載框架來說,我們的理想型大體應(yīng)該是這樣的:

  • 簡單:框架的出現(xiàn)當(dāng)然是為了提升我們的開發(fā)效率,使我們的開發(fā)變得簡單,所以在保證質(zhì)量的情況下簡單是第一位的
  • 可配置:天底下沒有完全相同的兩片樹葉,也沒有完全相同的兩個項(xiàng)目,所以某些差異應(yīng)該是可配置的,比如緩存位置、緩存大小、緩存策略等等
  • 方便擴(kuò)展:框架在設(shè)計(jì)的時候就要考慮到變化,并且封裝起來。舉個例子,比如有了更好的Http客戶端,我們應(yīng)該能很方便的修改并且不能對我們之前的代碼產(chǎn)生太大影響

但萬變不離其宗,這些框架的骨架其實(shí)基本上都是一樣的,今天我們就來討論下這些框架中的套路。

基本模塊

既然我們說這些框架的結(jié)構(gòu)其實(shí)基本上都是一樣的,那么我們就先來看看它們之間類似的模塊結(jié)構(gòu)。 

 

 

 

整體流程大概是這樣的:

客戶端請求->生成框架封裝的請求類型->調(diào)度器開始處理任務(wù)->調(diào)用數(shù)據(jù)獲取模塊->對獲取的數(shù)據(jù)進(jìn)行處理->回調(diào)給客戶端

生產(chǎn)者消費(fèi)者模型

框架中請求管理和任務(wù)調(diào)度模塊一般會用到生產(chǎn)者消費(fèi)者模型。

為什么會有生產(chǎn)者消費(fèi)者模型

在線程世界里,生產(chǎn)者就是生產(chǎn)數(shù)據(jù)的線程,消費(fèi)者就是消費(fèi)數(shù)據(jù)的線程。在多線程開發(fā)當(dāng)中,如果生產(chǎn)者處理速度很快,而消費(fèi)者處理速度很慢,那么生產(chǎn)者就必須等待消費(fèi)者處理完,才能繼續(xù)生產(chǎn)數(shù)據(jù)。同樣的道理,如果消費(fèi)者的處理能力大于生產(chǎn)者,那么消費(fèi)者就必須等待生產(chǎn)者。為了解決這個問題于是引入了生產(chǎn)者和消費(fèi)者模型。

什么是生產(chǎn)者消費(fèi)者模型

生產(chǎn)者消費(fèi)者模式是通過一個容器來解決生產(chǎn)者和消費(fèi)者的強(qiáng)耦合問題。生產(chǎn)者和消費(fèi)者彼此之間不直接通訊,而通過阻塞隊(duì)列來進(jìn)行通訊,所以生產(chǎn)者生產(chǎn)完數(shù)據(jù)之后不用等待消費(fèi)者處理,直接扔給阻塞隊(duì)列,消費(fèi)者不找生產(chǎn)者要數(shù)據(jù),而是直接從阻塞隊(duì)列里取,阻塞隊(duì)列就相當(dāng)于一個緩沖區(qū),平衡了生產(chǎn)者和消費(fèi)者的處理能力。

生產(chǎn)者消費(fèi)者模型的使用場景

Java中的線程池類其實(shí)就是一種生產(chǎn)者和消費(fèi)者模式的實(shí)現(xiàn)方式,但是實(shí)現(xiàn)方法更高明。生產(chǎn)者把任務(wù)丟給線程池,線程池創(chuàng)建線程并處理任務(wù),如果將要運(yùn)行的任務(wù)數(shù)大于線程池的基本線程數(shù)就把任務(wù)扔到阻塞隊(duì)列里,這種做法比只使用一個阻塞隊(duì)列來實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模型顯然要高明很多,因?yàn)橄M(fèi)者能夠處理直接就處理掉了,這樣速度更快,而生產(chǎn)者先存,消費(fèi)者再取這種方式顯然慢一些。

框架中的應(yīng)用

對于上述的使用場景我們分別可以在框架中找到實(shí)現(xiàn)。

Volley源碼中實(shí)現(xiàn)方式是用一個優(yōu)先級阻塞隊(duì)列來實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型。生產(chǎn)者是往隊(duì)列里添加數(shù)據(jù)的線程,消費(fèi)者是一個默認(rèn)4個元素的線程數(shù)組(不包括處理緩存的線程),來不停的取出消息處理。

而Picssso是一個比較典型的線程池實(shí)現(xiàn)的生產(chǎn)者消費(fèi)者模型,這里就不做過多介紹了。

這兩個框架使用的數(shù)據(jù)結(jié)構(gòu)都是PriorityBlockingQueue(優(yōu)先級阻塞隊(duì)列),目的是為了做排序,保證優(yōu)先級高的請求先被處理。

順便說一下Android的消息處理機(jī)制其實(shí)也是一個生產(chǎn)者消費(fèi)者模型。

一個小問題

這里博主當(dāng)時想到了一個小問題:那就是喚醒消費(fèi)者的時候喚醒的順序是怎樣的?

這里涉及到一個概念叫公平訪問隊(duì)列,所謂公平訪問隊(duì)列是指所有阻塞的生產(chǎn)者線程或者消費(fèi)者線程,當(dāng)隊(duì)列可用是,可以按照阻塞的先后順序訪問隊(duì)列,即先阻塞的生產(chǎn)者線程,可以先往隊(duì)列里插入元素,先阻塞的消費(fèi)者線程,可以先從隊(duì)列里獲取元素。通常情況下為了保證公平性會降低吞吐量。

緩存

Android緩存分為內(nèi)存緩存和文件緩存(磁盤緩存)。

一般網(wǎng)絡(luò)框架是不需要處理內(nèi)存緩存的,但是圖片加載框架需要。在Android3.1以后,Android推出了LruCache這個內(nèi)存緩存類,LruCache中的對象是強(qiáng)引用的。Picasso的內(nèi)存緩存就是使用的LruCache實(shí)現(xiàn)的。對于磁盤緩存,Google提供的一種解決方案是使用DiskLruCache(DiskLruCache并沒有集成到Android源碼中,在Android Doc的例子中有講解)。Picasso的磁盤緩存是基于okhttp的,使用了DiskLruCache。而Volley的磁盤緩存是在DiskBasedCache中實(shí)現(xiàn)得,也是基于Lru算法的。

至于其他緩存算法、緩存命中率等等概念這里我就不做過多介紹了。

異步的處理

我們知道Android是單線程模型,我們應(yīng)該避免在UI線程中進(jìn)行耗時操作,網(wǎng)絡(luò)請求算是一個比較典型的耗時操作,所以網(wǎng)絡(luò)相關(guān)的框架中都會對異步操作進(jìn)行一些封裝。

其實(shí)這里沒什么復(fù)雜的地方,無非就是利用Handler進(jìn)行線程間通信,然后配合回調(diào)機(jī)制,把結(jié)果返回到主線程里。這里可以參考我之前的文章《Android Handler 消息機(jī)制(解惑篇)》和《當(dāng)觀察者模式和回調(diào)機(jī)制遇上Android源碼》。

我們以Volley為例來簡單看一下,ExecutorDelivery類的職責(zé)是分發(fā)子線程產(chǎn)生的responses數(shù)據(jù)或者錯誤信息。初始化是在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的,負(fù)責(zé)處理隊(duì)列中的請求。所以處理請求的操作是發(fā)生在子線程的。

然后我們看下ExecutorDelivery類的構(gòu)造方法

  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進(jìn)行了一層包裝。Volley中的responses數(shù)據(jù)或者錯誤信息都會通過Executor發(fā)送出去,這樣消息就到了主線程中。

Picasso比Volley要稍稍復(fù)雜了一點(diǎn),由Picasso會對圖片進(jìn)行變換等操作,屬于耗時操作,所以在Picasso中請求的分發(fā)和結(jié)果的處理會單獨(dú)放到一個線程中。這個線程是一個帶有消息隊(duì)列的線程,用來執(zhí)行循環(huán)性任務(wù),即對獲取到的數(shù)據(jù)進(jìn)行處理。當(dāng)它對結(jié)果處理完成之后,才會通過主線程的Handler把結(jié)果發(fā)送回主線程進(jìn)行顯示等操作。

設(shè)計(jì)模式

優(yōu)秀的框架會合理的利用設(shè)計(jì)模式,使代碼易于擴(kuò)展和后期的維護(hù)。這里有一些出現(xiàn)頻率比較高的設(shè)計(jì)模式。

  • 靜態(tài)工廠方法:由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例
  • 單例模式:確保有且只有一個對象被創(chuàng)建
  • 建造者模式:將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示
  • 外觀模式:簡化一群類的接口
  • 命令模式:封裝請求成為對象
  • 策略模式:封裝可以互選的行為,并使用委托來決定使用哪一個

框架入口

一般框架為了調(diào)用簡潔,并不會讓客戶端直接通過new實(shí)例化一個入口對象。這里就需要用到創(chuàng)建型模式。

Volley的入口使用的是靜態(tài)工廠方法,與Android源碼中Bitmap的實(shí)例化類似,具體可以參考《Android源碼中的靜態(tài)工廠方法》

  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.   } 

同時由于可配置項(xiàng)太多,所以Picasso還使用了Builder模式。

同時一些框架為了給給客戶端提供一個簡潔的的API,會使用外觀模式定義一個高層接口,使得框架中的各個模塊更加容易使用。外觀模式是一種結(jié)構(gòu)型模式。

外觀模式可以參考《Android源碼中的外觀模式》

命令模式

命令模式的定義是將一個請求封裝成一個對象,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化,對請求排隊(duì)或記錄請求日志,以及支持可撤銷的操作。在網(wǎng)絡(luò)請求框架中都會將請求做一個封裝成對象,方便傳遞和使用。比如Volley中的Request,Picasso中的Request和Action。

命令模式可以參考《Android源碼中的命令模式》

策略模式

策略模式也是大部分框架都會用到的一個模式 ,作用是封裝可以互選的行為,并使用委托來決定使用哪一個。

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

  1. public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { 
  2.   //~省略部分無關(guān)代碼~ 
  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.   //~省略部分無關(guān)代碼~ 
  15.  

這里會根據(jù)API版本選擇不同的Http客戶端,它們實(shí)現(xiàn)了一個共同的接口

  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.  

當(dāng)然我們也可以自己實(shí)現(xiàn)這個接口,然后把Http客戶端換成okhttp。

后記

網(wǎng)絡(luò)相關(guān)的框架套路基本上就這些了,具體細(xì)節(jié)大家可以去自己看下相關(guān)源碼。如果有什么不完善或者不對的地方也請大家多指教。

責(zé)任編輯:龐桂玉 來源: 安卓開發(fā)精選
相關(guān)推薦

2018-08-24 13:55:05

2023-03-27 16:21:22

2017-10-26 10:58:47

筆記本廠商售價

2021-01-28 09:20:18

網(wǎng)絡(luò)犯罪網(wǎng)絡(luò)安全監(jiān)管

2023-01-10 11:29:34

2013-01-30 09:42:46

2017-01-19 15:49:28

2021-11-10 14:48:21

深信服網(wǎng)絡(luò)安全數(shù)據(jù)

2018-10-09 15:02:49

2019-11-05 15:57:23

網(wǎng)絡(luò)安全技術(shù)防火墻

2019-04-25 14:20:56

數(shù)據(jù)分析套路工具

2020-10-09 10:00:29

網(wǎng)絡(luò)安全數(shù)字化轉(zhuǎn)型遠(yuǎn)程辦公

2022-08-03 12:01:33

WAF漏洞網(wǎng)絡(luò)攻擊

2014-04-29 09:15:00

2010-09-28 15:28:34

2009-07-16 17:01:32

ibatis dao

2021-08-11 07:22:27

Vue 技巧 開發(fā)工具

2021-08-16 07:20:47

網(wǎng)絡(luò)詐騙支付寶學(xué)生賬戶

2019-07-25 07:04:33

網(wǎng)絡(luò)安全技術(shù)物聯(lián)網(wǎng)

2010-01-11 10:41:31

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號