ピカソでディスクキャッシュを使用するにはどうすればよいですか?


119

私のAndroidアプリで画像を表示するためにピカソを使用しています:

/**
* load image.This is within a activity so this context is activity
*/
public void loadImage (){
    Picasso picasso = Picasso.with(this); 
    picasso.setDebugging(true);
    picasso.load(quiz.getImageUrl()).into(quizImage);
}

デバッグを有効にしましたが、常に赤と緑のどちらかが表示されますが、黄色は表示されません

今度は同じ画像をロードし、インターネットが利用できない場合、画像はロードされません。

質問:

  1. ローカルディスクキャッシュはありませんか?
  2. 同じイメージを複数回使用するため、ディスクキャッシュを有効にする方法を教えてください。
  3. Androidマニフェストファイルにディスクアクセス許可を追加する必要がありますか?

同じ問題が発生しています。キャッシュされません!
ジョナサン

皆さん、facebookのFresco libを見てください。そのキャッシュ管理は素晴らしいです。
ミシェルフォルテス

回答:


229

これは私がやったことです。うまくいきます。

まず、アプリモジュールのGradleビルドファイルにOkHttpを追加します。

compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp3:okhttp:3.10.0'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'

次に、拡張するクラスを作成します Application

import android.app.Application;

import com.jakewharton.picasso.OkHttp3Downloader;
import com.squareup.picasso.Picasso;

public class Global extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Picasso.Builder builder = new Picasso.Builder(this);
        builder.downloader(new OkHttp3Downloader(this,Integer.MAX_VALUE));
        Picasso built = builder.build();
        built.setIndicatorsEnabled(true);
        built.setLoggingEnabled(true);
        Picasso.setSingletonInstance(built);

    }
}

次のようにマニフェストファイルに追加します。

<application
        android:name=".Global"
        .. >

</application>

通常どおりにピカソを使用します。変更なし。

編集:

キャッシュされた画像のみを使用する場合。このようなライブラリを呼び出します。networkPolicyを追加しない場合、画像がキャッシュされていても、完全にオフラインで起動すると画像が表示さないことに気付きました。以下のコードは問題を解決します。

Picasso.with(this)
            .load(url)
            .networkPolicy(NetworkPolicy.OFFLINE)
            .into(imageView);

編集#2

上記のコードの問題は、キャッシュをクリアすると、ピカソがオフラインでキャッシュを探し続けて失敗することです。次のコード例は、ローカルキャッシュを調べ、オフラインで見つからない場合はオンラインになり、キャッシュを補充します。

Picasso.with(getActivity())
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, new Callback() {
    @Override
    public void onSuccess() {

    }

    @Override
    public void onError() {
        //Try again online if cache failed
        Picasso.with(getActivity())
                .load(posts.get(position).getImageUrl())
                .error(R.drawable.header)
                .into(imageView, new Callback() {
            @Override
            public void onSuccess() {

            }

            @Override
            public void onError() {
                Log.v("Picasso","Could not fetch image");
            }
        });
    }
});

@ArtjomB。、私は質問に答えました。そして、ソリューションは機能します。しかし、私が使用できるこの小さな説明があります。私はOkHttpのドキュメントを調べましたが、「キャッシュ」の単位については触れられていません。だから、誰かが知恵を共有したいのなら...これは良い機会です。
Sanket Berde 2015

@ArtjomB。はい、それは理にかなっています。編集しました!
Sanket Berde 2015

5
@SanketBerde:簡単なメモに感謝しますが、アプリがバックグラウンドで実行されている場合(オフライン時)にのみ、画像がメモリから表示されることがわかりました。アプリを閉じると、実行中のアプリがクリアされ、再度アプリを開くと、画像がキャッシュから読み込まれません。表示されるエラーのデフォルトの読み込み画像を設定しました。ここで何が悪いのでしょうか?
TheDevMan 2015

1
多分、ピカソが物事の仕組みを変えたのかもしれません。なぜなら、私にとっては、okhttpとネットワークポリシーがなくてもうまく機能するからです。新たに開始すると、ディスクから画像を即座に取得し、ネットワークがオフラインのときでも、画像を正常に表示します。
zeeshan 2016年

1
ではokhttp3.OkHttpClientライブラリあなたは使用する必要がOkHttp3Downloaderクラスフォーム compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
チャック・

46

1)最初の質問の答え:With()メソッドのPicasso Docによると

with()から返されるグローバルなデフォルトのPicassoインスタンスは、ほとんどの実装に適したデフォルトで自動的に初期化されます。

  • 使用可能なアプリケーションRAMの15%のLRUメモリキャッシュ
  • 2%のストレージスペースのディスクキャッシュ。最大50MB。ただし5MB以上。

ただし Disk Cache、グローバルデフォルトピカソの操作は、API 14以降でのみ使用できます。

2) 2番目の質問の答え:クライアント要求をPicasso使用しHTTPDisk Cache操作を実行します。これにより、独自のhttp request headerhasプロパティCache-Controlmax-age 使用して、デフォルトのピカソの代わりに独自の静的ピカソインスタンスを作成できます。

1] HttpResponseCache(注:API 13以降でのみ機能します)
2] OkHttpClient(すべてのAPIで機能します)

を使用OkHttpClientして独自の静的ピカソクラスを作成する

  • まず、独自のシングルトンpicassoオブジェクトを取得する新しいクラスを作成します

    import android.content.Context;
    import com.squareup.picasso.Downloader;
    import com.squareup.picasso.OkHttpDownloader;
    import com.squareup.picasso.Picasso;
    
    public class PicassoCache {
    
        /**
         * Static Picasso Instance
         */
        private static Picasso picassoInstance = null;
    
        /**
         * PicassoCache Constructor
         *
         * @param context application Context
         */
        private PicassoCache (Context context) {
    
            Downloader downloader   = new OkHttpDownloader(context, Integer.MAX_VALUE);
            Picasso.Builder builder = new Picasso.Builder(context);
                builder.downloader(downloader);
    
            picassoInstance = builder.build();
        }
    
        /**
         * Get Singleton Picasso Instance
         *
         * @param context application Context
         * @return Picasso instance
         */
        public static Picasso getPicassoInstance (Context context) {
    
            if (picassoInstance == null) {
    
                new PicassoCache(context);
                return picassoInstance;
            }
    
            return picassoInstance;
        }
    
    } 
  • picasso代わりに独自のシングルトンオブジェクトを使用するPicasso.With()

PicassoCache.getPicassoInstance(getContext()).load(imagePath).into(imageView)

3) 3番目の質問に対する回答:ディスクキャッシュ操作にディスク権限は必要ありません

参照ディスクキャッシュに関するGithubの問題@ jake-whartonによる2つの質問への回答-> 質問1質問2


4
いいえ、アプリが閉じている場合、これは機能しません。アプリを強制停止した後、すべての画像が消えました。
nbumakov 2015年

2
これは私にこのエラーを与えています:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE

@CIRCLEお手数ですが、例を使用するには、最初に[okhttp](square.github.io/okhttp)パッケージと[okio](github.com/square/okio)パッケージをダウンロードする必要がありますokhttp
ahmed hamdy

@CIRCLEは[okhttp-urlconnection](mvnrepository.com/artifact/com.squareup.okhttp/…)パッケージもダウンロードする必要があるかもしれません
ahmed hamdy

これは私のために働いていません。画像をviewPagerの位置までスクロールするたびに画像がリロードされます
Charu

21

キャッシングについては、OkHttp インターセプターを使用して、キャッシングポリシーを制御します。OkHttpライブラリーに含まれているこのサンプルを確認してください。

RewriteResponseCacheControl.java

これがピカソで使用する方法です-

OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.networkInterceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response originalResponse = chain.proceed(chain.request());
            return originalResponse.newBuilder().header("Cache-Control", "max-age=" + (60 * 60 * 24 * 365)).build();
        }
    });

    okHttpClient.setCache(new Cache(mainActivity.getCacheDir(), Integer.MAX_VALUE));
    OkHttpDownloader okHttpDownloader = new OkHttpDownloader(okHttpClient);
    Picasso picasso = new Picasso.Builder(mainActivity).downloader(okHttpDownloader).build();
    picasso.load(imageURL).into(viewHolder.image);

1
networkInterceptors()は不変のリストを返します。
noev '06 / 06/29

1
OkHttp 3.xの@noevでは、ビルダーパターン(github.com/square/okhttp/wiki/Interceptorsを参照)を使用してインターセプターを追加できます。
Gaurav B 2016

10

最新のバージョン2.71828の場合これが答えです。

Q1:ローカルディスクキャッシュはありませんか?

A1:Picasso内にはデフォルトのキャッシュがあり、リクエストフローは次のようになります

App -> Memory -> Disk -> Server

彼らが最初に彼らのイメージに会ったところはどこでも、彼らはそのイメージを使い、それからリクエストフローを止めます。応答フローはどうですか?心配しないで、ここにあります。

Server -> Disk -> Memory -> App

デフォルトでは、拡張キーピングキャッシュ用に最初にローカルディスクに保存されます。次に、キャッシュ、インスタンスの使用のためのメモリ。

これを有効にすると、ピカソの組み込みインジケーターを使用して、画像が形成される場所を確認できます。

Picasso.get().setIndicatorEnabled(true);

写真の左上にフラグが表示されます。

  • 赤い旗は、画像がサーバーからのものであることを意味します。(最初のロードではキャッシングなし)
  • 青い旗は、写真がローカルディスクからのものであることを意味します。(キャッシング)
  • 緑のフラグは、画像がメモリからのものであることを意味します。(インスタンスキャッシュ)

Q2:同じイメージを複数回使用するため、ディスクキャッシュを有効にするにはどうすればよいですか?

A2:有効にする必要はありません。これがデフォルトです。

あなたがする必要があるのはあなたがあなたのイメージを常に新鮮にしたいときそれを無効にすることです。無効化されたキャッシングには2つの方法があります。

  1. NO_CACHEまたはNO_STORE、あるいはその両方に設定.memoryPolicy()すると、フローは次のようになります。

NO_CACHEは、メモリからの画像の検索をスキップします。

App -> Disk -> Server

NO_STOREは、最初のイメージのロード時に、イメージをメモリに保存しません

Server -> Disk -> App
  1. NO_CACHEまたはNO_STORE、あるいはその両方に設定.networkPolicy()すると、フローは次のようになります。

NO_CACHEは、ディスクからの画像の検索をスキップします。

App -> Memory -> Server

NO_STOREは、最初のイメージのロード時に、イメージをディスクに保存しません

Server -> Memory -> App

イメージを完全にキャッシュしない場合は、どちらも無効にできません。例を示します。

Picasso.get().load(imageUrl)
             .memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)
             .networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
             .fit().into(banner);

完全にキャッシュも保存もないフローは次のようになります。

App -> Server //Request

Server -> App //Response

そのため、アプリのストレージ使用量を抑えるためにもこれが必要になる場合があります。

Q3:Androidマニフェストファイルにディスクアクセス許可を追加する必要がありますか?

A3:いいえ。ただし、HTTPリクエストにINTERNET権限を追加することを忘れないでください。


6

1)デフォルトでピカソはキャッシュを持っています(ahmed hamdy answerを参照)

2)ディスクキャッシュとネットワークからイメージを取得する必要がある場合は、独自のダウンローダーを作成することをお勧めします。

public class OkHttpDownloaderDiskCacheFirst extends OkHttpDownloader {
    public OkHttpDownloaderDiskCacheFirst(OkHttpClient client) {
        super(client);
    }

    @Override
    public Response load(Uri uri, int networkPolicy) throws IOException {
        Response responseDiskCache = null;
        try {
            responseDiskCache = super.load(uri, 1 << 2); //NetworkPolicy.OFFLINE
        } catch (Exception ignored){} // ignore, handle null later

        if (responseDiskCache == null || responseDiskCache.getContentLength()<=0){
            return  super.load(uri, networkPolicy); //user normal policy
        } else {
            return responseDiskCache;
        }

    }
}

そして、メソッドOnCreateのアプリケーションシングルトンでは、ピカソでそれを使用します。

        OkHttpClient okHttpClient = new OkHttpClient();

        okHttpClient.setCache(new Cache(getCacheDir(), 100 * 1024 * 1024)); //100 MB cache, use Integer.MAX_VALUE if it is too low
        OkHttpDownloader downloader = new OkHttpDownloaderDiskCacheFirst(okHttpClient); 

        Picasso.Builder builder = new Picasso.Builder(this);

        builder.downloader(downloader);

        Picasso built = builder.build();

        Picasso.setSingletonInstance(built);

3)デフォルトのアプリケーションキャッシュフォルダーに権限は必要ありません


1

次のコードを追加しApplication.onCreateて通常どおり使用します

    Picasso picasso = new Picasso.Builder(context)
            .downloader(new OkHttp3Downloader(this,Integer.MAX_VALUE))
            .build();
    picasso.setIndicatorsEnabled(true);
    picasso.setLoggingEnabled(true);
    Picasso.setSingletonInstance(picasso);

最初に画像をキャッシュする場合は、次のようにしてください ProductImageDownloader.doBackground

final Callback callback = new Callback() {
            @Override
            public void onSuccess() {
                downLatch.countDown();
                updateProgress();
            }

            @Override
            public void onError() {
                errorCount++;
                downLatch.countDown();
                updateProgress();
            }
        };
        Picasso.with(context).load(Constants.imagesUrl+productModel.getGalleryImage())
                .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(callback);
        Picasso.with(context).load(Constants.imagesUrl+productModel.getLeftImage())
                .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(callback);
        Picasso.with(context).load(Constants.imagesUrl+productModel.getRightImage())
                .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(callback);

        try {
            downLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if(errorCount == 0){
            products.remove(productModel);
            productModel.isDownloaded = true;
            productsDatasource.updateElseInsert(productModel);
        }else {
            //error occurred while downloading images for this product
            //ignore error for now
            // FIXME: 9/27/2017 handle error
            products.remove(productModel);

        }
        errorCount = 0;
        downLatch = new CountDownLatch(3);

        if(!products.isEmpty() /*&& testCount++ < 30*/){
            startDownloading(products.get(0));
        }else {
            //all products with images are downloaded
            publishProgress(100);
        }

通常のように、またはディスクキャッシュを使用してイメージをロードします

    Picasso.with(this).load(Constants.imagesUrl+batterProduct.getGalleryImage())
        .networkPolicy(NetworkPolicy.OFFLINE)
        .placeholder(R.drawable.GalleryDefaultImage)
        .error(R.drawable.GalleryDefaultImage)
        .into(viewGallery);

注意:

赤い色は、画像がネットワークから取得されたことを示します

緑色は、画像がキャッシュメモリからフェッチされたことを示します

青色は、画像がディスクメモリからフェッチされたことを示します

削除アプリをリリースする前に、またはそれを設定しfalse picasso.setLoggingEnabled(true);picasso.setIndicatorsEnabled(true);必要でない場合。ありがとう


1

その解決策がどれほど優れているかはわかりませんが、間違いなくTHE EASY ONEです、アプリで使用した機能しています

あなたはそのような画像をロードします

public void loadImage (){
Picasso picasso = Picasso.with(this); 
picasso.setDebugging(true);
picasso.load(quiz.getImageUrl()).into(quizImage);
}

あなたはそのbimapようなものを得ることができます

Bitmap bitmap = Piccaso.with(this).load(quiz.getImageUrl()).get();

これをファイルにBitmap変換JPGしてキャッシュに保存します。以下は、バイマップを取得してキャッシュするための完全なコードです。

 Thread thread = new Thread() {
                            public void run() {
                                File file = new File(getActivity().getExternalCacheDir().getAbsolutePath() + "/" +member.getMemberId() + ".jpg");

                                try {
                                    Bitmap bitmap = Picasso.with(getActivity())
                                            .load(uri).get();
                                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100,new FileOutputStream(file));

                                } catch (Exception e) {
                                   e.printStackTrace();
                                }
                            }
                        };
                        thread.start();
                    })

get()方法Piccasso別のスレッドで呼び出されるために必要ないくつかの理由については、私は同じスレッドにもそのイメージを保存しています。

画像が保存されると、そのようなすべてのファイルを取得できます

List<File> files = new LinkedList<>(Arrays.asList(context.getExternalCacheDir().listFiles()));

今、あなたは探しているファイルを以下のように見つけることができます

for(File file : files){
                if(file.getName().equals("fileyouarelookingfor" + ".jpg")){ // you need the name of the file, for example you are storing user image and the his image name is same as his id , you can call getId() on user to get the file name
                    Picasso.with(getActivity()) // if file found then load it
                            .load(file)
                            .into(mThumbnailImage);
                    return; // return 
                }
        // fetch it over the internet here because the file is not found
       }

0

私はこのコードを使用して動作しましたが、おそらくあなたにとって便利です:

public static void makeImageRequest(final View parentView,final int id, final String imageUrl) {

    final int defaultImageResId = R.mipmap.user;
    final ImageView imageView = (ImageView) parentView.findViewById(id);
    Picasso.with(context)
            .load(imageUrl)
            .networkPolicy(NetworkPolicy.OFFLINE)
            .into(imageView, new Callback() {
                @Override
                public void onSuccess() {
                Log.v("Picasso","fetch image success in first time.");
                }

                @Override
                public void onError() {
                    //Try again online if cache failed
                    Log.v("Picasso","Could not fetch image in first time...");
                    Picasso.with(context).load(imageUrl).networkPolicy(NetworkPolicy.NO_CACHE)
                            .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE).error(defaultImageResId)
                            .into(imageView, new Callback() {

                                @Override
                                public void onSuccess() {
                                    Log.v("Picasso","fetch image success in try again.");
                                }

                                @Override
                                public void onError() {
                                  Log.v("Picasso","Could not fetch image again...");
                                }

                            });
                }
            });

}

-3

同じ問題があり、代わりにGlideライブラリを使用しました。キャッシュはそのまま使用できます。 https://github.com/bumptech/glide


グライドはピカソの5倍のコードです。すでにokhttpを使用している場合、ピカソを使用することをお勧めします
Alexander Farber

2
両方のライブラリは、異なるキャッシュが、キャッシュを使用します。 medium.com/@multidots/glide-vs-picasso-930eed42b81d
オッリ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.