偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

前言

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

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

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

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

基本模塊

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

 

 

 

整體流程大概是這樣的:

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

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

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

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

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

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

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

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

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ì)列里,這種做法比只使用一個(gè)阻塞隊(duì)列來(lái)實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模型顯然要高明很多,因?yàn)橄M(fèi)者能夠處理直接就處理掉了,這樣速度更快,而生產(chǎn)者先存,消費(fèi)者再取這種方式顯然慢一些。

框架中的應(yīng)用

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

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

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

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

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

一個(gè)小問(wèn)題

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

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

緩存

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

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

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

異步的處理

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

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

我們以Volley為例來(lái)簡(jiǎn)單看一下,ExecutorDelivery類的職責(zé)是分發(fā)子線程產(chǎn)生的responses數(shù)據(jù)或者錯(cuò)誤信息。初始化是在RequestQueue類里。

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

這里傳入的是主線程的Handler對(duì)象,而這個(gè)ExecutorDelivery對(duì)象會(huì)被傳入到NetworkDispatcher和CacheDispatcher中,這兩個(gè)類是繼承于Thread的,負(fù)責(zé)處理隊(duì)列中的請(qǐng)求。所以處理請(qǐng)求的操作是發(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對(duì)Handler進(jìn)行了一層包裝。Volley中的responses數(shù)據(jù)或者錯(cuò)誤信息都會(huì)通過(guò)Executor發(fā)送出去,這樣消息就到了主線程中。

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

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

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

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

框架入口

一般框架為了調(diào)用簡(jiǎn)潔,并不會(huì)讓客戶端直接通過(guò)new實(shí)例化一個(gè)入口對(duì)象。這里就需要用到創(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.   } 

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

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

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

命令模式

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

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

策略模式

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

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

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

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

  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)這個(gè)接口,然后把Http客戶端換成okhttp。

后記

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

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

2018-08-24 13:55:05

2023-03-27 16:21:22

2017-10-26 10:58:47

筆記本廠商售價(jià)

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-16 07:20:47

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

2021-08-11 07:22:27

Vue 技巧 開(kāi)發(fā)工具

2019-07-25 07:04:33

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

2010-01-11 10:41:31

點(diǎn)贊
收藏

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