web-dev-qa-db-ja.com

Android画像キャッシュ

ウェブからダウンロードした画像をキャッシュするにはどうすればよいですか?

139
d-man

そして今、パンチライン:システムキャッシュを使用します。

URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
  Bitmap bitmap = (Bitmap)response;
} 

ブラウザーと共有するメモリとフラッシュROMキャッシュの両方を提供します。

grr。私が自分でキャッシュマネージャーを作成する前に、誰かが私に言っていたことを望みます。

173
edrowland

上記のエレガントなconnection.setUseCachesソリューションに関して:悲しいことに、それは追加の努力なしでは機能しません。 ResponseCache.setDefaultを使用してResponseCacheをインストールする必要があります。それ以外の場合、HttpURLConnectionsetUseCaches(true)ビットを静かに無視します。

詳細については、FileResponseCache.Javaの上部にあるコメントを参照してください。

http://libs-for-Android.googlecode.com/svn/reference/com/google/Android/filecache/FileResponseCache.html

(これをコメントで投稿しますが、SOカルマが足りないようです。)

65
Joe

それらをビットマップに変換してから、コレクション(ハッシュマップ、リストなど)に保存するか、SDカードに書き込むことができます。

最初のアプローチを使用してそれらをアプリケーション空間に保存するとき、特にそれらの数が大きい場合(危機の間にガベージコレクションされるように)Java.lang.ref.SoftReferenceで囲む必要があります。ただし、これによりリロードが発生する可能性があります。

HashMap<String,SoftReference<Bitmap>> imageCache =
        new HashMap<String,SoftReference<Bitmap>>();

sDカードに書き込むには、リロードは不要です。ただのユーザー許可。

27
Samuh

LruCacheを使用して、画像を効率的にキャッシュします。 LruCacheについては、 Androidデベロッパーサイト から読むことができます

Androidでの画像のダウンロードとキャッシュには、以下のソリューションを使用しました。以下の手順を実行できます。

ステップ1:クラス名ImagesCacheを作成します。 Singleton object for this classを使用しました

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

public class ImagesCache 
{
    private  LruCache<String, Bitmap> imagesWarehouse;

    private static ImagesCache cache;

    public static ImagesCache getInstance()
    {
        if(cache == null)
        {
            cache = new ImagesCache();
        }

        return cache;
    }

    public void initializeCache()
    {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);

        final int cacheSize = maxMemory / 8;

        System.out.println("cache size = "+cacheSize);

        imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
                {
                    protected int sizeOf(String key, Bitmap value) 
                    {
                        // The cache size will be measured in kilobytes rather than number of items.

                        int bitmapByteCount = value.getRowBytes() * value.getHeight();

                        return bitmapByteCount / 1024;
                    }
                };
    }

    public void addImageToWarehouse(String key, Bitmap value)
    {       
        if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
        {
            imagesWarehouse.put(key, value);
        }
    }

    public Bitmap getImageFromWarehouse(String key)
    {
        if(key != null)
        {
            return imagesWarehouse.get(key);
        }
        else
        {
            return null;
        }
    }

    public void removeImageFromWarehouse(String key)
    {
        imagesWarehouse.remove(key);
    }

    public void clearCache()
    {
        if(imagesWarehouse != null)
        {
            imagesWarehouse.evictAll();
        }       
    }

}

ステップ2:

ビットマップがキャッシュで使用できない場合に使用されるDownloadImageTaskという名前の別のクラスを作成し、ここからダウンロードします。

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{   
    private int inSampleSize = 0;

    private String imageUrl;

    private BaseAdapter adapter;

    private ImagesCache cache;

    private int desiredWidth, desiredHeight;

    private Bitmap image = null;

    private ImageView ivImageView;

    public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight) 
    {
        this.adapter = adapter;

        this.cache = ImagesCache.getInstance();

        this.desiredWidth = desiredWidth;

        this.desiredHeight = desiredHeight;
    }

    public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
    {
        this.cache = cache;

        this.ivImageView = ivImageView;

        this.desiredHeight = desireHeight;

        this.desiredWidth = desireWidth;
    }

    @Override
    protected Bitmap doInBackground(String... params) 
    {
        imageUrl = params[0];

        return getImage(imageUrl);
    }

    @Override
    protected void onPostExecute(Bitmap result) 
    {
        super.onPostExecute(result);

        if(result != null)
        {
            cache.addImageToWarehouse(imageUrl, result);

            if(ivImageView != null)
            {
                ivImageView.setImageBitmap(result);
            }
            else
            {

            }

            if(adapter != null)
            {
                adapter.notifyDataSetChanged();
            }
        }
    }

    private Bitmap getImage(String imageUrl)
    {   
        if(cache.getImageFromWarehouse(imageUrl) == null)
        {
            BitmapFactory.Options options = new BitmapFactory.Options();

            options.inJustDecodeBounds = true;

            options.inSampleSize = inSampleSize;

            try
            {
                URL url = new URL(imageUrl);

                HttpURLConnection connection = (HttpURLConnection)url.openConnection();

                InputStream stream = connection.getInputStream();

                image = BitmapFactory.decodeStream(stream, null, options);

                int imageWidth = options.outWidth;

                int imageHeight = options.outHeight;

                if(imageWidth > desiredWidth || imageHeight > desiredHeight)
                {   
                    System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);

                    inSampleSize = inSampleSize + 2;

                    getImage(imageUrl);
                }
                else
                {   
                    options.inJustDecodeBounds = false;

                    connection = (HttpURLConnection)url.openConnection();

                    stream = connection.getInputStream();

                    image = BitmapFactory.decodeStream(stream, null, options);

                    return image;
                }
            }

            catch(Exception e)
            {
                Log.e("getImage", e.toString());
            }
        }

        return image;
    }

ステップ3:ActivityまたはAdapterからの使用

注:ActivityクラスのURLから画像をロードする場合。 DownloadImageTaskの2番目のコンストラクタを使用しますが、Adapterの画像を表示する場合は、DownloadImageTaskの最初のコンストラクタを使用します(たとえば、ListViewに画像があり、設定している場合「アダプター」の画像)

アクティビティからの使用:

ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.

  imgTask.execute(img);
}

アダプターからの使用:

ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.

  imgTask.execute(img);
}

注:

cache.initializeCache()アプリケーションの最初のアクティビティでこのステートメントを使用できます。キャッシュを初期化したら、ImagesCacheインスタンスを使用している場合、毎回キャッシュを初期化する必要はありません。

私は物事を説明するのが得意ではありませんが、これがLruCacheとその使用法を使用してキャッシュする方法を初心者に役立つことを願っています:)

編集:

現在、Androidアプリで画像を非常に効率的に読み込むために使用できるPicassoおよびGlideとして知られる非常に有名なライブラリがあります。この非常にシンプルで便利なライブラリ Androidのピカソ および Androidのグライド を試してください。キャッシュイメージについて心配する必要はありません。

Picassoを使用すると、アプリケーションで簡単に画像を読み込むことができます(多くの場合、1行のコードで!

Glideは、ピカソと同じように、多くのソースから画像を読み込んで表示することができます。また、画像の操作を行うときにキャッシュを管理し、メモリへの影響を低く抑えます。これは公式のGoogleアプリ(Google I/O 2015のアプリなど)で使用されており、ピカソと同じくらい人気があります。このシリーズでは、ピカソに対するグライドの違いと利点を探ります。

GlideとPicassoの違い のブログにもアクセスできます。

26
Zubair Ahmed

画像をダウンロードしてメモリカードに保存するには、次のようにします。

//First create a new URL object 
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")

//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");

//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());

//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));

マニフェストにインターネット許可を追加することを忘れないでください:

<uses-permission Android:name="Android.permission.INTERNET" />
17
Ljdawson

Droidfuの画像キャッシュの使用を検討します。インメモリとディスクベースの画像キャッシュの両方を実装しています。 ImageCacheライブラリを利用するWebImageViewも取得します。

DroidfuとWebImageViewの完全な説明は次のとおりです。 http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/

13
esilver

Thunder Rabbitが示唆したように、ImageDownloaderはこの仕事に最適です。また、クラスのわずかなバリエーションを次の場所で見つけました。

http://theandroidcoder.com/utilities/Android-image-download-and-caching/

2つの主な違いは、ImageDownloaderがAndroidキャッシングシステムを使用し、変更されたものが内部および外部ストレージをキャッシングとして使用することです。著者は、Android 2.1の互換性についても言及しています。

9
EZFrag

SoftReferencesを試しましたが、Androidで積極的に回収されているため、それらを使用しても意味がないと感じました

9
2cupsOfTech

これはジョーによる良いキャッチです。上記のコード例には2つの問題があります-1つ-応答オブジェクトはBitmapのインスタンスではありません(URLがhttp:\ website.com\image.jpgのようなjpgを参照する場合、

org.Apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream)。

次に、Joeが指摘しているように、応答キャッシュが構成されていないとキャッシュは発生しません。 Android開発者は、独自のキャッシュをロールする必要があります。以下に例を示しますが、メモリにキャッシュするだけで、実際には完全なソリューションではありません。

http://codebycoffee.com/2010/06/29/using-responsecache-in-an-Android-app/

URLConnectionキャッシングAPIについて説明します。

http://download.Oracle.com/javase/6/docs/technotes/guides/net/http-cache.html

私はまだこれがこのルートに行くには良い解決策だと思います-しかし、あなたはまだキャッシュを書く必要があります。楽しいように聞こえますが、私はむしろ機能を書きたいです。

7
Peter Pascale

Androidの公式トレーニングセクションには、これに関する特別なエントリがあります。 http://developer.Android.com/training/displaying-bitmaps/cache-bitmap.html

このセクションは非常に新しく、質問が行われたときには存在していませんでした。

推奨される解決策は、LruCacheを使用することです。このクラスはHoneycombで導入されましたが、互換性ライブラリにも含まれています。

最大数またはエントリを設定することでLruCacheを初期化できます。制限を超えると自動的にソートされ、使用頻度の低いものが削除されます。それ以外は、通常のマップとして使用されます。

公式ページのサンプルコード:

private LruCache mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get memory class of this device, exceeding this amount will throw an
    // OutOfMemory exception.
    final int memClass = ((ActivityManager) context.getSystemService(
            Context.ACTIVITY_SERVICE)).getMemoryClass();

    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = 1024 * 1024 * memClass / 8;

    mMemoryCache = new LruCache(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in bytes rather than number of items.
            return bitmap.getByteCount();
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}

以前は、SoftReferencesが優れた代替手段でしたが、公式ページから引用するようになりました。

注:過去には、一般的なメモリキャッシュの実装はSoftReferenceまたはWeakReferenceビットマップキャッシュでしたが、これは推奨されません。 Android 2.3(APIレベル9)以降、ガベージコレクターはソフト/ウィークリファレンスを収集することでより積極的になり、かなり効果がなくなります。さらに、Android 3.0(APIレベル11)より前は、ビットマップのバッキングデータがネイティブメモリに保存されていたため、予測可能な方法でリリースされず、アプリケーションがメモリ制限を短時間超えてしまう可能性があり、クラッシュ。

7
shalafi

の使用を検討してくださいユニバーサルイメージローダーライブラリ by Sergey Tarasevich 。付属品:

  • マルチスレッドイメージの読み込み。スレッドプールサイズを定義できます
  • メモリ、デバイスのファイルシステムおよびSDカード上の画像キャッシュ。
  • ロードの進行状況とロードイベントをリッスンする可能性

ユニバーサルイメージローダーにより 詳細なキャッシュ管理 次のキャッシュ構成を使用した、ダウンロードされたイメージの場合:

  • UsingFreqLimitedMemoryCache: 最低頻度 キャッシュサイズの制限を超えると、使用済みのビットマップが削除されます。
  • LRULimitedMemoryCache: 少なくとも最近 キャッシュサイズの制限を超えると、使用済みのビットマップが削除されます。
  • FIFOLimitedMemoryCache:FIFOルールは、キャッシュサイズの制限を超えた場合の削除に使用されます。
  • LargestLimitedMemoryCache: 最大 キャッシュサイズの制限を超えると、ビットマップが削除されます。
  • LimitedAgeMemoryCache:Cachedオブジェクトは、 年齢が定​​義された値を超えている
  • WeakMemoryCache:ビットマップへの弱い参照のみを持つメモリキャッシュ。

簡単な使用例:

ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://site.com/image.png"; 

ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);

この例では、デフォルトのUsingFreqLimitedMemoryCacheを使用しています。

3
Gunnar Karlsson

実際に私のために働いたのは、メインクラスでResponseCacheを設定することでした:

try {
   File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
   long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
   HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { } 

そして

connection.setUseCaches(true);

ビットマップをダウンロードするとき。

http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html

3
ZamPrano

Googleのlibs-for-Androidには、画像とファイルのキャッシュを管理するための素敵なライブラリがあります。

http://code.google.com/p/libs-for-Android/

2
Taoufix

私はしばらくの間これと格闘していた。 SoftReferencesを使用した回答では、データがすぐに失われます。 RequestCacheのインスタンス化を示唆する答えは面倒であり、完全な例を見つけることはできませんでした。

しかし、 ImageDownloader.Java は私にとっては素晴らしく機能します。容量に達するか、パージタイムアウトが発生するまでHashMapを使用します。その後、物事がSoftReferenceに移動し、両方の長所を使用します。

1
Thunder Rabbit

私は点火をお勧めしますこれはDroid fuよりも優れています

https://github.com/kaeppler/ignition

https://github.com/kaeppler/ignition/wiki/Sample-applications

1
Jorgesys

遅い答えですが、Android用の画像キャッシュを作成する方法をチュートリアルに書いているので、自分のサイトにリンクを追加する必要があると考えました。 http://squarewolf.nl/2010/11/Android-image-cache/ 更新:ソースが古くなったため、ページがオフラインになりました。 @elenasysに Ignition を使用するようアドバイスします。

だから、この質問に出くわし、解決策を見つけていないすべての人々に:楽しんでください! = D

0
Thomas Vervest

後で答えますが、キャッシュを透過的に処理するAndroid Image Manager(メモリとディスク)を書きました。コードはGithubにあります https://github.com/felipecsl/Android-ImageManager

0
Felipe Lima