Volley框架淺析

一、概念



什麼是Volley

開發android應用很多時候都要涉及網絡操作,Android SDK中提供了HttpClient 和 HttpUrlConnection兩種方式用來處理網絡操作,但當應用比較複雜的時候需要我們編寫大量的代碼處理很多東西:圖像緩存,請求的調度等等;

而Volley框架就是爲解決這些而生的,它與2013年Google I/O大會上被提出:使得Android應用網絡操作更方便更快捷;抽象了底層Http Client等實現的細節,讓開發者更專注與產生RESTful Request。另外,Volley在不同的線程上異步執行所有請求而避免了阻塞主線程。




二、特點


如下是Volley框架的內部實現原理圖,藍色是主線程,黃色是緩存線程,綠色是網絡線程


  1. 自動調度網絡請求
  2. 多個併發的網絡連接
  3. 通過使用標準的HTTP緩存機制保持磁盤和內存響應的一致
  4. 支持請求優先級
  5. 支持取消請求的強大API,可以取消單個請求或多個
  6. 易於定製
  7. 健壯性:便於正確的更新UI和獲取數據
  8. 包含調試和追蹤工具

三、使用


1. 獲得String數據
        volleybt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //新建請求隊列
                RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

                //新建請求
                String url = "https://www.baidu.com/?tn=sitehao123_15";

                StringRequest request = new StringRequest(url, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {
                        volleytext.setText(s);
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        volleytext.setText("加載失敗"+volleyError);
                    }
                });

                requestQueue.add(request);
            }
        });

2. 獲得json數據
        json_bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1 創建一個請求隊列
                RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

                // 2 創建一個請求
                String url = "http://api.m.mtime.cn/PageSubArea/TrailerList.api";
                JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject jsonObject) {
                        volleytext.setText(jsonObject.toString());
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        volleytext.setText("請求失敗" + volleyError);
                    }
                });

                // 3 將創建的請求添加到請求隊列中
                requestQueue.add(jsonObjectRequest);
            }
        });

3. post獲得String數據
        post_bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1 創建一個請求隊列
                RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

                // 2 創建一個post請求
                String url = "http://api.m.mtime.cn/PageSubArea/TrailerList.api";
                StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {
                        volleytext.setText(s);
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        volleytext.setText("請求失敗" + volleyError);
                    }
                }) {

                };

                // 3 將post請求添加到隊列中
                requestQueue.add(stringRequest);
            }
        });

4. 自定義獲得xml數據
        xml_bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1 創建一個請求隊列
                RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
                XMLRequest xmlRequest = new XMLRequest(
                        "http://flash.weather.com.cn/wmaps/xml/china.xml",
                        new Response.Listener<XmlPullParser>() {
                            @Override
                            public void onResponse(XmlPullParser response) {
                                try {
                                    int eventType = response.getEventType();
                                    while (eventType != XmlPullParser.END_DOCUMENT) {
                                        switch (eventType) {
                                            case XmlPullParser.START_TAG:
                                                String nodeName = response.getName();
                                                if ("city".equals(nodeName)) {
                                                    String pName = response.getAttributeValue(0);
                                                    Log.d("TAG", "pName is " + pName);
                                                }
                                                break;
                                        }
                                        eventType = response.next();
                                    }
                                } catch (XmlPullParserException e) {
                                    e.printStackTrace();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("TAG", error.getMessage(), error);
                    }
                });
                requestQueue.add(xmlRequest);
            }
        });

需要重新一個xmlRequest繼承Request
public class XMLRequest extends Request<XmlPullParser> {

    private final Response.Listener<XmlPullParser> mListener;

    public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener,
                      Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    public XMLRequest(String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
        try {
            String xmlString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlString));
            return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (XmlPullParserException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(XmlPullParser response) {
        mListener.onResponse(response);
    }
}

5. 自定義獲得Gson解析後的數據
        gson_bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

                GsonRequest<Weather> gsonRequest = new GsonRequest<Weather>(
                        "http://www.weather.com.cn/data/sk/101010100.html", Weather.class,
                        new Response.Listener<Weather>() {
                            @Override
                            public void onResponse(Weather weather) {
                                WeatherInfo weatherInfo = weather.getWeatherinfo();
                                Log.d("TAG", "city is " + weatherInfo.getCity());
                                Log.d("TAG", "temp is " + weatherInfo.getTemp());
                                Log.d("TAG", "time is " + weatherInfo.getTime());
                            }
                        }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("TAG", error.getMessage(), error);
                    }
                });
                requestQueue.add(gsonRequest);
            }
        });

同樣需要定義一個GsonRequest繼承Request
public class GsonRequest<T> extends Request<T> {

    private final Response.Listener<T> mListener;

    private Gson mGson;

    private Class<T> mClass;

    public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener,
                       Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mGson = new Gson();
        mClass = clazz;
        mListener = listener;
    }

    public GsonRequest(String url, Class<T> clazz, Response.Listener<T> listener,
                       Response.ErrorListener errorListener) {
        this(Method.GET, url, clazz, listener, errorListener);
    }
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            return Response.success(mGson.fromJson(jsonString, mClass),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(T response) {
        mListener.onResponse(response);
    }
}


//用於解析的類
class Weather {

    private WeatherInfo weatherinfo;

    public WeatherInfo getWeatherinfo() {
        return weatherinfo;
    }

    public void setWeatherinfo(WeatherInfo weatherinfo) {
        this.weatherinfo = weatherinfo;
    }

}

還需要一個WeatherInfo類來解析Gson
public class WeatherInfo {

    private String city;

    private String temp;

    private String time;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getTemp() {
        return temp;
    }

    public void setTemp(String temp) {
        this.temp = temp;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }
}


四、原理

首先來梳理一下整體的流程,先建立也隊列,根據不同的系統版本建立不同的網絡初始化,接下來在這個隊列 中建立一個緩存進程和四個網絡進程,一共是五個進程,然後在就是往這個隊列中添加任務來。如果得到的請求有緩存資源就可以進行通過緩存線程執行任務了,如果沒有緩存就加入到網路線程任務中,處理完成後將response發送出去,我們在回調函數中就可以得到結果了。

(一)隊列創建

(1)網絡的選擇,是選擇HttpStack還是HttpClientStack

    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {  
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  
        String userAgent = "volley/0";  
        try {  
            String packageName = context.getPackageName();  
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);  
            userAgent = packageName + "/" + info.versionCode;  
        } catch (NameNotFoundException e) {  
        }  
        if (stack == null) {  
            if (Build.VERSION.SDK_INT >= 9) {  
                stack = new HurlStack();  
            } else {  
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
            }  
        }  
        Network network = new BasicNetwork(stack);  
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
        queue.start();  
        return queue;  
    }  


(2)建立一個緩存線程和四個網絡線程


    public void start() {  
        stop();  // Make sure any currently running dispatchers are stopped.  
        // Create the cache dispatcher and start it.  
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
        mCacheDispatcher.start();  
        // Create network dispatchers (and corresponding threads) up to the pool size.  
        for (int i = 0; i < mDispatchers.length; i++) {  
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
                    mCache, mDelivery);  
            mDispatchers[i] = networkDispatcher;  
            networkDispatcher.start();  
        }  
    }  


(二)增加請求


(1)加入隊列中

    public <T> Request<T> add(Request<T> request) {  
        // Tag the request as belonging to this queue and add it to the set of current requests.  
        request.setRequestQueue(this);  
        synchronized (mCurrentRequests) {  
            mCurrentRequests.add(request);  
        }  
        // Process requests in the order they are added.  
        request.setSequence(getSequenceNumber());  
        request.addMarker("add-to-queue");  
        // If the request is uncacheable, skip the cache queue and go straight to the network.  
        if (!request.shouldCache()) {  
            mNetworkQueue.add(request);  
            return request;  
        }  
        // Insert request into stage if there's already a request with the same cache key in flight.  
        synchronized (mWaitingRequests) {  
            String cacheKey = request.getCacheKey();  
            if (mWaitingRequests.containsKey(cacheKey)) {  
                // There is already a request in flight. Queue up.  
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);  
                if (stagedRequests == null) {  
                    stagedRequests = new LinkedList<Request<?>>();  
                }  
                stagedRequests.add(request);  
                mWaitingRequests.put(cacheKey, stagedRequests);  
                if (VolleyLog.DEBUG) {  
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);  
                }  
            } else {  
                // Insert 'null' queue for this cacheKey, indicating there is now a request in  
                // flight.  
                mWaitingRequests.put(cacheKey, null);  
                mCacheQueue.add(request);  
            }  
            return request;  
        }  
    }  

(2)通過while循環的方式進行相應,如果緩存爲空就加入到網絡線程中

    public class CacheDispatcher extends Thread {  
      
        ……  
      
        @Override  
        public void run() {  
            if (DEBUG) VolleyLog.v("start new dispatcher");  
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
            // Make a blocking call to initialize the cache.  
            mCache.initialize();  
            while (true) {  
                try {  
                    // Get a request from the cache triage queue, blocking until  
                    // at least one is available.  
                    final Request<?> request = mCacheQueue.take();  
                    request.addMarker("cache-queue-take");  
                    // If the request has been canceled, don't bother dispatching it.  
                    if (request.isCanceled()) {  
                        request.finish("cache-discard-canceled");  
                        continue;  
                    }  
                    // Attempt to retrieve this item from cache.  
                    Cache.Entry entry = mCache.get(request.getCacheKey());  
                    if (entry == null) {  
                        request.addMarker("cache-miss");  
                        // Cache miss; send off to the network dispatcher.  
                        mNetworkQueue.put(request);  
                        continue;  
                    }  
                    // If it is completely expired, just send it to the network.  
                    if (entry.isExpired()) {  
                        request.addMarker("cache-hit-expired");  
                        request.setCacheEntry(entry);  
                        mNetworkQueue.put(request);  
                        continue;  
                    }  
                    // We have a cache hit; parse its data for delivery back to the request.  
                    request.addMarker("cache-hit");  
                    Response<?> response = request.parseNetworkResponse(  
                            new NetworkResponse(entry.data, entry.responseHeaders));  
                    request.addMarker("cache-hit-parsed");  
                    if (!entry.refreshNeeded()) {  
                        // Completely unexpired cache hit. Just deliver the response.  
                        mDelivery.postResponse(request, response);  
                    } else {  
                        // Soft-expired cache hit. We can deliver the cached response,  
                        // but we need to also send the request to the network for  
                        // refreshing.  
                        request.addMarker("cache-hit-refresh-needed");  
                        request.setCacheEntry(entry);  
                        // Mark the response as intermediate.  
                        response.intermediate = true;  
                        // Post the intermediate response back to the user and have  
                        // the delivery then forward the request along to the network.  
                        mDelivery.postResponse(request, response, new Runnable() {  
                            @Override  
                            public void run() {  
                                try {  
                                    mNetworkQueue.put(request);  
                                } catch (InterruptedException e) {  
                                    // Not much we can do about this.  
                                }  
                            }  
                        });  
                    }  
                } catch (InterruptedException e) {  
                    // We may have been interrupted because it was time to quit.  
                    if (mQuit) {  
                        return;  
                    }  
                    continue;  
                }  
            }  
        }  
    }  

(3)最後進行事件的分發





尊重作者,尊重原創,參考文章:


http://blog.csdn.net/guolin_blog/article/details/17656437

http://www.cnblogs.com/zyw-205520/p/4950357.html



















發佈了228 篇原創文章 · 獲贊 45 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章