Android画像のキャッシュ


141

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

回答:


177

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

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。誰かが私に自分のキャッシュマネージャーを書く前に私に言ってくれたらよかったのに。


1
うわー、これは信じられないほどエレガントな方法でした。ありがとうございました。それは私自身の単純なキャッシュマネージャーよりも遅いわけではなく、SDカードフォルダーのハウスキーピングを行う必要はありません。
ケビンは

11
connection.getContent()常に私のためにInputStreamを返します、何が間違っていますか?
タイラーコリアー

3
キャッシュのコンテンツに有効期限も設定できるようになれば、私の人生ははるかに簡単になります:)
Janusz

11
@Scienceprodigyは、そのBitmapLoaderが何であるかはわかりませんが、確かに私が知っている標準のAndroidライブラリにはありませんが、少なくとも正しい方向に導いてくれました。Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
Stephen Fuhry、2011

6
キャッシュを機能させるために必要な追加の手順については、以下のJoeの回答を必ず確認してください
Keith

65

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

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

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

(私はこれをコメントで投稿しますが、明らかに十分なカルマがありません。)


ここにあるファイル
Telémako

2
を使用するとHttpResponseCacheHttpResponseCache.getHitCount()0 が返されることがあります。よくわかりませんが、これは、リクエストしているWebサーバーがキャッシュヘッダーを使用していないためだと思います。とにかくキャッシュを機能させるには、を使用しますconnection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
アルマー2013年

1
Google codesearchリンクが停止しています(もう一度?)。リンクを更新してください。
Felix D.

また、この動作が修正されたかどうかはわかりません。何らかの理由で、サーバーから304を返すと、.getContent()メソッドの使用時にHUCがハングします。これは、304応答にはRFC標準による関連付けられた応答本文がないためです。
TheRealChx101

27

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

最初のアプローチを使用してそれらをアプリケーションスペースに格納する場合、特にそれらの数が大きい場合は、java.lang.ref.SoftReferenceにそれらをラップすることをお勧めします(そのため、危機時にガベージコレクションされます)。ただし、これにより再読み込みが発生する可能性があります。

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

SDカードにそれらを書き込むことはリロードを必要としません。ユーザー権限のみ。


SDまたは電話のメモリに画像を書き込むにはどうすればよいですか?
d-man

SDカードに画像を保存するには:通常のファイルI / O操作を使用して、リモートサーバーから読み取った画像ストリームをメモリにコミットするか、画像をビットマップオブジェクトに変換した場合は、Bitmap.compress()メソッドを使用できます。
Samuh

@ d-man最初にをディスクに書き込んでから、Uriパス参照ImageViewや他のカスタムビューを取得することをお勧めします。毎回compress、あなたは品質を失うことになるからです。もちろん、これは非可逆アルゴリズムにのみ当てはまります。このメソッドは、あなたもあなたを介してサーバからファイルを要求次回のファイルのハッシュを格納し、それを使用できるようになるIf-None-MatchETagヘッダを。
TheRealChx101

@ TheRealChx101は、次回サーバーからファイルをリクエストするときに、If-None-MatchヘッダーとETagヘッダーを使用して何を意味するのかを理解するのに役立ちますか?基本的に、定義されたローカルキャッシュを使用するために画像を残す必要があるソリューションアプローチを探していますまたは、これが達成できない場合は、URLのコンテンツが変更されるたびに、最新のコンテンツでアプリケーションに反映し、それをキャッシュする必要があります。
CoDe、

@code訪問今のところ、このリンク、android.jlelse.eu/...
TheRealChx101

27

LruCache画像を効率的にキャッシュするために使用します。Android開発者サイトLruCacheから読むことができます

私は画像のダウンロードと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から画像をロードする場合。の2番目のコンストラクタを使用しますが、最初のコンストラクタを使用DownloadImageTaskして画像を表示したい場合(たとえば、画像があり、「アダプタ」から画像を設定している場合)AdapterDownloadImageTaskListView

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

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とその使用法ています:)

編集:

今日は、として知られている非常に有名なライブラリがあるPicassoGlideAndroidアプリで非常に効率的に負荷の画像に使用することができます。この非常にシンプルで便利なライブラリPicasso for androidGlide For Androidをお試しください。キャッシュイメージについて心配する必要はありません。

Picassoを使用すると、アプリケーションに手間をかけずに画像を読み込むことができます。多くの場合、1行のコードで読み込みます。

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

グライドとピカソの違いについてはブログをご覧ください


3
優れた回答と説明!オフラインでも機能し、Android LruCacheを使用するため、これが最良のソリューションだと思います。統合するのにより多くの労力を必要とするジョーの追加があっても、エドローランドのソリューションは飛行機モードでは機能しないことがわかりました。ところで、Androidやネットワークは、何もしなくてもかなりの量のキャッシュを提供しているようです。(1つのマイナーなnit:サンプルの使用getImageFromWareHouseの場合、「H」は小文字にする必要があります。)ありがとうございます。
エドウィンエバンス

1
素晴らしい説明:)
XtreemDeveloper '10

getImage()メソッド、特にそれが画像サイズに対して何をするのか、それがどのように発生するのかを説明できますか?たとえば、なぜ内部で関数を再度呼び出すのか、またどのように機能するのかわかりません。
Greyshack、2015

1
if(cache == null)私の問題を解決したことに賛成してください!:)
MR。ガルシア

1
最後に私の編集済み回答も参照してください。今日、ほとんどの開発者が使用している有名なライブラリーについて言及しました。それらのピカソを試してください:square.github.io/picassoおよびGlide:futurestud.io/blog/glide-getting-started
Zubair Ahmed

18

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

//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" />

10
JPEGをデコードしてから再エンコードするのはなぜですか?URLをバイト配列にダウンロードし、そのバイト配列を使用してビットマップを作成し、ファイルに書き出す方が適切です。JPEGをデコードして再エンコードするたびに、画質が低下します。
CommonsWare、2009

2
フェアポイントは、スピードよりも何よりも多かった。ただし、バイト配列として保存され、ソースファイルがJPEGでない場合、ファイルを変換する必要はありませんか?SDKの「decodeByteArray」が「デコードされたビットマップ、または画像データをデコードできなかった場合はnull」を返すため、常に画像データをデコードしているので、再エンコードする必要がないと思いますか?
Ljdawson、2009

効率といえば、FileOutputStreamを渡す代わりにBufferedOutputStreamを渡すと効率的ではないでしょうか。
Samuh

1
SDカードに画像をキャッシュすることはお勧めしません。アプリケーションがアンインストールされると、画像は削除されず、SDカードが無駄なゴミでいっぱいになります。アプリケーションのキャッシュディレクトリに画像を保存することをお
勧めします

APKの制限が50 MBになった今、SDカードへのキャッシュが開発者にとって唯一の方法かもしれません。
Ljdawson、2011年

13

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

ここdroidfuの完全な記述があるとWebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/


彼は2010年からコードをリファクタリングしています。ここにルートリンクがあります:github.com/kaeppler/droid-fu
esilver

3
そのリンクはまだ機能しません。私はAndroid-ImageManager github.com/felipecsl/Android-ImageManager
Felipe Lima

9

私はSoftReferencesを試しましたが、それらはAndroidであまりにも積極的に再利用されているので、それらを使用しても意味がないと感じました


2
同意済み
esilver

3
Googleは、DalvikのGCがSoftReferenceSの収集に非常に積極的であることを自ら確認しました。LruCache代わりに使用することをお勧めします。
2009

9

サンダーラビットが示唆したように、ImageDownloaderは仕事に最適です。私はまた、クラスのわずかなバリエーションを見つけました:

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

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


7

これはジョーの良いキャッチです。上記のコード例には2つの問題があります-1つ-応答オブジェクトがビットマップのインスタンスではありません(私の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

私はまだこれがこのルートに進むためのOKソリューションであると思います-しかし、あなたはまだキャッシュを書かなければなりません。楽しそうに聞こえますが、機能を記述したいのですが。


7

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

このセクションはかなり新しいもので、質問されたときにそこにはありませんでした。

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

最大数またはエントリを設定することでLruCacheを初期化できます。制限を超えると、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)より前のバージョンでは、ビットマップのバッキングデータはネイティブメモリに格納されていたため、予測可能な方法で解放されず、アプリケーションがメモリ制限を一時的に超えてクラッシュする可能性がありました。


3

Sergey TarasevichによるUniversal Image Loaderライブラリの使用を検討してください。付属しています:

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

Universal Image Loaderは、以下のキャッシュ構成で、ダウンロードされたイメージの詳細なキャッシュ管理を可能にします。

  • UsingFreqLimitedMemoryCache:キャッシュサイズの制限を超えると、使用頻度最も低いビットマップが削除されます。
  • LRULimitedMemoryCache:キャッシュサイズの制限を超えると、最も使用頻度の低いビットマップが削除されます。
  • FIFOLimitedMemoryCache:FIFOルールは、キャッシュサイズの制限を超えた場合の削除に使用されます。
  • LargestLimitedMemoryCache:キャッシュサイズの制限を超えると、最大のビットマップが削除されます。
  • LimitedAgeMemoryCache:キャッシュされたオブジェクトは、その経過時間が定義された値を超えると削除されます
  • 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


集中的に使用すると、Universal Image Loaderは大量のメモリリークを引き起こします。これはコードでシングルトンを使用しているために発生するのではないかと思います(例の「getInstance()」を参照)。多くの画像を読み込んでから画面を数回回転させた後、UILのOutOfMemoryErrorsが原因でアプリが常にクラッシュしました。...それは偉大なライブラリだが、それはあなたが特にないアンドロイドで、シングルトンを使用しないでくださいthetは周知の事実だ
はGeert Bellemans

1
方法がわかっている場合は、シングルトンを使用してください。:)
Renetik 2013

3

実際に機能したのは、メインクラスに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


httpresponsecacheと組み合わせてlrucacheを使用することは可能ですか
iOSAndroidWindowsMo​​bileAppsDev


1

私はしばらくこれに取り組んできました。SoftReferencesを使用した回答では、データがすぐに失われます。RequestCacheをインスタンス化することを示唆する回答は、あまりにも複雑で、完全な例を見つけることはできませんでした。

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




0

後期の答えが、私は、私はアンドロイドのためのイメージキャッシュを作成する方法のチュートリアルを書かれているので、私は自分のサイトへのリンクを追加する必要があります考え出し:http://squarewolf.nl/2010/11/android-image-cache/ アップデート:ソースが古くなっているため、ページはオフラインになっています。Ignitionを使用するためのアドバイスとして@elenasysに参加します。

この質問に遭遇し、解決策を見つけていないすべての人に、楽しんでください!= D


0

遅い答えですが、このライブラリは画像のキャッシングに非常に役立つと思います:https : //github.com/crypticminds/ColdStorage

@LoadCache(R.id.id_of_my_image_view、 "URL_to_downlaod_image_from)でImageViewに注釈を付けるだけで、画像のダウンロードと画像ビューへの読み込みが処理されます。プレースホルダー画像と読み込みアニメーションを指定することもできます。

アノテーションの詳細なドキュメントはここにあります:-https : //github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.