開源項(xiàng)目學(xué)習(xí)之Volley(二)

Volley不僅可以進(jìn)行普通的網(wǎng)絡(luò)請(qǐng)求,還提供了一個(gè)簡(jiǎn)單的圖片加載框架,下面這段代碼展示了最普遍的使用Volley加載圖片的方法

RequestQueue queue = Volley.newRequestQueue(context);
ImageLoader loader = new ImageLoader(queue, new ImageCache() {
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
    
    }
    
    @Override
    public Bitmap getBitmap(String url) {
    
    }
});

ImageListener listener = ImageLoader.getImageListener(imageView, R.drwable.ic_default_icon, R.drawable.ic_error_icon);

loader.get("xxx.jpeg", listener, 200, 200);

除了這種用法外,另一種比較常用的是NetworkImageView,它的用法更為簡(jiǎn)單是:

  1. 在布局文件中聲明一個(gè)NetworkImageView控件
  2. 獲取控件實(shí)例
networkImageView.setDefaultImageResId(R.drawable.ic_default_icon);  
networkImageView.setErrorImageResId(R.drawable.ic_error_icon);  
networkImageView.setImageUrl("xxx.png",  
                imageLoader);

NetworkImageView內(nèi)部的實(shí)現(xiàn)其實(shí)仍然是使用了ImageLoader,所以我們主要看一下ImageLoader相關(guān)的代碼

代碼分析

1. ImageLoader.getImageListener


    public static ImageListener getImageListener(final ImageView view,
            final int defaultImageResId, final int errorImageResId) {

        return new ImageListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                if (errorImageResId != 0) {
                    view.setImageResource(errorImageResId);
                }
            }

            @Override
            public void onResponse(ImageContainer response, boolean isImmediate) {
                if (response.getBitmap() != null) {
                    view.setImageBitmap(response.getBitmap());
                } else if (defaultImageResId != 0) {
                    view.setImageResource(defaultImageResId);
                }
            }
        };
    }
  • defaultImageResId是占位圖,errorImageResId是當(dāng)出現(xiàn)錯(cuò)誤時(shí)顯示的占位圖
  • 也可以自定義一個(gè)ImgeListener

2. ImageLoader.get


    public ImageContainer get(String requestUrl, final ImageListener listener) {
        return get(requestUrl, listener, 0, 0);
    }


    public ImageContainer get(String requestUrl, ImageListener imageListener,
            int maxWidth, int maxHeight) {
        return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
    }
    
    public ImageContainer get(String requestUrl, ImageListener imageListener,
            int maxWidth, int maxHeight, ScaleType scaleType) {

        //確定是在主線程
        throwIfNotOnMainThread();
        //生成緩存key
        final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);

        //從緩存中查找Bitmap
        Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
        if (cachedBitmap != null) {
            // 如果從緩存中找到,則直接回調(diào)onResponse
            ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
            imageListener.onResponse(container, true);
            return container;
        }

        // 生成一個(gè)bitmap = null的ImageContainer, 回調(diào)onResponse, 這樣做的目的是顯示占位圖
        ImageContainer imageContainer =
                new ImageContainer(null, requestUrl, cacheKey, imageListener);
        imageListener.onResponse(imageContainer, true);

        // 從InFlightRequest中查找是否有正在請(qǐng)求的Request
        BatchedImageRequest request = mInFlightRequests.get(cacheKey);
        if (request != null) {
            // If it is, add this request to the list of listeners.
            request.addContainer(imageContainer);
            return imageContainer;
        }


        //創(chuàng)建一個(gè)新的請(qǐng)求,用于請(qǐng)求圖片
        Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
                cacheKey);
        mRequestQueue.add(newRequest);
        mInFlightRequests.put(cacheKey,
                new BatchedImageRequest(newRequest, imageContainer));
        return imageContainer;
    }
  1. 確定當(dāng)前是在主線程
  2. 生成緩存key, key的規(guī)則是“url + #W${maxWidth} + #H${maxHeight} + #S${scaleType.ordinal}”
  3. 從緩存中查找Bitmap1,如果找到則回調(diào)ImageListener.onResponse,并返回
  4. 如果緩存中沒有找到,則先生成一個(gè)bitmap = nullImageContainer,然后回調(diào)ImageListener.onResponse, 這樣做的目的在于可以先讓ImageView顯示占位圖
  5. inFlightRequests中查找是否存在key對(duì)應(yīng)的正在請(qǐng)求的Request
  6. 創(chuàng)建一個(gè)新的請(qǐng)求,添加到隊(duì)列

2.1 ImageLoader.makeImageRequest

    protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
            ScaleType scaleType, final String cacheKey) {

        return new ImageRequest(requestUrl, new Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap response) {
                onGetImageSuccess(cacheKey, response);
            }
        }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                onGetImageError(cacheKey, error);
            }
        });
    }

可以看到,實(shí)際就是構(gòu)建了一個(gè)ImageRequest

2.1.1 ImageRequest

public class ImageRequest extends Request<Bitmap> {
    ...
    @Override
    protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
        synchronized (sDecodeLock) {
            try {
                return doParse(response);
            } catch (OutOfMemoryError e) {
                return Response.error(new ParseError(e));
            }
        }
    }
    
    private Response<Bitmap> doParse(NetworkResponse response) {
        byte[] data = response.data;
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap bitmap = null;
        if (mMaxWidth == 0 && mMaxHeight == 0) {
            decodeOptions.inPreferredConfig = mDecodeConfig;
            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
        } else {
            // If we have to resize this image, first get the natural bounds.
            decodeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
            int actualWidth = decodeOptions.outWidth;
            int actualHeight = decodeOptions.outHeight;

            // 計(jì)算出合適的width和height
            int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                    actualWidth, actualHeight, mScaleType);
            int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                    actualHeight, actualWidth, mScaleType);
            
            decodeOptions.inJustDecodeBounds = false;
            // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
            //計(jì)算出合適的imSampleSize
            decodeOptions.inSampleSize =
                findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
            Bitmap tempBitmap =
                BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);

            // If necessary, scale down to the maximal acceptable size.
            if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
                    tempBitmap.getHeight() > desiredHeight)) {
                bitmap = Bitmap.createScaledBitmap(tempBitmap,
                        desiredWidth, desiredHeight, true);
                tempBitmap.recycle();
            } else {
                bitmap = tempBitmap;
            }
        }

        if (bitmap == null) {
            return Response.error(new ParseError(response));
        } else {
            return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
        }
    }
    
}

從之前的開源項(xiàng)目學(xué)習(xí)之Volley(一)可以知道,Request最重要的方法就是performNetworkResponse,ImageRequestperformNetworkResponse直接調(diào)用的私有方法doParse, 從doParse的代碼中可以看出其邏輯主要就是解析出合適的Bitmap

2.1.2 ImageLoader.onGetImageSuccess

onGetImageSuccess會(huì)再調(diào)用batchResponse, 這兩個(gè)方法的邏輯總結(jié)起來就是: 將請(qǐng)求到的Bitmap放入緩存,之后獲取到之前傳入的ImageListener,回調(diào)onResponse

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容