【Android框架】volley源碼分析

網絡請求是Android最常見的功能模塊,一開始我們有JDK中自帶的網絡請求框架HttpURLConnection,這是JDK1.1中就開始有的,後面有了Apache的HttpClient框架,它提供了更高效的,更豐富的網絡請求功能。後來Apache又推出了 HttpComponents框架,它性能更強,更靈活,所以HttpClient就停止維護了,有興趣的可以點擊鏈接去看看。

Volley簡介

  • Volley的在有道詞典中的翻譯是齊射、齊發的意思,只不過這裏齊發的是網絡請求
  • 引用官網的一句話介紹 Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster
  • 使用場景,依然是官方所說的:Volley適合用於填充UI的RPC(Remote Procedure Call)類型的操作,RPC字面意思就是遠程程序調用,什麼意思呢,就比如說頁面的有個搜索欄,你輸入一個字符就要自動聯想你要輸入的單詞,那麼這個聯想的功能肯定要做在服務端,所以我們就要調用服務端的這個功能,這就是遠程程序調用,然後這個調用的結果用於填充UI。而這個功能要求實時性要高,不能你單詞都寫完了,那邊請求還沒處理完呢,產品肯定會找你麻煩!所以Volley很適合用於做這方面的操作

Volley用法

在Android官網上有Volley的用法。(需要翻牆)

//創建一個請求隊列
RequestQueue requestQueue = Volley.newRequestQueue(context);

//創建一個請求
String url ="http://www.liyafeng.com";
StringRequest stringRequest = new StringRequest(url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.i(TAG, "onResponse: "+response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.i(TAG, "onErrorResponse: "+error.getMessage());
            }
        });

//最後一步,添加請求到請求隊列
requestQueue.add(stringRequest);
//你可能會說這裏只是把請求加入隊列啊,什麼時候開啓線程發出請求的呢?實際上在上面newRequestQueue的時候裏面已經幫我們調用了start()方法開啓線程去等待請求隊列中的

  是的,就是那麼清爽,Volley本身提供了StringRequest.class、JsonRequest.class和ImageRequest.class,裏面將請求回來的數據封裝成String、JsonObject和Bitmap,這是三種常見的請求結果,Volley都幫咱們封裝好了,當然你還可以根據自己的需求進行封裝,只需要繼承Request類即可。
  官網上還給了一個用Volley進行圖片加載並緩存的例子,實際上是在Volley的ImageRequest.class的基礎上又進行了封裝,加入了緩存的邏輯,用的LruCache。然後還有一些請求上的優化邏輯,比如你短時間請求同一個圖片的鏈接很多次,那麼Volley並不會發這麼多的請求,而是將這些請求記錄下來,然後當結果返回的時候,再用一個foreach調用所有請求的回調,這樣就能在邏輯上優化了請求速度。

Volley源碼

  Volley的整個包結構很簡單,com.android.volley包下主要是Volley封裝的請求邏輯,裏面還有一個子包是toolbox,顧名思義,就是工具箱的意思,看一下里面,又更進一步封裝了一些功能,比如可以進行網絡圖片請求的類ImageLoader.class,封裝好的圖片加載控件NetworkImageView.class,緩存請求結果的實現類DiskBasedCache.class等,都是可以拿來就用的,不用咱們寫重複的代碼了。
  好,接下來咱們就從分析一下上面那三行簡單的網絡請求裏面的實現到底是怎麼樣的,先看第一行代碼
RequestQueue requestQueue = Volley.newRequestQueue(context);
跳進去看看,整個Volley.class這個類就兩個方法。

//這是第一個調用的
public static RequestQueue newRequestQueue(Context context) {
       return newRequestQueue(context, null);
}
//繼續跳
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            stack = new HttpClientStack();
        }

        Network network = new BasicNetwork(stack);

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);

        queue.start();

        return queue;
    }

上面代碼是我精簡過的,看步驟
1. 選擇緩存目錄
2. 根據當前系統版本號判斷使用哪個HttpStack的實現類,HttpStack是個接口,註釋上寫着此類是 An HTTP stack abstraction,一個Http棧的抽象,就是發起Http請求的代碼要在子類中實現,那麼子類HurlStack裏面是用HttpURLConnection來實現的,而HttpClientStack就是用HttpClient來實現的,就是我們開始提到的已經停止維護的那個框架。
3. 創建一個Network對象,一句話介紹:An interface for performing requests。一個執行請求的接口,那麼我們有了HttpStack來執行請求,爲什麼還要這個類呢?每次Response回來我們得判斷狀態碼是200或者別的吧,或者返回304代表請求結果沒更新,我們可以用緩存,這些邏輯也要處理,一些異常的狀態也要處理,所以Network對象就是用來幹這個的,它並不發送真正的請求,而是做一些請求結果回來後的一些後續處理。
4. 創建RequestQueue對象:A request dispatch queue with a thread pool of dispatchers。一個使用線程池的請求調度隊列。調度其實是兩個動詞,調,就是調遣,指派。度,就是度過,由此到彼,由這到那,和‘調‘都是一個意思。很明顯,這是一個調遣Request對象的一個類,那麼爲什麼要調遣請求對象呢?比如說這個請求是要加入緩存隊列還是加入請求隊列肯定都是需要判斷的,還有要判斷是不是已經發出同樣的請求,但是結果還沒有回來,那麼這個請求就不必發出了,就等着那一個Response回來就行,這些操作都是在RequestQueue中完成的。
5. 最後 queue.start(),開啓請求線程,緩存線程,等待請求隊列中有請求加入就開始請求了。

    /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        mCacheDispatcher = new CacheDispatcher();
        mCacheDispatcher.start();

        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher();
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

  那些Dispatcher其實就是Thread的子類,裏面run方法主要做的就是等待請求隊列中有請求加入,等待的機制就是用BlockingQueue來實現,這個是JDK中concurrent(併發)包下的類,調用它的take()方法,如果隊列中有數據就取出數據,沒有數據就等待,這裏面的實現是依靠這個包下的LockSupport.class來實現的,沒有數據時,就調用park(停止)讓線程就停止執行,加入數據後,調用unpark就繼續執行,當然park和unpark的實現就是用的JNI來實現的了,以後我們還可以往底下跟。
  所以整個Volley的工作步驟就是先開啓線程處於阻塞狀態,然後你創建Reuqest對象,加入請求隊列,這時候調用unpark激活線程,內部利用HttpURLConnection來最終發出Request,然後獲取Response,再經過上層的一些列處理,最終將處理好的結果返回給StringRequest的那個回調,至此一次簡單的請求就結束了。
  所以,線程+BlockingQueue+HttpURLConnection是整個Volley的核心,線程+BlockingQueue用來調度請求,HttpURLConnection用來發出請求,我們自己也可以模仿着寫一個簡單的網絡請求框架出來。

 RequestQueue -> Dispacher(NetWork/Cache-> Network ->HttpStack -> HttpURLConnection

  最終就是 請求隊列持有(Dispacher)線程,線程中持有Network,Network中有HttpStack,HttpStack中又持有HttpURLConnection,最終HttpURLConnection發出請求獲取響應,然後又一層層返回,一層層處理,最終結果返回給Request的回調。
  有的時候面試官會問,用沒用過Volley啊,Volley的結構是什麼啊,Volley用於什麼場景啊?你就可以回答,基於緩存線程和請求線程來調度請求,底層使用HttpURLConnection/HttpClient來實現網絡請求。適用於RPC(Remote Procedure Call)類型操作的使用場景,因爲它在內存中緩存了每個請求結果,而且在請求邏輯上做了優化,所以響應速度會很快。不適合流式的操作(比如直播數據的請求)或者下載操作,因爲如果執行這些操作會很佔內存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章