在項目開發過程當中,博主曾寫過大量的訪問網絡重複代碼,特別是ListView adapter很難避免getView()方法不被重複調用,若是ImageView不利用緩存機制,那麼網絡的負荷就會更大!曾將訪問網絡代碼和緩存封裝起來使用,可是中間仍存在很多瑕疵!今年的Google I/O 2013上,Volley發佈了!Volley是Android平臺上的網絡通訊庫,能使網絡通訊更快,更簡單,更健壯2.Volley源碼分析
![]()
Volley特別適合數據量不大可是通訊頻繁的場景,如今android提供的源碼已經包含Volley,之後在項目中,能夠根據需求引入Volley jar文件!
(1).Volley.javaVolley.newRequestQueue()方法在一個app最好執行一次,可使用單例設計模式或者在application完成初始化,具體緣由請查看代碼分析
複製代碼
- /**
- * @author zimo2013
- * [url=home.php?mod=space&uid=189949]@See[/url] http://blog.csdn.net/zimo2013
- */
- 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);
- //cacheDir 緩存路徑 /data/data/<pkg name>/cache/<name>
- RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
- queue.start();
- /*
- * 實例化一個RequestQueue,其中start()主要完成相關工做線程的開啓,
- * 好比開啓緩存線程CacheDispatcher先完成緩存文件的掃描, 還包括開啓多個NetworkDispatcher訪問網絡線程,
- * 該多個網絡線程將從 同一個 網絡阻塞隊列中讀取消息
- *
- * 此處可見,start()已經開啓,全部咱們不用手動的去調用該方法,在start()方法中若是存在工做線程應該首先終止,並從新實例化工做線程並開啓
- * 在訪問網絡很頻繁,而又重複調用start(),勢必會致使性能的消耗;可是若是在訪問網絡不多時,調用stop()方法,中止多個線程,而後調用start(),反而又能夠提升性能,具體可折中選擇
- */
- return queue;
- }
(2).RequestQueue.java複製代碼
- /**
- * RequestQueue類存在2個很是重要的PriorityBlockingQueue類型的成員字段mCacheQueue mNetworkQueue ,該PriorityBlockingQueue爲java1.5併發庫提供的新類
- * 其中有幾個重要的方法,好比take()爲從隊列中取得對象,若是隊列不存在對象,將會被阻塞,直到隊列中存在有對象,相似於Looper.loop()
- *
- * 實例化一個request對象,調用RequestQueue.add(request),該request若是不容許被緩存,將會被添加至mNetworkQueue隊列中,待多個NetworkDispatcher線程take()取出對象
- * 若是該request能夠被緩存,該request將會被添加至mCacheQueue隊列中,待mCacheDispatcher線程從mCacheQueue.take()取出對象,
- * 若是該request在mCache中不存在匹配的緩存時,該request將會被移交添加至mNetworkQueue隊列中,待網絡訪問完成後,將關鍵頭信息添加至mCache緩存中去!
- *
- * @author zimo2013
- * @see http://blog.csdn.net/zimo2013
- */
- public void start() {
- stop();
- 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();
- }
- }
(3).CacheDispatcher.java
複製代碼
- /**
- * @author zimo2013
- * @see http://blog.csdn.net/zimo2013
- */
- @Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- //緩存初始化,會遍歷整個緩存文件夾
- mCache.initialize();
- {
- //執行代碼
- /*if (!mRootDirectory.exists()) {
- if (!mRootDirectory.mkdirs()) {
- VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
- }
- return;
- }
- File[] files = mRootDirectory.listFiles();
- if (files == null) {
- return;
- }
- for (File file : files) {
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(file);
- CacheHeader entry = CacheHeader.readHeader(fis);
- entry.size = file.length();
- putEntry(entry.key, entry);
- } catch (IOException e) {
- if (file != null) {
- file.delete();
- }
- } finally {
- try {
- if (fis != null) {
- fis.close();
- }
- } catch (IOException ignored) { }
- }
- }*/
- }
- while (true) {
- try {
- //該方法可能會被阻塞
- final Request request = mCacheQueue.take();
- Cache.Entry entry = mCache.get(request.getCacheKey());
- if (entry == null) {
- //緩存不存在,則將該request添加至網絡隊列中
- mNetworkQueue.put(request);
- continue;
- }
- //是否已通過期
- if (entry.isExpired()) {
- request.setCacheEntry(entry);
- mNetworkQueue.put(request);
- continue;
- }
- Response<?> response = request.parseNetworkResponse(
- new NetworkResponse(entry.data, entry.responseHeaders));
- //存在緩存,執行相關操做
- } catch (InterruptedException e) {
- }
- }
- }
(4).NetworkDispatcher.java
複製代碼
- /**
- * @author zimo2013
- * @see http://blog.csdn.net/zimo2013
- */
- @Override
- public void run() {
- Request request;
- while (true) {
- try {
- //可能會被
- request = mQueue.take();
- } catch (InterruptedException e) {
- // We may have been interrupted because it was time to quit.
- if (mQuit) {
- return;
- }
- continue;
- }
- try {
- //訪問網絡,獲得數據
- NetworkResponse networkResponse = mNetwork.performRequest(request);
- if (networkResponse.notModified && request.hasHadResponseDelivered()) {
- request.finish("not-modified");
- continue;
- }
- // Parse the response here on the worker thread.
- Response<?> response = request.parseNetworkResponse(networkResponse);
- // 寫入緩存
- if (request.shouldCache() && response.cacheEntry != null) {
- mCache.put(request.getCacheKey(), response.cacheEntry);
- request.addMarker("network-cache-written");
- }
- // Post the response back.
- request.markDelivered();
- mDelivery.postResponse(request, response);
- } catch (VolleyError volleyError) {
- parseAndDeliverNetworkError(request, volleyError);
- } catch (Exception e) {
- VolleyLog.e(e, "Unhandled exception %s", e.toString());
- mDelivery.postError(request, new VolleyError(e));
- }
- }
- }
(5).StringRequest.java其中在parseNetworkResponse()中,完成將byte[]到String的轉化,可能會出現字符亂碼,HttpHeaderParser.parseCharset(response.headers)方法在還沒有指定是返回爲ISO-8859-1,能夠修改成 utf-8複製代碼
- public class StringRequest extends Request<String> {
- private final Listener<String> mListener;
- /**
- * Creates a new request with the given method.
- *
- * @param method the request {@link Method} to use
- * @param url URL to fetch the string at
- * @param listener Listener to receive the String response
- * @param errorListener Error listener, or null to ignore errors
- */
- public StringRequest(int method, String url, Listener<String> listener,
- ErrorListener errorListener) {
- super(method, url, errorListener);
- mListener = listener;
- }
- public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
- this(Method.GET, url, listener, errorListener);
- }
- @Override
- protected void deliverResponse(String response) {
- mListener.onResponse(response);
- }
- @Override
- protected Response<String> parseNetworkResponse(NetworkResponse response) {
- String parsed;
- try {
- //將data字節數據轉化爲String對象
- parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
- } catch (UnsupportedEncodingException e) {
- parsed = new String(response.data);
- }
- //返回Response對象,其中該對象包含訪問相關數據
- return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
- }
- }
(6).ImageLoader.java複製代碼
- /**
- * @author zimo2013
- * @see http://blog.csdn.net/zimo2013
- */
- public ImageContainer get(String requestUrl, ImageListener imageListener,
- int maxWidth, int maxHeight) {
- throwIfNotOnMainThread();
- final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
- //從mCache獲得bitmap對象,所以能夠覆寫ImageCache,完成圖片的三級緩存,即在原有的LruCache添加一個軟引用緩存
- Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
- if (cachedBitmap != null) {
- //獲得緩存對象
- ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
- imageListener.onResponse(container, true);
- return container;
- }
- ImageContainer imageContainer =
- new ImageContainer(null, requestUrl, cacheKey, imageListener);
- // 首先更新該view,其指定了defaultImage
- imageListener.onResponse(imageContainer, true);
- // 根據能夠去檢查該請求是否已經發起過
- BatchedImageRequest request = mInFlightRequests.get(cacheKey);
- if (request != null) {
- request.addContainer(imageContainer);
- return imageContainer;
- }
- Request<?> newRequest =
- new ImageRequest(requestUrl, new Listener<Bitmap>() {
- @Override
- public void onResponse(Bitmap response) {
- //若是請求成功
- onGetImageSuccess(cacheKey, response);
- }
- }, maxWidth, maxHeight,
- Config.RGB_565, new ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- onGetImageError(cacheKey, error);
- }
- });
- //添加至請求隊列中
- mRequestQueue.add(newRequest);
- //同一添加進map集合,以方便檢查該request是否正在請求網絡,能夠節約資源
- mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));
- return imageContainer;
- }
複製代碼
- private void onGetImageSuccess(String cacheKey, Bitmap response) {
- //緩存對象
- mCache.putBitmap(cacheKey, response);
- // 請求完成,不須要檢測
- BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
- if (request != null) {
- request.mResponseBitmap = response;
- //處理結果
- batchResponse(cacheKey, request);
- }
- }
複製代碼
- private void batchResponse(String cacheKey, BatchedImageRequest request) {
- mBatchedResponses.put(cacheKey, request);
- //經過handler,發送一個操做
- if (mRunnable == null) {
- mRunnable = new Runnable() {
- @Override
- public void run() {
- for (BatchedImageRequest bir : mBatchedResponses.values()) {
- for (ImageContainer container : bir.mContainers) {
- if (container.mListener == null) {
- continue;
- }
- if (bir.getError() == null) {
- container.mBitmap = bir.mResponseBitmap;
- //更新結果
- container.mListener.onResponse(container, false);
- } else {
- container.mListener.onErrorResponse(bir.getError());
- }
- }
- }
- mBatchedResponses.clear();
- mRunnable = null;
- }
- };
- // mHandler對應的looper是MainLooper,所以被MainLooper.loop()獲得該message,故該runnable操做在主線程中執行,
- mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
- }
- }
3.總結RequestQueue類存在2個很是重要的PriorityBlockingQueue類型的成員字段mCacheQueue mNetworkQueue ,該PriorityBlockingQueue爲java1.5併發庫提供的!其中有幾個重要的方法,好比take()爲從隊列中取得對象,若是隊列不存在對象,將會被阻塞,直到隊列中存在有對象,相似於Looper.loop()。實例化一個request對象,調用RequestQueue.add(request),該request若是不容許被緩存,將會被添加至mNetworkQueue隊列中,待多個NetworkDispatcher線程從mNetworkQueue中take()取出對象。若是該request能夠被緩存,該request將會被添加至mCacheQueue隊列中,待mCacheDispatcher線程從mCacheQueue.take()取出對象,若是該request在mCache中不存在匹配的緩存時,該request將會被移交添加至mNetworkQueue隊列中,待網絡訪問完成後,將關鍵頭信息添加至mCache緩存中去,並經過ResponseDelivery主線程調用request的相關方法!Volley實例