Volley全解析

什麼是Volley

  • Volley 的單詞涵義是:迸發、萬箭齊發。 是比喻的命名方式,寓指網絡請求併發效率高。

Volley 的重要方法

  • mQueue = Volley.newRequestQueue(context);
    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 {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);

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

        return queue;
    }
  • Volley 在新建RequestQueue 的時候,其實是新建了兩個 Queue, 一個CacheQueue(用於獲取獲取本地緩存結果),一個NetworkQueue(用於網絡請求); 在 queue.start(); 中, start 的是 一個 CacheDispatcher 和 4個 NetworkDispatcher, 分別作用在前面的兩個Queue

  • mQueue = Volley.newRequestQueue(context); 最好放在 Application 中,整個app 只有一個requestQueue.

  • new ExecutorDelivery(new Handler(Looper.getMainLooper())) 在RequestQueue 的參數中,有一個delivey 將handler 傳入其中,後面就是通過handler 的post 在主線程中回調 Listener 的。

  • 需要注意一下的是,網絡請求,在android 版本2.2 以前建議使用的是 httpclient, 而在 2.2 以後建議使用的是HttpUrlConnection 。 5.2 以後,若使用 HttpClient 會出現通篇的warning, 這是因爲google 已經強烈要求使用 HttpUrlConnection 了。 原因是: HttpUrlConnection 相比於 HttpClient 體積更小,API 更爲簡單,而且擴展性比HttpClient 好很多,在壓縮、緩存、省電、減少流量等各方面都要突出一些。 所以HttpUrlConnection 更適用於 android。

  • mQueue.add(new Request());
    /**
     * Adds a Request to the dispatch queue.
     * @param request The request to service
     * @return The passed-in request
     */
    public Request add(Request 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;
        }
    }
  • Request 是完全可以自定義的。Volley 提供的 Request 有 StringRequest 、 JsonObjectRequest、 JsonArrayRequest、 ImageRequest 等等。

  • 往Queue中添加Request 的過程是: 先判斷 Request 的 shouldCache 屬性,如果該屬性爲 true 則將 Request 添加到 CacheQueue 中,當然其中會有 waitingRequest 的 list 來控制相同的 請求不再重複添加。 如果該屬性爲 false, 則直接加入到 NetworkRequest 中。如果本地沒有緩存,即在CacheQueue處理過後,發現沒有數據,則重新添加到NetworkQueue 中。 在請求數據回來之後,再次判斷 showCache 來寫緩存及返回數據到UI線程。

自定義Request

  • 覆蓋 ContentType 屬性
    @Override
    public String getBodyContentType() {
//        super.getBodyContentType();
        return "application/json";
    }
  • 覆蓋 getBody() , Volley 默認的添加 content params 的方法是添加 “&”等等,與我們經常使用的是不一樣的,所以覆蓋 getParams() 還達不到我們想要的效果,所以直接覆蓋 getBody() 方法。
        if (mParams != null && !mParams.isEmpty()) {
            String ss = JSON.toJSONString(mParams);
            return ss.getBytes();
        }
        return super.getBody();
  • 添加自定義 header
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return mHeaderParams;
    }
  • 結果處理
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        // 在這處理
    }
  • 其他

默認的 get 請求, 傳入的 params 不會被處理,所以需要在外面將 url 自行拼接好再傳入 Volley 中。

   private String toGetUrl(String url, Map<String, Object> entity) {
        StringBuilder urlSB = new StringBuilder();
        if (entity != null && !entity.isEmpty()) {
            for (String key : entity.keySet()) {
                if (urlSB.length() != 0) {
                    urlSB.append("&");
                }
                urlSB.append(key + "="
                        + Uri.encode(JSON.toJSONString(entity.get(key))));
            }
        }
        if (urlSB == null || "".equals(urlSB.toString())) {
            return url;
        }
        return url + "?" + urlSB.toString();
    }
  • 常用屬性設置
        setShouldCache(false);
        setRetryPolicy(
                // 超時時間、retry次數、超時時間乘數 例如第一次5秒  第二次超時想要設置10秒, 乘數就爲2 默認爲1 可以是float
                new DefaultRetryPolicy(5000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)
        );
        setTag("tag"); // 標記 可用於cancle request

ImageLoader、 NetworkImageView

        mNetworkImageView.setDefaultImageResId(R.drawable.ic_launcher); // 默認圖片
        mNetworkImageView.setErrorImageResId(R.drawable.ic_launcher); // 發生錯誤時
        mNetworkImageView.setImageUrl(URLS[1], MyApplication.genImageLoader());// 請求地址,及 imageloader
  • MyApplication
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
        mQueue = Volley.newRequestQueue(this);
        VolleyImageCache lruImageCache = VolleyImageCache.genInstance();
        mImageLoader = new ImageLoader(mQueue,lruImageCache);
    }
  • VolleyImageCache
public class VolleyImageCache implements ImageLoader.ImageCache {

    private static LruCache<String, Bitmap> mMemoryCache;
    private static VolleyImageCache mVolleyImageCache = new VolleyImageCache();

    private VolleyImageCache() {
        // Get the Max available memory
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight();
            }
        };
    }

    public static VolleyImageCache genInstance() {
        return mVolleyImageCache;
    }

    @Override
    public Bitmap getBitmap(String arg0) {
        return mMemoryCache.get(arg0);
    }

    @Override
    public void putBitmap(String arg0, Bitmap arg1) {
        if (getBitmap(arg0) == null) {
            mMemoryCache.put(arg0, arg1);
        }
    }
}

使用Volley 上傳文件

    private MultipartEntity mReqEntity = new MutipartEntity();

    @Override
    public String getBodyContentType() {
        return mReqEntity.getContentType().getValue();
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        if (!CollectionUtils.isCollectionEmpty(mFilePath)) {
            // upload files
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                mReqEntity.writeTo(bos);
            } catch (IOException e) {
                VolleyLog.e("IOException writing to ByteArrayOutputStream");
            }
            return bos.toByteArray();
        }   
        return super.getBody(); 
    }

    private void buildMultipartEntity() {
        try {
            for (String filepath : mFilePath) {
                File file = new File(filepath);
                FileBody filePart = new FileBody(file);
                mReqEntity.addPart(file.getName(), filePart);
            }
            if (mParams != null && !mParams.isEmpty()) {
                Iterator<String> keySet = mParams.keySet().iterator();
                while (keySet.hasNext()) {
                    String key = keySet.next();
                    String ss = JSON.toJSONString(mParams.get(key));
                    mReqEntity.addPart(key, new StringBody(ss));
                }
            }
        } catch (UnsupportedEncodingException e) {
        // empty here
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章