English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android Volleyの画像ロード機能の詳細

Gituhbプロジェクト

Volleyのソースコードの中国語コメントプロジェクトはgithubにアップロードしました。forkとstarをお待ちしています。

このブログを書く理由

この記事はgithubでメンテナンスされていましたが、ImageLoaderのソースコードを分析している過程で問題に直面しました。皆様の助けを期待しています。

Volleyがネットワーク画像を取得します 

本来はUniversal Image Loaderのソースコードを分析しようと思っていましたが、Volleyが既にネットワーク画像のロード機能を実装していることを発見しました。実際、ネットワーク画像のロードもいくつかのステップに分かれています:
1. ネットワーク画像のURLを取得します。
2. このURLに対応する画像がローカルキャッシュにあるかどうかを確認します。
3. ローカルキャッシュがある場合は、直接ローカルキャッシュの画像を使用し、非同期コールバックを通じてImageViewに設定します。
4. ローカルキャッシュがない場合は、まずネットワークから引き取ってローカルに保存し、その後、非同期コールバックを通じてImageViewに設定します。

Volleyのソースコードを通じて、Volleyがネットワーク画像のロードをこのステップに従って実装しているか確認します。

ImageRequest.java

Volleyのアーキテクチャに従って、まずネットワーク画像リクエストを構築する必要があります。VolleyはImageRequestクラスを包装しており、その具体的な実装を見てみましょう:

/** ネットワーク画像リクエストクラス. */
@SuppressWarnings("unused")
public class ImageRequest extends Request<Bitmap> {
  /** デフォルトの画像取得のタイムアウト時間(単位:ミリ秒) */
  public static final int DEFAULT_IMAGE_REQUEST_MS = 1000;
  /** デフォルトの画像取得のリトライ回数. */
  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の解析を同期するロックオブジェクト、同じ時間に1つのBitmapがメモリにloadされ解析されることを保証し、OOMを防ぎます。 */
  private static final Object sDecodeLock = new Object();
  /**
   * ネットワーク画像リクエストを構築します。
   * @param url 画像のURLアドレス。
   * @param listener リクエスト成功時のユーザー設定の回调インターフェース。
   * @param maxWidth 画像の最大幅。
   * @param maxHeight 画像の最大高さ。
   * @param scaleType 画像のスケール変更タイプ。
   * @param decodeConfig bitmapの解析設定。
   * @param errorListener リクエスト失敗時のユーザー設定の回调インターフェース。
   */
  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;
  }
  /** ネットワーク画像リクエストの優先度を設定します。 */
  @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 {
      // ネットワーク画像の実際のサイズを取得します。
      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;
  }
  /** 画像のScaleTypeに応じて画像のサイズを設定します. */
  private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
                      int actualSecondary, ImageView.ScaleType scaleType) {}}
    // ImageViewの最大値が設定されていない場合、ネットワーク画像の実際のサイズを直接返します。
    if ((maxPrimary == 0) && (maxSecondary == 0)) {
      return actualPrimary;
    }
    // ImageViewのScaleTypeがFIX_XYの場合、それを画像の最も小さいサイズに設定します。
    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);
  }
}

Volleyフレームワーク自体がネットワークリクエストのローカルキャッシュを実装しているため、ImageRequestが行う主な業務は、バイトストリームをBitmapに解析し、解析中に静的変数を使用して一度に1つのBitmapのみを解析することでOOMを防ぎ、ScaleTypeとユーザーが設定したMaxWidthとMaxHeightを使用して画像のサイズを設定します。
全体的に見て、ImageRequestの実装は非常にシンプルであり、詳細な説明は省略します。ImageRequestの欠点は以下の通りです:

1.ユーザーが多くの設定を行う必要があります。画像の大きさの最大値を含めます。
2.メモリキャッシュはありません。Volleyのキャッシュはディスクキャッシュに基づいており、オブジェクトの反序列化プロセスがあります。 

ImageLoader.java

以上の2つの欠点を克服するために、Volleyはより強力なImageLoaderクラスを提供しました。その中で最も重要なのはメモリキャッシュの追加です。
ImageLoaderのソースコードを説明する前に、まずImageLoaderの使用方法について説明します。前のRequestリクエストとは異なり、ImageLoaderは直接RequestQueueに投げ込むのではなく、使用方法は大別して以下の通りです。4歩:

 • RequestQueueオブジェクトを生成します。 

RequestQueue queue = Volley.newRequestQueue(context);

 • ImageLoaderオブジェクトを生成します。

ImageLoaderのコンストラクタは2つの引数を受け取ります。1つ目はRequestQueueオブジェクト、2つ目はImageCacheオブジェクトです(つまりメモリキャッシュクラスです。詳細な実装はここでは示しませんが、ImageLoaderのソースコードを説明した後、LRUアルゴリズムを使用するImageCacheの実装クラスを提供します)。 

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

 • ImageListenerオブジェクトを取得します。 

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

• ImageLoaderのgetメソッドを呼び出してネットワーク画像をロードします。 

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

ImageLoaderの使用方法を説明した後、その使用方法と合わせてImageLoaderのソースコードを見てみましょう。

@SuppressWarnings({"unused", "StringBufferReplaceableByString"})
public class ImageLoader {
  /**
   * ImageLoaderを呼び出すために使用されるRequestQueueを関連付け.
   */
  private final RequestQueue mRequestQueue;
  /** 画像メモリキャッシュインターフェース実装クラス. */
  private final ImageCache mCache;
  /** 同一時刻に実行される同じ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キャッシュインターフェース、画像のメモリキャッシュ処理をユーザーに実現させる. */
  public interface ImageCache {
    Bitmap getBitmap(String url);
    void putBitmap(String url, Bitmap bitmap);
  }
  /** 構築ImageLoader. */
  public ImageLoader(RequestQueue queue, ImageCache imageCache) {
    mRequestQueue = queue;
    mCache = imageCache;
  }
  /** 構築ネットワーク画像リクエストの成功と失敗のカールバックインターフェース. */
  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) {
    // 現在のメソッドがUIスレッド内で実行されているかどうかを判断します。もしでない場合は、例外を投げます。
    throwIfNotOnMainThread();
    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
    // 从L1キーに基づいてレベルキャッシュから対応するBitmapを取得します。
    Bitmap cacheBitmap = mCache.getBitmap(cacheKey);
    if (cacheBitmap != null) {
      // L1キャッシュヒットした場合、キャッシュヒットしたBitmapを使用してImageContainerを構築し、imageListenerのレスポンス成功インターフェースを呼び出します。
      ImageContainer container = new ImageContainer(cacheBitmap, requestUrl, null, null);
      // 注意:現在はUIスレッド内で、このためonResponseメソッドを呼び出し、コールバックではありません。
      imageListener.onResponse(container, true);
      return container;
    }
    ImageContainer imageContainer =
        new ImageContainer(null, requestUrl, cacheKey, imageListener);
    // L1キャッシュヒット失敗した場合、まずImageViewにデフォルト画像を設定する必要があります。その後、サブスレッドを通じてネットワーク画像を引き出し、表示します。
    imageListener.onResponse(imageContainer, true);
    // cacheKeyに対応するImageRequestのリクエストが実行中であるか確認します。
    BatchedImageRequest request = mInFlightRequests.get(cacheKey);
    if (request != null) {
      // 同じImageRequestが既に実行中であれば、同じImageRequestを同時に実行する必要はありません。
      // 必要なのは、対応するImageContainerをBatchedImageRequestのmContainersコレクションに追加することだけです。
      // 現在実行中のImageRequestが終了すると、現在どれだけのブロッキングImageRequestが存在するかを確認します。
      // それから、mContainersコレクションに対してコールバックを実行します。
      request.addContainer(imageContainer);
      return imageContainer;
    }
    // L1キャッシュがヒットしなかった場合、ImageRequestを構築し、RequestQueueのスケジューリングを通じてネットワーク画像を取得する必要があります
    // 取得方法は以下の通りです:L2缓存(ps:ディスクキャッシュ)またはHTTPネットワークリクエスト.
    Request<Bitmap> newRequest =
        makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, cacheKey);
    mRequestQueue.add(newRequest);
    mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));
    return imageContainer;
  }
  /** 构造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;
  }
  /** 当L1キャッシュがヒットしない場合、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);
      }
    });
  }
  /** 画像リクエスト失敗のカールバック。UIスレッドで実行されます。 */
  private void onGetImageError(String cacheKey, VolleyError error) {
    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    if (request != null) {
      request.setError(error);
      batchResponse(cacheKey, request);
    }
  }
  /** 画像リクエスト成功のカールバック。UIスレッドで実行されます。 */
  protected void onGetImageSuccess(String cacheKey, Bitmap response) {
    // 增加L1缓存的键值对。
    mCache.putBitmap(cacheKey, response);
    // 在同一时间内,最初的ImageRequest执行成功后,将回调这段时间内阻塞的相同ImageRequest对应的成功回调接口。
    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    if (request != null) {
      request.mResponseBitmap = response;
      // 对阻塞的ImageRequest进行结果分发。
      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;
        }
      };
      // Runnableをポストします
      mHandler.postDelayed(mRunnable, 100);
    }
  }
  private void throwIfNotOnMainThread() {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new IllegalStateException("ImageLoaderはメインスレッドから呼び出される必要があります。");
    }
  }
  /** リクエスト成功および失敗のカールバックインターフェースを抽象化します。デフォルトではVolleyが提供するImageListenerを使用できます。 */
  public interface ImageListener extends Response.ErrorListener {
    void onResponse(ImageContainer response, boolean isImmediate);
  }
  /** ネットワーク画像リクエストのキャリアオブジェクト. */
  public class ImageContainer {
    /** ImageViewが読み込む必要があるBitmap. */
    private Bitmap mBitmap;
    /** L1キャッシュのキー */
    private final String mCacheKey;
    /** ImageRequestリクエストのURL. */
    private final String mRequestUrl;
    /** 画像リクエスト成功または失敗のカールバックインターフェースクラス. */
    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リクエストの抽象クラス。
   * 二つのImageRequestが同じと判断するには、以下を含みます:
   * 1. urlが同じ.
   * 2. maxWidthとmaxHeightが同じ.
   * 3. 表示するscaleTypeが同じ.
   * 同一時間に同じCacheKeyのImageRequestが複数ある場合、返されるBitmapがすべて同じであるため、BatchedImageRequestを使用します。
   * この機能を実現するために。同一時間に同じCacheKeyのImageRequestは一つだけです。
   * なぜRequestQueueのmWaitingRequestQueueを使用してこの機能を実現しないのですか?63;
   * 答え:URLだけでは二つのImageRequestが同じかどうかを判断することができません。
   */
  private class BatchedImageRequest {
    /** 対応するImageRequestリクエスト. */
    private final Request<?> mRequest;
    /** リクエスト結果のBitmapオブジェクト. */
    private Bitmap mResponseBitmap;
    /** ImageRequestのエラー. */
    private VolleyError mError;
    /** 同じImageRequestの結果をエンキャップしたコレクションの集合。 */
    private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>();
    public BatchedImageRequest(Request<63;> 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;
    }
  }
}

大きな疑問

Imageloaderのソースコードについての二つの大きな疑問があります。63;

 •batchResponseメソッドの実装。 

私は不思議に思っています、なぜImageLoaderクラスにはBatchedImageRequestコレクションを保存するためのHashMapがあるのか?63;

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

結局、batchResponseは特定のImageRequestが成功したコールバックで呼び出されます。呼び出しコードは以下の通りです:

  protected void onGetImageSuccess(String cacheKey, Bitmap response) {
    // 增加L1缓存的键值对。
    mCache.putBitmap(cacheKey, response);
    // 在同一时间内,最初的ImageRequest执行成功后,将回调这段时间内阻塞的相同ImageRequest对应的成功回调接口。
    BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    if (request != null) {
      request.mResponseBitmap = response;
      // 对阻塞的ImageRequest进行结果分发。
      batchResponse(cacheKey, request);
    }
  }

从上述代码可以看出,ImageRequest请求成功后,已经从mInFlightRequests中获取了对应的BatchedImageRequest对象。而同一时间被阻塞的相同的ImageRequest对应的ImageContainer都在BatchedImageRequest的mContainers集合中。
我认为,batchResponse方法只需要遍历对应BatchedImageRequest的mContainers集合即可。
然而,在ImageLoader的源码中,我认为多创建了一个HashMap对象mBatchedResponses来保存BatchedImageRequest集合,然后在batchResponse方法中又对集合进行了两层for循环的遍历,这实在非常诡异,恳请指导。
以下代码颇为诡异:

  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;
        }
      };
      // Runnableをポストします
      mHandler.postDelayed(mRunnable, 100);
    }
  }

私の考えでは、コードの実装は以下の通りです:

  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;
        }
      };
      // Runnableをポストします
      mHandler.postDelayed(mRunnable, 100);
    }
  }

 •ImageLoaderがデフォルトで提供するImageListenerを使用すると、画像が一時的に表示される問題があります。ListViewのアイテムに画像を設定する場合、TAG判定を追加する必要があります。なぜなら、対応するImageViewが再利用されている可能性があるからです。 

カスタムL1キャッシュクラス

まず説明します。言うことに、L1およびL2キャッシュはメモリキャッシュとディスクキャッシュを指します。
実装L1キャッシュを使用して、Androidが提供するLruキャッシュクラスを使用できます。以下は例です:

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
/** LruアルゴリズムのL1キャッシュ実装クラス. */
@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);
  }
}

これでこの記事のすべての内容が終わります。皆様の学習に役立てば幸いですし、呐喊もののサポートを多くいただければと思います。

声明:この記事の内容はインターネットから取得しており、著作権者は所有者であり、インターネットユーザーによって自発的に貢献し、自己でアップロードされたものであり、このサイトは所有権を持ちません。また、人工的な編集は行われておらず、関連する法的責任も負いません。著作権侵害を疑う内容がある場合は、以下のメールアドレスにご連絡ください:notice#oldtoolbag.com(メールを送信する際には、#を@に置き換えてください。報告を行い、関連する証拠を提供してください。一旦確認がついたら、このサイトは侵害を疑う内容をすぐに削除します。)

おすすめ