AndroidのListViewで画像を遅延読み込みする方法


1931

ListView一部の画像とそれらの画像に関連付けられたキャプションを表示するためにを使用しています。インターネットから画像を取得しています。画像を遅延読み込みする方法はありますか?テキストが表示されている間、UIはブロックされず、画像がダウンロードされるときに表示されますか?

画像の総数は決まっていません。


10
GreenDroidのAsyncImageViewを使用できます。電話するだけsetUrl
Pascal Dimassimo 2011

7
私はそれを使用しました。それは素晴らしい実装です。AsyncImageViewが大規模なGreenDroidプロジェクトの一部であり、必要なものがAsyncImageViewだけの場合でもアプリケーションが大きくなるという悪いニュース。また、GreenDroidプロジェクトは2011
。– borisstr

5
このライブラリを試すこともできます。Android-http-image-managerは、私の意見では、画像の非同期ロードに最適です。
Ritesh Kumar Dubey

34
ピカソを使うだけで、すべてがうまくいきます。'Picasso.with(yourContext).load(img src / path / drawable here).into(imageView ie your target);' それでおしまい!
Anuj Sharma 2014

6
使用してみてください:github.com/nostra13/Android-Universal-Image-Loader、このライブラリは遅延読み込みと画像キャッシュに非常に高速で効率的です
krunal patel

回答:


1087

これが、アプリが現在表示している画像を保持するために作成したものです。ここで使用されている「Log」オブジェクトは、Android内の最後のLogクラスのカスタムラッパーであることに注意してください。

package com.wilson.android.library;

/*
 Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
    private final Map<String, Drawable> drawableMap;

    public DrawableManager() {
        drawableMap = new HashMap<String, Drawable>();
    }

    public Drawable fetchDrawable(String urlString) {
        if (drawableMap.containsKey(urlString)) {
            return drawableMap.get(urlString);
        }

        Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
        try {
            InputStream is = fetch(urlString);
            Drawable drawable = Drawable.createFromStream(is, "src");


            if (drawable != null) {
                drawableMap.put(urlString, drawable);
                Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                        + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                        + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
            } else {
              Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
            }

            return drawable;
        } catch (MalformedURLException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        } catch (IOException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        }
    }

    public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
        if (drawableMap.containsKey(urlString)) {
            imageView.setImageDrawable(drawableMap.get(urlString));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                //TODO : set imageView to a "pending" image
                Drawable drawable = fetchDrawable(urlString);
                Message message = handler.obtainMessage(1, drawable);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }

    private InputStream fetch(String urlString) throws MalformedURLException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);
        return response.getEntity().getContent();
    }
}

104
プログラムでOutOfMemoryExceptionが発生しないように、SoftReferencesを使用する必要があると思います。ヒープサイズが増加している場合、GCはソフト参照をクリアできるので、数秒後にイメージをそのリストに配置でき、ロードする前にイメージが存在するかどうかを確認して、ダウンロードせずに収集するなど、独自の世代を管理できます。そのリストからそれを取り出し、softrefリストに戻します。しばらくすると、ハードリストを消去できます:)
AZ_

36
Google Shelvesプロジェクトは、code.google.com
p /

12
drawableMapに画像が含まれている場合、フェッチスレッドを開始せずにリターンを逃しませんか?
Karussell、2011年

5
このコードにはいくつかの問題があります。まず、Drawableをキャッシュする必要があります。これにより、メモリリークが発生します:stackoverflow.com/questions/7648740/…。次に、キャッシュ自体がクリアされることはないため、キャッシュは永久に大きくなります。これは、別のメモリリークです。
satur9nine


1025

画像付きの遅延リスト(GitHubにあります)の簡単なデモを作成しまし

基本的な使い方

ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView); 

AndroidManifest.xmlに次の権限を追加することを忘れないでください。

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please

ImageLoaderのインスタンスを1つだけ作成し、アプリケーション全体で再利用します。このようにして、画像キャッシングははるかに効率的になります。

誰かに役立つかもしれません。バックグラウンドスレッドで画像をダウンロードします。画像はSDカードとメモリにキャッシュされています。キャッシュの実装は非常に単純で、デモには十分です。メモリの消費を減らすために、inSampleSizeで画像をデコードします。また、リサイクルされたビューを正しく処理しようとしています。

代替テキスト


7
ありがとう、私は私のプロジェクトでは、ほとんどどこでもあなたのコードを少し変更したバージョンを使用しています: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/...
shaman.sir

3
代わりにGilles Debunneのコードを見た人はいますか?私が目にする2つの大きな違いは、1)メモリキャッシュとSDカードの違い、および2)スレッドプールの代わりにAsyncTasksを使用することです。android-developers.blogspot.com/2010/07/...
リチャード

4
バグがあり、発生する場合があります:10-13 09:58:46.738:ERROR / AndroidRuntime(24250):キャッチされないハンドラー:キャッチされない例外が原因でスレッドのメインが終了しています10-13 09:58:46.768:ERROR / AndroidRuntime(24250) :java.lang.ArrayIndexOutOfBoundsException:配列のインデックスが範囲外です:0 10-13 09:58:46.768:ERROR / AndroidRuntime(24250):at java.util.Vector.elementAt(Vector.java:331)10-13 09: 58:46.768:ERROR / AndroidRuntime(24250):at java.util.Vector.get(Vector.java:445)10-13 09:58:46.768:ERROR / AndroidRuntime(24250):at com.my.app.image .ImageLoader $ PhotosQueue.Clean(ImageLoader.java:91)
Mikooos '13 / 10/13

64
私のような初心者のために追加したいのですが、このコードを自分のプロジェクトに追加する場合は、マニフェストに外部キャッシュのアクセス許可を追加する必要があります。ありがとう
アダム

2
@BruceHill最新のソースコードにphotosQueueはありません。非常に古いバージョンを使用しているようです。
ヒョードル

552

オープンソースの計測器Universal Image Loaderをお勧めします。もともとはFedor VlasovのプロジェクトLazyListに基づいており、それ以来大幅に改善されています。

  • マルチスレッドイメージの読み込み
  • ImageLoaderの構成を幅広く調整する可能性(スレッドエグゼキューター、ダウンローダー、デコーダー、メモリとディスクキャッシュ、画像オプションの表示など)
  • メモリおよび/またはデバイスのファイルシステム(またはSDカード)での画像キャッシュの可能性
  • ロードプロセスを「リッスン」する可能性
  • 別々のオプションですべてのディスプレイ画像呼び出しをカスタマイズする可能性
  • ウィジェットのサポート
  • Android 2.0以降のサポート


16
作成者が彼の貴重な赤ん坊として扱い、維持する素晴らしい「画像読み込み」コードを探しているなら(一度限りの解決策ではない)、それを見つけただけです。big ups Nostra
AndroidGecko 2012年

本当に素晴らしい仕事ですが、それは私のリストビューに少し遅れます。最高のパフォーマンスの 'ImageLoader'ビルドを伝えることができる場合...または、おそらく 'DisplayImageOptions'が原因です。
dinigo 2012年

私はこのグリッドビューが大好きですが、チュートリアルには使用できません。私はアンドロイドの初心者です。私はgridviewについてアプリケーションを作成しましたが、私のアプリケーションはtargetSdkVersion 15だったので、バックグラウンドスレッドでのプロセスにAsynctaskを使用する必要があります。以前はこのグリッドビューを使用していましたが、機能しません。targetSdkVersion 15でそれをどのように使用できますか?
kongkea 2012年

タグ[universal-image-loader]で
nostra13 '25 / 10/25

3
公式のandroidのドキュメントを読んで、自分でこれを実行しようとした後、このライブラリがすべての画像の読み込みの最後にあると確信しています。十分に賛成できない。
Core

159

パフォーマンスのためのマルチスレッド、Gilles Debunneによるチュートリアル。

これはAndroid開発者ブログからです。提案されたコードは以下を使用します:

  • AsyncTasks
  • ハードな、限られたサイズFIFO cache
  • ソフトで簡単にgarbage collect編集できるキャッシュ。
  • ダウンロード中のプレースホルダー Drawable

ここに画像の説明を入力してください


10
2.1でも問題なく動作します。AndroidHttpClientは使用しないでください。
Thomas Ahle、2011

2
@ thomas-ahleありがとうございます。AndroidHttpClientは2.2から実装されているため、2.1でエラーが発生するのを見ましたが、実際にそれを置き換えるものを見つけようとしませんでした。
Adinia、2011

4
@Adinaあなたは正しい、私は忘れた。ただし、レシピには通常のHttpClientで同様に実行できないものはありません。
Thomas Ahle

Androidカーネルはシステムの以前のバージョンと比較してこれらの参照を非常に熱心に収集しているため、Googleはソフト参照を推奨しないといくつかの場所で聞いています。
Muhammad Ahmed AbuTalib 2014年

109

更新:この回答は現在かなり効果がないことに注意してください。ガベージコレクターはSoftReferenceとWeakReferenceに対して積極的に動作するため、このコードは新しいアプリには適していません。 (代わりに、他の回答で提案されているUniversal Image Loaderのようなライブラリを試してください。)

コードについてはJamesに、SoftReferenceの使用を提案してくれたBao-Longに感謝します。JamesのコードにSoftReferenceの変更を実装しました。残念ながら、SoftReferencesを使用すると、画像のガベージコレクションが速すぎます。私の場合、リストのサイズが制限されており、画像が小さいため、SoftReferenceがなくても問題ありませんでした。

1年前のGoogleグループのSoftReferencesに関する議論があります:スレッドへのリンク。初期のガベージコレクションの解決策として、dalvik.system.VMRuntime.setMinimumHeapSize()を使用してVMヒープサイズを手動で設定する可能性が示唆されていますが、あまり魅力的ではありません。

public DrawableManager() {
    drawableMap = new HashMap<String, SoftReference<Drawable>>();
}

public Drawable fetchDrawable(String urlString) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null)
            return drawable;
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
    try {
        InputStream is = fetch(urlString);
        Drawable drawable = Drawable.createFromStream(is, "src");
        drawableRef = new SoftReference<Drawable>(drawable);
        drawableMap.put(urlString, drawableRef);
        if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
        return drawableRef.get();
    } catch (MalformedURLException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    } catch (IOException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    }
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null) {
            imageView.setImageDrawable(drawableRef.get());
            return;
        }
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            imageView.setImageDrawable((Drawable) message.obj);
        }
    };

    Thread thread = new Thread() {
        @Override
        public void run() {
            //TODO : set imageView to a "pending" image
            Drawable drawable = fetchDrawable(urlString);
            Message message = handler.obtainMessage(1, drawable);
            handler.sendMessage(message);
        }
    };
    thread.start();
}

3
ハード世代やソフト世代のような世代を作成できます。クリアキャッシュが3秒
以内に

developer.android.com/reference/java/lang/ref/…SoftReference docにはキャッシングに関する注意事項があります。「キャッシングのためのソフト参照の回避」セクションを参照してください。ほとんどのアプリケーションでは、ソフト参照の代わりにandroid.util.LruCacheを使用する必要があります。
vokilam 2013

私はあなたのコードに感心しますが、今新しいAndroid O / Sでは「積極的な」ガベージコレクションがあります。弱い参照を保持することは、私には意味がありません。
j2emanue 2013

@ j2emanue正解です。私の回答の冒頭で指摘しようとしたように、SoftReferencesはガベージコレクションが速すぎます。この回答を編集して、さらに明確にするようにします。
TalkLittle 2013

97

ピカソ

Jake WhartonのPicasso Libraryを使用してください。(Perfect ImageLoading LibraryはActionBarSherlockの開発者を形成しています)

Android用の強力な画像ダウンロードおよびキャッシュライブラリ。

画像は、Androidアプリケーションに待望のコンテキストと視覚的センスを追加します。Picassoを使用すると、多くの場合1行のコードで、アプリケーションに手間をかけずに画像を読み込むことができます。

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Androidでの画像読み込みの一般的な多くの落とし穴は、ピカソによって自動的に処理されます。

アダプターでのImageViewのリサイクルとダウンロードのキャンセルの処理。最小限のメモリ使用で複雑な画像変換。自動メモリとディスクキャッシュ。

ピカソジェイクウォートン図書館

グライド

Glideは、Android向けの高速で効率的なオープンソースのメディア管理フレームワークで、メディアのデコード、メモリとディスクのキャッシュ、リソースのプールをシンプルで使いやすいインターフェースにラップします。

Glideは、ビデオの静止画、画像、アニメーションGIFのフェッチ、デコード、表示をサポートしています。Glideには、開発者がほとんどすべてのネットワークスタックにプラグインできる柔軟なAPIが含まれています。デフォルトでは、GlideはカスタムのHttpUrlConnectionベースのスタックを使用しますが、代わりにGoogleのVolleyプロジェクトまたはSquareのOkHttpライブラリにプラグインするユーティリティライブラリも含まれています。

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

Glideの主な焦点は、あらゆる種類の画像のリストをできるだけスムーズかつ高速にスクロールすることですが、Glideは、リモート画像をフェッチ、サイズ変更、表示する必要があるほとんどすべての場合にも効果的です。

グライド画像読み込みライブラリ

Facebookによるフレスコ

フレスコは、Androidアプリケーションで画像を表示するための強力なシステムです。

Frescoが画像のロードと表示を処理するので、必要はありません。ネットワーク、ローカルストレージ、またはローカルリソースから画像を読み込み、画像が到着するまでプレースホルダーを表示します。キャッシュには2つのレベルがあります。1つはメモリに、もう1つは内部ストレージにあります。

フレスコGithub

Android 4.x以前では、Frescoは画像をAndroidメモリの特別な領域に配置します。これにより、アプリケーションの実行が速くなり、恐ろしいOutOfMemoryErrorが発生する頻度が大幅に減少します。

フレスコドキュメンテーション


PicassoはSquareによって開発されたライブラリです
LordRaydenMK 2018年

83

高性能ローダー-ここで提案された方法を検討した後、いくつかの変更を加えてベンのソリューションを使用しました-

  1. ドローアブルでの作業はビットマップでの作業よりも速いため、代わりにドローアブルを使用していることに気付きました

  2. SoftReferenceの使用はすばらしいですが、キャッシュされた画像が頻繁に削除されるので、画像の参照を保持するリンクリストを追加して、事前定義されたサイズに達するまで画像が削除されないようにしました

  3. InputStreamを開くには、java.net.URLConnectionを使用しました。これにより、Webキャッシュを使用できます(最初に応答キャッシュを設定する必要がありますが、それは別の話です)。

私のコード:

import java.util.Map; 
import java.util.HashMap; 
import java.util.LinkedList; 
import java.util.Collections; 
import java.util.WeakHashMap; 
import java.lang.ref.SoftReference; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ExecutorService; 
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException; 
import java.io.IOException; 
import java.net.URL;
import java.net.URLConnection;

public class DrawableBackgroundDownloader {    

private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();   
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;  
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());  

public static int MAX_CACHE_SIZE = 80; 
public int THREAD_POOL_SIZE = 3;

/**
 * Constructor
 */
public DrawableBackgroundDownloader() {  
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);  
}  


/**
 * Clears all instance data and stops running threads
 */
public void Reset() {
    ExecutorService oldThreadPool = mThreadPool;
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
    oldThreadPool.shutdownNow();

    mChacheController.clear();
    mCache.clear();
    mImageViews.clear();
}  

public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {  
    mImageViews.put(imageView, url);  
    Drawable drawable = getDrawableFromCache(url);  

    // check in UI thread, so no concurrency issues  
    if (drawable != null) {  
        //Log.d(null, "Item loaded from mCache: " + url);  
        imageView.setImageDrawable(drawable);  
    } else {  
        imageView.setImageDrawable(placeholder);  
        queueJob(url, imageView, placeholder);  
    }  
} 


private Drawable getDrawableFromCache(String url) {  
    if (mCache.containsKey(url)) {  
        return mCache.get(url).get();  
    }  

    return null;  
}

private synchronized void putDrawableInCache(String url,Drawable drawable) {  
    int chacheControllerSize = mChacheController.size();
    if (chacheControllerSize > MAX_CACHE_SIZE) 
        mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();

    mChacheController.addLast(drawable);
    mCache.put(url, new SoftReference<Drawable>(drawable));

}  

private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {  
    /* Create handler in UI thread. */  
    final Handler handler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            String tag = mImageViews.get(imageView);  
            if (tag != null && tag.equals(url)) {
                if (imageView.isShown())
                    if (msg.obj != null) {
                        imageView.setImageDrawable((Drawable) msg.obj);  
                    } else {  
                        imageView.setImageDrawable(placeholder);  
                        //Log.d(null, "fail " + url);  
                    } 
            }  
        }  
    };  

    mThreadPool.submit(new Runnable() {  
        @Override  
        public void run() {  
            final Drawable bmp = downloadDrawable(url);
            // if the view is not visible anymore, the image will be ready for next time in cache
            if (imageView.isShown())
            {
                Message message = Message.obtain();  
                message.obj = bmp;
                //Log.d(null, "Item downloaded: " + url);  

                handler.sendMessage(message);
            }
        }  
    });  
}  



private Drawable downloadDrawable(String url) {  
    try {  
        InputStream is = getInputStream(url);

        Drawable drawable = Drawable.createFromStream(is, url);
        putDrawableInCache(url,drawable);  
        return drawable;  

    } catch (MalformedURLException e) {  
        e.printStackTrace();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  

    return null;  
}  


private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
    URL url = new URL(urlString);
    URLConnection connection;
    connection = url.openConnection();
    connection.setUseCaches(true); 
    connection.connect();
    InputStream response = connection.getInputStream();

    return response;
}
}

よく働く!ところでクラス名にタイプミスがあります。
マリンズ

5
時間を節約できる場合:import java.util.Map; import java.util.HashMap; import java.util.LinkedList; import java.util.Collections; import java.util.WeakHashMap; import java.lang.ref.SoftReference; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import android.graphics.drawable.Drawable; import android.widget.ImageView; import android.os.Handler; import android.os.Message; import java.io.InputStream; import java.net.MalformedURLException; import java.io.IOException; import java.net.URL; import java.net.URLConnection;
マイケルリード

どうもありがとう、これは素晴らしい実装です。また、ユーザーがフィードバックを取得できるように、ドローアブルのロード時に別のプレースホルダーを配置しました。
ファンエルナンデス

また、デフォルトのFIFOではなく、executorService(mThreadPool)でLIFOキューを使用する方が良いと思うので、要求された最後の画像(おそらく表示されているもの)が最初にロードされます。stackoverflow.com/questions/4620061/how-to-create-lifo-executorを
Juan Hernandez

9
@ MichaelReed、Eclipseユーザーの場合は、Ctrl-Shift-Oを使用することをお勧めします(これは数字の0ではなく、文字Oです)。インポートを追加するプロセスを自動化し、それらを整理します。Macを使用している場合は、代わりにCommand-Shift-Oを使用してください。
SilithCrowe

78

私はこのAndroidトレーニングを実施しましたが、メインのUIをブロックすることなく画像をダウンロードすることは素晴らしい仕事だと思います。また、キャッシュと多くの画像のスクロール処理も処理します。大きなビットマップを効率的にロードする


申し訳ありませんが、Google IOアプリの1つのクラスのみをポイントしました(編集するには遅すぎます)。キャッシュクラスと同じパッケージにある、画像の読み込みとキャッシュユーティリティのすべてのクラスを実際に検討する必要があります。
mkuech 2013年

リストビューのイメージのロード/キャッシュの処理に役立つように、ioschedアプリのutilフォルダーからDiskLruCache、Image * .javaファイルを取得することをお勧めしますか?つまり、このテーマに関するオンラインの開発者ガイドに従うことは間違いなく価値がありますが、(ioschedからの)これらのクラスは、パターンを少し進めます。
ゴータム2013

65

1. Picassoを使用すると、多くの場合1行のコードでアプリケーションに画像を簡単にロードできます。

Gradleを使用する:

implementation 'com.squareup.picasso:picasso:2.71828'

コードは1行だけです。

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

2. Glideスムーズなスクロールに焦点を当てたAndroid用の画像読み込みおよびキャッシュライブラリ

Gradleを使用する:

repositories {
  mavenCentral() 
  google()
}

dependencies {
   implementation 'com.github.bumptech.glide:glide:4.7.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}

//単純なビューの場合:

  Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);

3. フレスコ は、Androidアプリケーションで画像を表示するための強力なシステムですフレスコは、画像のロードと表示を処理するので、あなたがする必要はありません。

フレスコ入門


このチュートリアルでは、より多くのPICASOOためのお手伝いをすることがあります。 - androidtutorialshub.com/...とGLIDE: - androidtutorialshub.com/...
ラリットvasan


41

私が行う方法は、バックグラウンドで画像をダウンロードするスレッドを起動し、各リスト項目のコールバックを渡すことです。画像のダウンロードが完了すると、リストアイテムのビューを更新するコールバックを呼び出します。

ただし、ビューをリサイクルしている場合、この方法はあまり機能しません。


各画像にスレッドを使用することも、私が使用するアプローチです。モデルをビューから切り離すと、モデルをアクティビティの外部(「アプリケーション」クラスなど)に永続化して、それらをキャッシュに保持できます。画像が多い場合は、リソース不足に注意してください。
James A Wilson、

詳しく説明してもらえますか?アンドロイド開発は初めてです。ヒントをありがとう
lostInTransit 2009

14
画像ごとに新しいスレッドを開始することは効果的な解決策ではありません。メモリ内のスレッドが多くなり、UIがフリーズする可能性があります。
ヒョードル

ヒョードル、同意した、私は通常キューとスレッドプールを使用する、それはimoに行く最良の方法です。
jasonhudgins 2010

32

もう1つ良い例として、XMLアダプターを追加したいと思います。これはGoogleで使用されているため、同じロジックを使用してOutOfMemoryエラーを回避しています。

基本的に、このImageDownloaderはあなたの答えです(ほとんどの要件をカバーしているため)。その中に実装できるものもあります。


2
ImageDownloaderクラスが準拠しない:code.google.com/p/parleys-android-nextgen/issues/detail?id
サム

29

これはAndroidに共通する問題であり、多くの人々によってさまざまな方法で解決されています。私の見解では、私が見た最良の解決策は、ピカソと呼ばれる比較的新しいライブラリです。ここにハイライトがあります:

  • オープンソースですがJake WhartonActionBarSherlockが率いています名声にます。
  • 1行のコードでネットワークリソースまたはアプリリソースから画像を非同期に読み込みます
  • 自動 ListView検出
  • ディスクとメモリの自動キャッシュ
  • カスタム変換を行うことができます
  • 多くの設定可能なオプション
  • 超シンプルなAPI
  • 頻繁に更新

29

私は新しいAndroid Volley LibraryのNetworkImageViewを使用していますがcom.android.volley.toolbox.NetworkImageView、かなりうまく機能しているようです。どうやら、これはGoogle Playや他の新しいGoogleアプリケーションで使用されているのと同じビューです。間違いなくチェックアウトする価値があります。


1
私はこれが最善の解決策だと思う-他の答えは非常に古いです-ボレーは本当に高速で、ジェイクのwarthonsと組み合わせてPERFEKTソリューションをdisklrucache -私は他の多くを試みたが、ない1は、安定かつ高速なボレーなどである
アレクサンダーSidikov Pfeif

26

まあ、インターネットからの画像読み込み時間には多くの解決策があります。ライブラリAndroid-Queryを使用することもできます。それはあなたに必要なすべての活動を提供します。あなたが何をしたいかを確認し、ライブラリのwikiページを読んでください。そして、画像の読み込み制限を解決します。

これは私のコードです:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
        LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.row, null);
    }

    ImageView imageview = (ImageView) v.findViewById(R.id.icon);
    AQuery aq = new AQuery(convertView);

    String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";

    aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
        @Override
        public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
            iv.setImageBitmap(bm);
        }
    ));

    return v;
}

遅延読み込みの問題を解決する必要があります。


いい仕事ですが、プロジェクトに含めるにはJarファイルが必要です。そのJARファイルは、ここからダウンロードできます。AQuery androidAQuery = new AQuery(this); リンクは:code.google.com/archive/p/android-query/downloads
Selim Raza

25

この問題はAndroid開発者の間で非常に人気があると思います。この問題を解決すると主張しているライブラリーはたくさんありますが、その一部しかマークされていないようです。AQueryはそのようなライブラリの1つですが、あらゆる面でほとんどのライブラリよりも優れており、試す価値があります。


22

このUniversal Loaderを試す必要があります。私は遅延読み込みで多くのRnDを実行した後にこれを使用しています。

ユニバーサルイメージローダー

特徴

  • マルチスレッドイメージの読み込み(非同期または同期)
  • ImageLoaderの構成の幅広いカスタマイズ(スレッドエグゼキューター、ダウンローダー、デコーダー、メモリとディスクキャッシュ、画像の表示オプションなど)
  • すべての表示イメージ呼び出しの多くのカスタマイズオプション(スタブイメージ、キャッシュスイッチ、デコードオプション、ビットマップ処理と表示など)
  • メモリおよび/またはディスク(デバイスのファイルシステムまたはSDカード)での画像のキャッシュ
  • ロードプロセスのリスニング(ダウンロードの進行状況を含む)

Android 2.0以降のサポート

ここに画像の説明を入力してください


20

見ていShutterbugを、Applidiumの軽量SDWebImage(iOSの素敵なライブラリ)のAndroidへの移植。非同期キャッシュをサポートし、失敗したURLを保存し、同時実行性を適切に処理し、役立つサブクラスが含まれています。

プルリクエスト(およびバグレポート)も大歓迎です!


16

DroidPartsにImageFetcherがあり、開始するためにゼロ構成が必要です。

  • ディスクとメモリ内の最も最近使用された(LRU)キャッシュを使用します。
  • 画像を効率的にデコードします。
  • バックグラウンドスレッドでのビットマップの変更をサポートします。
  • シンプルなクロスフェードがあります。
  • 画像読み込み進捗コールバックがあります。

例としてDroidPartsGramのクローンを作成します。

ここに画像の説明を入力してください


こんにちは、コード例を確認しましたが、ArrayAdapterでImageFetcherを使用すると問題が発生します。私の質問を見ていただけませんか?stackoverflow.com/questions/21089147/…ありがとう=]
masha 14年

16

画像の遅延読み込みに使用するライブラリーについて迷っている人のための簡単なヒント:

基本的な方法は4つあります。

  1. DIY =>最善の解決策ではありませんが、いくつかの画像で、他のライブラリを使用する手間をかけずに移動したい場合

  2. Volleyの遅延読み込みライブラリ=> androidの人から。それは素晴らしいですが、文書化が不十分であるため、使用するには問題があります。

  3. ピカソ:機能するシンプルなソリューションです。持ち込みたい画像の正確なサイズを指定することもできます。非常に簡単に使用できますが、膨大な量の画像を処理する必要があるアプリでは、それほど「パフォーマンスがよくない」場合があります。

  4. UIL:画像を遅延読み込みする最良の方法。あなたは画像をキャッシュすることができ(もちろんあなたは許可が必要です)、ローダーを一度初期化し、そしてあなたの仕事を終わらせます。これまでに見た中で最も成熟した非同期画像読み込みライブラリ。


15

Novodaには優れた遅延画像読み込みライブラリもあり、Songkick、Podio、SecretDJ、ImageSearchなどの多くのアプリがライブラリを使用しています。

彼らのライブラリはここ Githubでホストれており、かなり活発な課題トラッカーも持っています。彼らのプロジェクトもかなり活発で、この返信を書いている時点では300以上のコミットがあります。


1
実際、Novodaは素晴らしいライブラリですが、巨大なライブラリを必要とせず、ソリューションの単純なアプローチしか必要ない場合があります。これが、GithubのLazyListが非常に優れている理由です。アプリがlistViewに画像を表示するだけで、アプリの主な機能ではない場合、別のアクティビティとして、もっと軽いものを使用したいと思います。それ以外の場合、頻繁に使用する必要があり、コアの一部であることがわかっている場合は、Novodaを試してください。
Nicolas Jafelle

13

LazyListの私のフォークを確認してください。基本的に、ImageViewの呼び出しを遅らせることでLazyListを改善し、2つのメソッドを作成します。

  1. 「画像を読み込んでいます...」のようなものを配置する必要がある場合
  2. ダウンロードした画像を表示する必要がある場合。

このオブジェクトにシングルトンを実装することにより、ImageLoaderも改善しました。


12

FacebookのようなShimmerレイアウトを表示したい場合は、公式Facebookライブラリがあります。FaceBookシマーAndroid

それはすべての面倒を見てくれます、あなたはあなたが望むデザインコードを入れ子式にきらめくフレームに置くだけです。これがサンプルコードです。

<com.facebook.shimmer.ShimmerFrameLayout
     android:id=“@+id/shimmer_view_container”
     android:layout_width=“wrap_content”
     android:layout_height="wrap_content"
     shimmer:duration="1000">

 <here will be your content to display />

</com.facebook.shimmer.ShimmerFrameLayout>

そして、これはそのためのJavaコードです。

ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();

この依存関係をgradleファイルに追加します。

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

これはどのように見えるかです。Shimmer Androidスクリーンショット


11

上記のコードにはすべて独自の価値がありますが、私の個人的な経験では、ピカソを試してみてください。

ピカソ は、この目的専用のライブラリです。実際には、キャッシュとその他すべてのネットワーク操作を自動的に管理します。プロジェクトにライブラリを追加し、コードを1行書くだけで、リモートURLから画像を読み込む必要があります。

こちらをご覧ください:http : //code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149


9

グライドライブラリを使用します。それは私のために働いて、あなたのコードのためにも働きます。それはまた、画像だけでなくgifのためにも働きます。

ImageView imageView = (ImageView) findViewById(R.id.test_image); 
    GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
    Glide
            .with(this)
            .load(url)
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {                       
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    return false;
                }
            })
            .into(imagePreview);
}

7

チャームのように機能する別の方法、Androidクエリをお勧めします。

ここからJARファイルをダウンロードできます。

AQuery androidAQuery = new AQuery(this);

例として:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

これは非常に高速で正確です。これを使用すると、ロード時のアニメーション、ビットマップの取得(必要な場合)など、多くの機能を見つけることができます。




4
public class ImageDownloader {

Map<String, Bitmap> imageCache;

public ImageDownloader() {
    imageCache = new HashMap<String, Bitmap>();

}

// download function
public void download(String url, ImageView imageView) {
    if (cancelPotentialDownload(url, imageView)) {

        // Caching code right here
        String filename = String.valueOf(url.hashCode());
        File f = new File(getCacheDirectory(imageView.getContext()),
                filename);

        // Is the bitmap in our memory cache?
        Bitmap bitmap = null;

        bitmap = (Bitmap) imageCache.get(f.getPath());

        if (bitmap == null) {

            bitmap = BitmapFactory.decodeFile(f.getPath());

            if (bitmap != null) {
                imageCache.put(f.getPath(), bitmap);
            }

        }
        // No? download it
        if (bitmap == null) {
            try {
                BitmapDownloaderTask task = new BitmapDownloaderTask(
                        imageView);
                DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
                        task);
                imageView.setImageDrawable(downloadedDrawable);
                task.execute(url);
            } catch (Exception e) {
                Log.e("Error==>", e.toString());
            }

        } else {
            // Yes? set the image
            imageView.setImageBitmap(bitmap);
        }
    }
}

// cancel a download (internal only)
private static boolean cancelPotentialDownload(String url,
        ImageView imageView) {
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

    if (bitmapDownloaderTask != null) {
        String bitmapUrl = bitmapDownloaderTask.url;
        if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            bitmapDownloaderTask.cancel(true);
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

// gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(
        ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
            return downloadedDrawable.getBitmapDownloaderTask();
        }
    }
    return null;
}

// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
    String sdState = android.os.Environment.getExternalStorageState();
    File cacheDir;

    if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
        File sdDir = android.os.Environment.getExternalStorageDirectory();

        // TODO : Change your diretcory here
        cacheDir = new File(sdDir, "data/ToDo/images");
    } else
        cacheDir = context.getCacheDir();

    if (!cacheDir.exists())
        cacheDir.mkdirs();
    return cacheDir;
}

private void writeFile(Bitmap bmp, File f) {
    FileOutputStream out = null;

    try {
        out = new FileOutputStream(f);
        bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (out != null)
                out.close();
        } catch (Exception ex) {
        }
    }
}

// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    // Actual download method, run in the task thread
    protected Bitmap doInBackground(String... params) {
        // params comes from the execute() call: params[0] is the url.
        url = (String) params[0];
        return downloadBitmap(params[0]);
    }

    @Override
    // Once the image is downloaded, associates it to the imageView
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
            // Change bitmap only if this process is still associated with
            // it
            if (this == bitmapDownloaderTask) {
                imageView.setImageBitmap(bitmap);

                // cache the image

                String filename = String.valueOf(url.hashCode());
                File f = new File(
                        getCacheDirectory(imageView.getContext()), filename);

                imageCache.put(f.getPath(), bitmap);

                writeFile(bitmap, f);
            }
        }
    }

}

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
        super(Color.WHITE);
        bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
                bitmapDownloaderTask);
    }

    public BitmapDownloaderTask getBitmapDownloaderTask() {
        return bitmapDownloaderTaskReference.get();
    }
}

// the actual download code
static Bitmap downloadBitmap(String url) {
    HttpParams params = new BasicHttpParams();
    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
            HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    final HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            Log.w("ImageDownloader", "Error " + statusCode
                    + " while retrieving bitmap from " + url);
            return null;
        }

        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                final Bitmap bitmap = BitmapFactory
                        .decodeStream(inputStream);
                return bitmap;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                entity.consumeContent();
            }
        }
    } catch (Exception e) {
        // Could provide a more explicit error message for IOException or
        // IllegalStateException
        getRequest.abort();
        Log.w("ImageDownloader", "Error while retrieving bitmap from "
                + url + e.toString());
    } finally {
        if (client != null) {
            // client.close();
        }
    }
    return null;
 }
}

4

私にはこの問題があり、lruCacheを実装しました。API 12以上が必要か、互換性v4ライブラリを使用していると思います。lurCacheは高速メモリですが、予算もあるので、心配な場合はディスクキャッシュを使用できます...これはすべてビットマップのキャッシュで説明されていますます。

次に、このようなどこからでも呼び出すシングルトンである実装を提供します。

//Where the first is a string and the other is a imageview to load.

DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);

キャッシュしてWebイメージを取得するときに、アダプターのgetViewで上記を呼び出す理想的なコードを次に示します。

public class DownloadImageTask {

    private LruCache<String, Bitmap> mMemoryCache;

    /* Create a singleton class to call this from multiple classes */

    private static DownloadImageTask instance = null;

    public static DownloadImageTask getInstance() {
        if (instance == null) {
            instance = new DownloadImageTask();
        }
        return instance;
    }

    //Lock the constructor from public instances
    private DownloadImageTask() {

        // Get max available VM memory, exceeding this amount will throw an
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an
        // int in its constructor.
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

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

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

    public void loadBitmap(String avatarURL, ImageView imageView) {
        final String imageKey = String.valueOf(avatarURL);

        final Bitmap bitmap = getBitmapFromMemCache(imageKey);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            imageView.setImageResource(R.drawable.ic_launcher);

            new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
        }
    }

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

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

    /* A background process that opens a http stream and decodes a web image. */

    class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
        ImageView bmImage;

        public DownloadImageTaskViaWeb(ImageView bmImage) {
            this.bmImage = bmImage;
        }

        protected Bitmap doInBackground(String... urls) {

            String urldisplay = urls[0];
            Bitmap mIcon = null;
            try {
                InputStream in = new java.net.URL(urldisplay).openStream();
                mIcon = BitmapFactory.decodeStream(in);

            } 
            catch (Exception e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }

            addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);

            return mIcon;
        }

        /* After decoding we update the view on the main UI. */
        protected void onPostExecute(Bitmap result) {
            bmImage.setImageBitmap(result);
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.