亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android Volley圖片加載功能詳解

 更新時(shí)間:2016年09月15日 11:32:29   作者:zinss26914  
這篇文章主要為大家詳細(xì)介紹了Android Volley圖片加載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

Gituhb項(xiàng)目

Volley源碼中文注釋項(xiàng)目我已經(jīng)上傳到github,歡迎大家fork和start.

為什么寫這篇博客

本來文章是維護(hù)在github上的,但是我在分析ImageLoader源碼過程中與到了一個(gè)問題,希望大家能幫助解答.

Volley獲取網(wǎng)絡(luò)圖片 

本來想分析Universal Image Loader的源碼,但是發(fā)現(xiàn)Volley已經(jīng)實(shí)現(xiàn)了網(wǎng)絡(luò)圖片的加載功能.其實(shí),網(wǎng)絡(luò)圖片的加載也是分幾個(gè)步驟:
1. 獲取網(wǎng)絡(luò)圖片的url.
2. 判斷該url對(duì)應(yīng)的圖片是否有本地緩存.
3. 有本地緩存,直接使用本地緩存圖片,通過異步回調(diào)給ImageView進(jìn)行設(shè)置.
4. 無本地緩存,就先從網(wǎng)絡(luò)拉取,保存在本地后,再通過異步回調(diào)給ImageView進(jìn)行設(shè)置.

我們通過Volley源碼,看一下Volley是否是按照這個(gè)步驟實(shí)現(xiàn)網(wǎng)絡(luò)圖片加載的.

ImageRequest.java

按照Volley的架構(gòu),我們首先需要構(gòu)造一個(gè)網(wǎng)絡(luò)圖片請(qǐng)求,Volley幫我們封裝了ImageRequest類,我們來看一下它的具體實(shí)現(xiàn):

/** 網(wǎng)絡(luò)圖片請(qǐng)求類. */
@SuppressWarnings("unused")
public class ImageRequest extends Request<Bitmap> {
  /** 默認(rèn)圖片獲取的超時(shí)時(shí)間(單位:毫秒) */
  public static final int DEFAULT_IMAGE_REQUEST_MS = 1000;

  /** 默認(rèn)圖片獲取的重試次數(shù). */
  public static final int DEFAULT_IMAGE_MAX_RETRIES = 2;

  private final Response.Listener<Bitmap> mListener;
  private final Bitmap.Config mDecodeConfig;
  private final int mMaxWidth;
  private final int mMaxHeight;
  private ImageView.ScaleType mScaleType;

  /** Bitmap解析同步鎖,保證同一時(shí)間只有一個(gè)Bitmap被load到內(nèi)存進(jìn)行解析,防止OOM. */
  private static final Object sDecodeLock = new Object();

  /**
   * 構(gòu)造一個(gè)網(wǎng)絡(luò)圖片請(qǐng)求.
   * @param url 圖片的url地址.
   * @param listener 請(qǐng)求成功用戶設(shè)置的回調(diào)接口.
   * @param maxWidth 圖片的最大寬度.
   * @param maxHeight 圖片的最大高度.
   * @param scaleType 圖片縮放類型.
   * @param decodeConfig 解析bitmap的配置.
   * @param errorListener 請(qǐng)求失敗用戶設(shè)置的回調(diào)接口.
   */
  public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
            ImageView.ScaleType scaleType, Bitmap.Config decodeConfig,
            Response.ErrorListener errorListener) {
    super(Method.GET, url, errorListener);
    mListener = listener;
    mDecodeConfig = decodeConfig;
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;
    mScaleType = scaleType;
  }

  /** 設(shè)置網(wǎng)絡(luò)圖片請(qǐng)求的優(yōu)先級(jí). */
  @Override
  public Priority getPriority() {
    return Priority.LOW;
  }

  @Override
  protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
    synchronized (sDecodeLock) {
      try {
        return doParse(response);
      } catch (OutOfMemoryError e) {
        return Response.error(new VolleyError(e));
      }
    }
  }

  private Response<Bitmap> doParse(NetworkResponse response) {
    byte[] data = response.data;
    BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
    Bitmap bitmap;
    if (mMaxWidth == 0 && mMaxHeight == 0) {
      decodeOptions.inPreferredConfig = mDecodeConfig;
      bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
    } else {
      // 獲取網(wǎng)絡(luò)圖片的真實(shí)尺寸.
      decodeOptions.inJustDecodeBounds = true;
      BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
      int actualWidth = decodeOptions.outWidth;
      int actualHeight = decodeOptions.outHeight;

      int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
          actualWidth, actualHeight, mScaleType);
      int desireHeight = getResizedDimension(mMaxWidth, mMaxHeight,
          actualWidth, actualHeight, mScaleType);

      decodeOptions.inJustDecodeBounds = false;
      decodeOptions.inSampleSize =
          findBestSampleSize(actualWidth, actualHeight, desiredWidth, desireHeight);
      Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);

      if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
          tempBitmap.getHeight() > desireHeight)) {
        bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desireHeight, true);
        tempBitmap.recycle();
      } else {
        bitmap = tempBitmap;
      }
    }

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

  static int findBestSampleSize(
      int actualWidth, int actualHeight, int desiredWidth, int desireHeight) {
    double wr = (double) actualWidth / desiredWidth;
    double hr = (double) actualHeight / desireHeight;
    double ratio = Math.min(wr, hr);
    float n = 1.0f;
    while ((n * 2) <= ratio) {
      n *= 2;
    }
    return (int) n;
  }

  /** 根據(jù)ImageView的ScaleType設(shè)置圖片的大小. */
  private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
                      int actualSecondary, ImageView.ScaleType scaleType) {
    // 如果沒有設(shè)置ImageView的最大值,則直接返回網(wǎng)絡(luò)圖片的真實(shí)大小.
    if ((maxPrimary == 0) && (maxSecondary == 0)) {
      return actualPrimary;
    }

    // 如果ImageView的ScaleType為FIX_XY,則將其設(shè)置為圖片最值.
    if (scaleType == ImageView.ScaleType.FIT_XY) {
      if (maxPrimary == 0) {
        return actualPrimary;
      }
      return maxPrimary;
    }

    if (maxPrimary == 0) {
      double ratio = (double)maxSecondary / (double)actualSecondary;
      return (int)(actualPrimary * ratio);
    }

    if (maxSecondary == 0) {
      return maxPrimary;
    }

    double ratio = (double) actualSecondary / (double) actualPrimary;
    int resized = maxPrimary;

    if (scaleType == ImageView.ScaleType.CENTER_CROP) {
      if ((resized * ratio) < maxSecondary) {
        resized = (int)(maxSecondary / ratio);
      }
      return resized;
    }

    if ((resized * ratio) > maxSecondary) {
      resized = (int)(maxSecondary / ratio);
    }

    return resized;
  }


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

因?yàn)閂olley本身框架已經(jīng)實(shí)現(xiàn)了對(duì)網(wǎng)絡(luò)請(qǐng)求的本地緩存,所以ImageRequest做的主要事情就是解析字節(jié)流為Bitmap,再解析過程中,通過靜態(tài)變量保證每次只解析一個(gè)Bitmap防止OOM,使用ScaleType和用戶設(shè)置的MaxWidth和MaxHeight來設(shè)置圖片大小.
總體來說,ImageRequest的實(shí)現(xiàn)非常簡(jiǎn)單,這里不做過多的講解.ImageRequest的缺陷在于:

1.需要用戶進(jìn)行過多的設(shè)置,包括圖片的大小的最大值.
2.沒有圖片的內(nèi)存緩存,因?yàn)閂olley的緩存是基于Disk的緩存,有對(duì)象反序列化的過程. 

ImageLoader.java

鑒于以上兩個(gè)缺點(diǎn),Volley又提供了一個(gè)更牛逼的ImageLoader類.其中,最關(guān)鍵的就是增加了內(nèi)存緩存.
再講解ImageLoader的源碼之前,需要先介紹一下ImageLoader的使用方法.和之前的Request請(qǐng)求不同,ImageLoader并不是new出來直接扔給RequestQueue進(jìn)行調(diào)度,它的使用方法大體分為4步:

 •創(chuàng)建一個(gè)RequestQueue對(duì)象. 

RequestQueue queue = Volley.newRequestQueue(context);

 •創(chuàng)建一個(gè)ImageLoader對(duì)象.

ImageLoader構(gòu)造函數(shù)接收兩個(gè)參數(shù),第一個(gè)是RequestQueue對(duì)象,第二個(gè)是ImageCache對(duì)象(也就是內(nèi)存緩存類,我們先不給出具體實(shí)現(xiàn),講解完ImageLoader源碼之后,我會(huì)提供一個(gè)利用LRU算法的ImageCache實(shí)現(xiàn)類) 

ImageLoader imageLoader = new ImageLoader(queue, new ImageCache() {
  @Override
  public void putBitmap(String url, Bitmap bitmap) {}
  @Override
  public Bitmap getBitmap(String url) { return null; }
});

 •獲取一個(gè)ImageListener對(duì)象. 

ImageListener listener = ImageLoader.getImageListener(imageView, R.drawable.default_imgage, R.drawable.failed_image); 

•調(diào)用ImageLoader的get方法加載網(wǎng)絡(luò)圖片. 

imageLoader.get(mImageUrl, listener, maxWidth, maxHeight, scaleType);

有了ImageLoader的使用方法,我們結(jié)合使用方法來看一下ImageLoader的源碼:

@SuppressWarnings({"unused", "StringBufferReplaceableByString"})
public class ImageLoader {
  /**
   * 關(guān)聯(lián)用來調(diào)用ImageLoader的RequestQueue.
   */
  private final RequestQueue mRequestQueue;

  /** 圖片內(nèi)存緩存接口實(shí)現(xiàn)類. */
  private final ImageCache mCache;

  /** 存儲(chǔ)同一時(shí)間執(zhí)行的相同CacheKey的BatchedImageRequest集合. */
  private final HashMap<String, BatchedImageRequest> mInFlightRequests =
      new HashMap<String, BatchedImageRequest>();

  private final HashMap<String, BatchedImageRequest> mBatchedResponses =
      new HashMap<String, BatchedImageRequest>();

  /** 獲取主線程的Handler. */
  private final Handler mHandler = new Handler(Looper.getMainLooper());


  private Runnable mRunnable;

  /** 定義圖片K1緩存接口,即將圖片的內(nèi)存緩存工作交給用戶來實(shí)現(xiàn). */
  public interface ImageCache {
    Bitmap getBitmap(String url);
    void putBitmap(String url, Bitmap bitmap);
  }

  /** 構(gòu)造一個(gè)ImageLoader. */
  public ImageLoader(RequestQueue queue, ImageCache imageCache) {
    mRequestQueue = queue;
    mCache = imageCache;
  }

  /** 構(gòu)造網(wǎng)絡(luò)圖片請(qǐng)求成功和失敗的回調(diào)接口. */
  public static ImageListener getImageListener(final ImageView view, final int defaultImageResId,
                         final int errorImageResId) {
    return new ImageListener() {
      @Override
      public void onResponse(ImageContainer response, boolean isImmediate) {
        if (response.getBitmap() != null) {
          view.setImageBitmap(response.getBitmap());
        } else if (defaultImageResId != 0) {
          view.setImageResource(defaultImageResId);
        }
      }

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

  public ImageContainer get(String requestUrl, ImageListener imageListener,
                int maxWidth, int maxHeight, ScaleType scaleType) {
    // 判斷當(dāng)前方法是否在UI線程中執(zhí)行.如果不是,則拋出異常.
    throwIfNotOnMainThread();

    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);

    // 從L1級(jí)緩存中根據(jù)key獲取對(duì)應(yīng)的Bitmap.
    Bitmap cacheBitmap = mCache.getBitmap(cacheKey);
    if (cacheBitmap != null) {
      // L1緩存命中,通過緩存命中的Bitmap構(gòu)造ImageContainer,并調(diào)用imageListener的響應(yīng)成功接口.
      ImageContainer container = new ImageContainer(cacheBitmap, requestUrl, null, null);
      // 注意:因?yàn)槟壳笆窃赨I線程中,因此這里是調(diào)用onResponse方法,并非回調(diào).
      imageListener.onResponse(container, true);
      return container;
    }

    ImageContainer imageContainer =
        new ImageContainer(null, requestUrl, cacheKey, imageListener);
    // L1緩存命中失敗,則先需要對(duì)ImageView設(shè)置默認(rèn)圖片.然后通過子線程拉取網(wǎng)絡(luò)圖片,進(jìn)行顯示.
    imageListener.onResponse(imageContainer, true);

    // 檢查cacheKey對(duì)應(yīng)的ImageRequest請(qǐng)求是否正在運(yùn)行.
    BatchedImageRequest request = mInFlightRequests.get(cacheKey);
    if (request != null) {
      // 相同的ImageRequest正在運(yùn)行,不需要同時(shí)運(yùn)行相同的ImageRequest.
      // 只需要將其對(duì)應(yīng)的ImageContainer加入到BatchedImageRequest的mContainers集合中.
      // 當(dāng)正在執(zhí)行的ImageRequest結(jié)束后,會(huì)查看當(dāng)前有多少正在阻塞的ImageRequest,
      // 然后對(duì)其mContainers集合進(jìn)行回調(diào).
      request.addContainer(imageContainer);
      return imageContainer;
    }

    // L1緩存沒命中,還是需要構(gòu)造ImageRequest,通過RequestQueue的調(diào)度來獲取網(wǎng)絡(luò)圖片
    // 獲取方法可能是:L2緩存(ps:Disk緩存)或者HTTP網(wǎng)絡(luò)請(qǐng)求.
    Request<Bitmap> newRequest =
        makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, cacheKey);
    mRequestQueue.add(newRequest);
    mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));

    return imageContainer;
  }

  /** 構(gòu)造L1緩存的key值. */
  private String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) {
    return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
        .append("#H").append(maxHeight).append("#S").append(scaleType.ordinal()).append(url)
        .toString();
  }

  public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {
    return isCached(requestUrl, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
  }

  private boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) {
    throwIfNotOnMainThread();

    String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
    return mCache.getBitmap(cacheKey) != null;
  }


  /** 當(dāng)L1緩存沒有命中時(shí),構(gòu)造ImageRequest,通過ImageRequest和RequestQueue獲取圖片. */
  protected Request<Bitmap> makeImageRequest(final String requestUrl, int maxWidth, int maxHeight,
                        ScaleType scaleType, final String cacheKey) {
    return new ImageRequest(requestUrl, new Response.Listener<Bitmap>() {
      @Override
      public void onResponse(Bitmap response) {
        onGetImageSuccess(cacheKey, response);
      }
    }, maxWidth, maxHeight, scaleType, Bitmap.Config.RGB_565, new Response.ErrorListener() {
      @Override
      public void onErrorResponse(VolleyError error) {
        onGetImageError(cacheKey, error);
      }
    });
  }

  /** 圖片請(qǐng)求失敗回調(diào).運(yùn)行在UI線程中. */
  private void onGetImageError(String cacheKey, VolleyError error) {
    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    if (request != null) {
      request.setError(error);
      batchResponse(cacheKey, request);
    }
  }

  /** 圖片請(qǐng)求成功回調(diào).運(yùn)行在UI線程中. */
  protected void onGetImageSuccess(String cacheKey, Bitmap response) {
    // 增加L1緩存的鍵值對(duì).
    mCache.putBitmap(cacheKey, response);

    // 同一時(shí)間內(nèi)最初的ImageRequest執(zhí)行成功后,回調(diào)這段時(shí)間阻塞的相同ImageRequest對(duì)應(yīng)的成功回調(diào)接口.
    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    if (request != null) {
      request.mResponseBitmap = response;
      // 將阻塞的ImageRequest進(jìn)行結(jié)果分發(fā).
      batchResponse(cacheKey, request);
    }
  }

  private void batchResponse(String cacheKey, BatchedImageRequest request) {
    mBatchedResponses.put(cacheKey, request);
    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;
        }
      };
      // Post the runnable
      mHandler.postDelayed(mRunnable, 100);
    }
  }

  private void throwIfNotOnMainThread() {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new IllegalStateException("ImageLoader must be invoked from the main thread.");
    }
  }

  /** 抽象出請(qǐng)求成功和失敗的回調(diào)接口.默認(rèn)可以使用Volley提供的ImageListener. */
  public interface ImageListener extends Response.ErrorListener {
    void onResponse(ImageContainer response, boolean isImmediate);
  }

  /** 網(wǎng)絡(luò)圖片請(qǐng)求的承載對(duì)象. */
  public class ImageContainer {
    /** ImageView需要加載的Bitmap. */
    private Bitmap mBitmap;

    /** L1緩存的key */
    private final String mCacheKey;

    /** ImageRequest請(qǐng)求的url. */
    private final String mRequestUrl;

    /** 圖片請(qǐng)求成功或失敗的回調(diào)接口類. */
    private final ImageListener mListener;

    public ImageContainer(Bitmap bitmap, String requestUrl, String cacheKey,
               ImageListener listener) {
      mBitmap = bitmap;
      mRequestUrl = requestUrl;
      mCacheKey = cacheKey;
      mListener = listener;

    }

    public void cancelRequest() {
      if (mListener == null) {
        return;
      }

      BatchedImageRequest request = mInFlightRequests.get(mCacheKey);
      if (request != null) {
        boolean canceled = request.removeContainerAndCancelIfNecessary(this);
        if (canceled) {
          mInFlightRequests.remove(mCacheKey);
        }
      } else {
        request = mBatchedResponses.get(mCacheKey);
        if (request != null) {
          request.removeContainerAndCancelIfNecessary(this);
          if (request.mContainers.size() == 0) {
            mBatchedResponses.remove(mCacheKey);
          }
        }
      }
    }

    public Bitmap getBitmap() {
      return mBitmap;
    }

    public String getRequestUrl() {
      return mRequestUrl;
    }
  }

  /**
   * CacheKey相同的ImageRequest請(qǐng)求抽象類.
   * 判定兩個(gè)ImageRequest相同包括:
   * 1. url相同.
   * 2. maxWidth和maxHeight相同.
   * 3. 顯示的scaleType相同.
   * 同一時(shí)間可能有多個(gè)相同CacheKey的ImageRequest請(qǐng)求,由于需要返回的Bitmap都一樣,所以用BatchedImageRequest
   * 來實(shí)現(xiàn)該功能.同一時(shí)間相同CacheKey的ImageRequest只能有一個(gè).
   * 為什么不使用RequestQueue的mWaitingRequestQueue來實(shí)現(xiàn)該功能?
   * 答:是因?yàn)閮H靠URL是沒法判斷兩個(gè)ImageRequest相等的.
   */
  private class BatchedImageRequest {
    /** 對(duì)應(yīng)的ImageRequest請(qǐng)求. */
    private final Request<?> mRequest;

    /** 請(qǐng)求結(jié)果的Bitmap對(duì)象. */
    private Bitmap mResponseBitmap;

    /** ImageRequest的錯(cuò)誤. */
    private VolleyError mError;

    /** 所有相同ImageRequest請(qǐng)求結(jié)果的封裝集合. */
    private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>();

    public BatchedImageRequest(Request<?> request, ImageContainer container) {
      mRequest = request;
      mContainers.add(container);
    }

    public VolleyError getError() {
      return mError;
    }

    public void setError(VolleyError error) {
      mError = error;
    }

    public void addContainer(ImageContainer container) {
      mContainers.add(container);
    }

    public boolean removeContainerAndCancelIfNecessary(ImageContainer container) {
      mContainers.remove(container);
      if (mContainers.size() == 0) {
        mRequest.cancel();
        return true;
      }
      return false;
    }
  }
}

重大疑問

個(gè)人對(duì)Imageloader的源碼有兩個(gè)重大疑問?

 •batchResponse方法的實(shí)現(xiàn). 

我很奇怪,為什么ImageLoader類里面要有一個(gè)HashMap來保存BatchedImageRequest集合呢?

 private final HashMap<String, BatchedImageRequest> mBatchedResponses =
    new HashMap<String, BatchedImageRequest>();

畢竟batchResponse是在特定的ImageRequest執(zhí)行成功的回調(diào)中被調(diào)用的,調(diào)用代碼如下:

  protected void onGetImageSuccess(String cacheKey, Bitmap response) {
    // 增加L1緩存的鍵值對(duì).
    mCache.putBitmap(cacheKey, response);

    // 同一時(shí)間內(nèi)最初的ImageRequest執(zhí)行成功后,回調(diào)這段時(shí)間阻塞的相同ImageRequest對(duì)應(yīng)的成功回調(diào)接口.
    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    if (request != null) {
      request.mResponseBitmap = response;
      // 將阻塞的ImageRequest進(jìn)行結(jié)果分發(fā).
      batchResponse(cacheKey, request);
    }
  }

從上述代碼可以看出,ImageRequest請(qǐng)求成功后,已經(jīng)從mInFlightRequests中獲取了對(duì)應(yīng)的BatchedImageRequest對(duì)象.而同一時(shí)間被阻塞的相同的ImageRequest對(duì)應(yīng)的ImageContainer都在BatchedImageRequest的mContainers集合中.
那我認(rèn)為,batchResponse方法只需要遍歷對(duì)應(yīng)BatchedImageRequest的mContainers集合即可.
但是,ImageLoader源碼中,我認(rèn)為多余的構(gòu)造了一個(gè)HashMap對(duì)象mBatchedResponses來保存BatchedImageRequest集合,然后在batchResponse方法中又對(duì)集合進(jìn)行兩層for循環(huán)各種遍歷,實(shí)在是非常詭異,求指導(dǎo).
詭異代碼如下:

  private void batchResponse(String cacheKey, BatchedImageRequest request) {
    mBatchedResponses.put(cacheKey, request);
    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;
        }
      };
      // Post the runnable
      mHandler.postDelayed(mRunnable, 100);
    }
  }

我認(rèn)為的代碼實(shí)現(xiàn)應(yīng)該是:

  private void batchResponse(String cacheKey, BatchedImageRequest request) {
    if (mRunnable == null) {
      mRunnable = new Runnable() {
        @Override
        public void run() {
          for (ImageContainer container : request.mContainers) {
            if (container.mListener == null) {
              continue;
            }

            if (request.getError() == null) {
              container.mBitmap = request.mResponseBitmap;
              container.mListener.onResponse(container, false);
            } else {
              container.mListener.onErrorResponse(request.getError());
            }
          }
          mRunnable = null;
        }
      };
      // Post the runnable
      mHandler.postDelayed(mRunnable, 100);
    }
  }

 •使用ImageLoader默認(rèn)提供的ImageListener,我認(rèn)為存在一個(gè)缺陷,即圖片閃現(xiàn)問題.當(dāng)為L(zhǎng)istView的item設(shè)置圖片時(shí),需要增加TAG判斷.因?yàn)閷?duì)應(yīng)的ImageView可能已經(jīng)被回收利用了. 

自定義L1緩存類

首先說明一下,所謂的L1和L2緩存分別指的是內(nèi)存緩存和硬盤緩存.
實(shí)現(xiàn)L1緩存,我們可以使用Android提供的Lru緩存類,示例代碼如下:

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

/** Lru算法的L1緩存實(shí)現(xiàn)類. */
@SuppressWarnings("unused")
public class ImageLruCache implements ImageLoader.ImageCache {
  private LruCache<String, Bitmap> mLruCache;

  public ImageLruCache() {
    this((int) Runtime.getRuntime().maxMemory() / 8);
  }

  public ImageLruCache(final int cacheSize) {
    createLruCache(cacheSize);
  }

  private void createLruCache(final int cacheSize) {
    mLruCache = new LruCache<String, Bitmap>(cacheSize) {
      @Override
      protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
      }
    };
  }

  @Override
  public Bitmap getBitmap(String url) {
    return mLruCache.get(url);
  }

  @Override
  public void putBitmap(String url, Bitmap bitmap) {
    mLruCache.put(url, bitmap);
  }
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論