Como faço para usar o cache de disco no Picasso?

119

Estou usando o Picasso para exibir imagens em meu aplicativo 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);
}

Eu habilitei a depuração e sempre mostra vermelho e verde. Mas nunca mostra amarelo

Agora, se eu carregar a mesma imagem da próxima vez e a internet não estiver disponível, a imagem não será carregada.

Questões:

  1. Não tem cache de disco local?
  2. Como faço para habilitar o cache de disco, pois estarei usando a mesma imagem várias vezes.
  3. Preciso adicionar alguma permissão de disco ao arquivo de manifesto do Android?
user93796
fonte
Estou tendo o mesmo problema. Não vai armazenar em cache!
Jonathan
Pessoal, vocês deveriam dar uma olhada no Fresco lib do facebook. Seu gerenciamento de cache é incrível.
Michel Fortes

Respostas:

229

Isso é o que eu fiz. Funciona bem.

Primeiro, adicione OkHttp ao arquivo de compilação do gradle do módulo do app:

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'

Em seguida, faça uma extensão de classe 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);

    }
}

adicione-o ao arquivo Manifest da seguinte maneira:

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

</application>

Agora use o Picasso como faria normalmente. Sem alterações.

EDITAR:

se você quiser usar apenas imagens em cache. Ligue para a biblioteca assim. Percebi que, se não adicionarmos a networkPolicy, as imagens não serão exibidas em um início totalmente offline, mesmo se estiverem em cache . O código abaixo resolve o problema.

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

EDITAR # 2

o problema com o código acima é que, se você limpar o cache, o Picasso continuará procurando off-line no cache e falhará, o exemplo de código a seguir examina o cache local; se não for encontrado off-line, ele fica online e reabastece o cache.

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");
            }
        });
    }
});
Sanket Berde
fonte
@ArtjomB. , Eu respondi à pergunta. E a solução funciona. Mas há um pequeno esclarecimento que posso usar. Passei pela documentação do OkHttp e eles não mencionam a unidade de "cache". Então, se alguém quiser compartilhar alguma sabedoria ... esta é uma boa oportunidade.
Sanket Berde
@ArtjomB. sim, isso faz sentido. Editado!
Sanket Berde
5
@SanketBerde: Obrigado pela nota rápida, mas descobri que a imagem está saindo da memória apenas se o aplicativo estiver sendo executado em segundo plano (quando estiver offline). se eu fechar o aplicativo, não haverá aplicativos em execução e, em seguida, abrir meu aplicativo novamente, as imagens não carregam do cache. Eu configurei a imagem de carregamento padrão de erro que está chegando. Oque pode estar errado aqui?
TheDevMan
1
Talvez o Picasso tenha mudado a forma como as coisas funcionam, porque para mim funciona bem sem okhttp e política de rede. Em uma nova inicialização, ele obtém instantaneamente imagens do disco e, quando a rede está offline, ainda as mostra bem.
zeeshan
1
Com a okhttp3.OkHttpClientbiblioteca, você tem que usar o OkHttp3Downloaderformulário de classe compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck
46

1) resposta da primeira pergunta: de acordo com o método Picasso Doc para With ()

A instância padrão global do Picasso retornada com () é inicializada automaticamente com os padrões que são adequados para a maioria das implementações.

  • Cache de memória LRU de 15% da RAM de aplicativo disponível
  • Cache de disco com 2% de espaço de armazenamento até 50 MB, mas não menos que 5 MB.

Mas a Disk Cache operação para Picasso padrão global está disponível apenas na API 14+

2) resposta da segunda pergunta: Picassouse a HTTPsolicitação do cliente para a Disk Cacheoperação Assim, você pode fazer sua própria http request headerpropriedade Cache-Controlcom max-age E criar sua própria instância estática de Picasso em vez do Picasso padrão.

1] HttpResponseCache (Nota: Funciona apenas para API 13+)
2] OkHttpClient (Funciona para todas as APIs)

Exemplo de uso OkHttpClientpara criar sua própria classe Static Picasso:

  • Primeiro crie uma nova classe para obter seu próprio picassoobjeto singleton

    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;
        }
    
    } 
    
  • use seu próprio picassoobjeto singleton em vez dePicasso.With()

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

3) resposta para a terceira pergunta: você não precisa de nenhuma permissão de disco para operações de cache de disco

Referências : Problema do Github sobre cache de disco , duas perguntas foram respondidas por @ jake-wharton -> Pergunta1 e Pergunta2

ahmed hamdy
fonte
4
Não, isso não funcionará se o aplicativo tiver sido fechado. Depois que o aplicativo foi interrompido à força, todas as imagens desapareceram.
nbumakov
2
isso está me gerando este erro:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CÍRCULO
@CIRCLE desculpe pelo atraso, para usar o exemplo, você precisa baixar primeiro o pacote [okhttp] ( square.github.io/okhttp ) e o pacote [okio] ( github.com/square/okio ) que foi usado porokhttp
ahmed hamdy
@CIRCLE pode ser necessário fazer o download do pacote [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… ) também
ahmed hamdy
Isso não está funcionando para mim. As imagens estão sendo recarregadas toda vez que eu rolar para suas posições no viewPager
Charu
21

Para armazenamento em cache, eu usaria interceptores OkHttp para obter controle sobre a política de armazenamento em cache. Confira este exemplo que está incluído na biblioteca OkHttp.

RewriteResponseCacheControl.java

É assim que eu o usaria com o Picasso -

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);
Gaurav B
fonte
1
Não funciona mais networkInterceptors () retorna uma lista imutável.
noev
1
@noev no OkHttp 3.x você pode usar o padrão builder (veja github.com/square/okhttp/wiki/Interceptors ) para adicionar interceptores.
Gaurav B
10

Para a versão mais atualizada 2.71828 Esta é a sua resposta.

T1 : ele não tem cache de disco local?

A1 : Há cache padrão no Picasso e o fluxo de solicitação exatamente como este

App -> Memory -> Disk -> Server

Sempre que encontrarem sua imagem primeiro, eles a usarão e, em seguida, interromperão o fluxo de solicitação. E quanto ao fluxo de resposta? Não se preocupe, aqui está.

Server -> Disk -> Memory -> App

Por padrão, eles serão armazenados em um disco local primeiro para o cache de armazenamento estendido. Em seguida, a memória, para o uso de instância do cache.

Você pode usar o indicador embutido no Picasso para ver onde as imagens se formam, ativando isso.

Picasso.get().setIndicatorEnabled(true);

Ele vai mostrar uma bandeira no canto superior esquerdo de suas fotos.

  • Bandeira vermelha significa que as imagens vêm do servidor. (Sem cache no primeiro carregamento)
  • A bandeira azul significa que as fotos vêm do disco local. (Cache)
  • A bandeira verde significa que as imagens vêm da memória. (Cache de instância)

P2 : Como habilito o cache de disco, pois usarei a mesma imagem várias vezes?

A2 : Você não precisa habilitá-lo. É o padrão.

O que você precisa fazer é DESATIVÁ - LO quando quiser que suas imagens estejam sempre frescas. Há duas formas de cache desabilitado.

  1. Defina .memoryPolicy()como NO_CACHE e / ou NO_STORE e o fluxo ficará assim.

NO_CACHE irá pular a procura de imagens da memória.

App -> Disk -> Server

NO_STORE irá pular imagens de armazenamento na memória quando as imagens forem carregadas pela primeira vez.

Server -> Disk -> App
  1. Defina .networkPolicy()como NO_CACHE e / ou NO_STORE e o fluxo ficará assim.

NO_CACHE irá pular a procura de imagens do disco.

App -> Memory -> Server

NO_STORE irá pular imagens de armazenamento no disco quando as imagens forem carregadas pela primeira vez.

Server -> Memory -> App

Você não pode DESATIVAR para não armazenar imagens em cache. Aqui está um exemplo.

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

O fluxo sem cache e sem armazenamento será assim.

App -> Server //Request

Server -> App //Response

Portanto, você pode precisar disso para minimizar o uso de armazenamento do aplicativo também.

P3 : Preciso adicionar alguma permissão de disco ao arquivo de manifesto do Android?

A3 : Não, mas não se esqueça de adicionar a permissão INTERNET para sua solicitação HTTP.

Narerit Rittiplaeng
fonte
6

1) Por padrão, o Picasso tem cache (veja a resposta de ahmed hamdy)

2) Se você realmente precisa tirar uma imagem do cache de disco e então da rede, recomendo escrever seu próprio downloader:

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;
        }

    }
}

E no singleton do aplicativo no método OnCreate, use-o com o picasso:

        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) Nenhuma permissão necessária para cancelar a pasta de cache do aplicativo

tsm
fonte
1

Adicione o código a Application.onCreateseguir e use-o normalmente

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

Se você armazenar imagens em cache primeiro, faça algo assim em 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);
        }

e carregue suas imagens normalmente ou com cache de disco

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

Nota:

A cor vermelha indica que a imagem foi obtida da rede .

A cor verde indica que a imagem foi obtida da memória cache .

A cor azul indica que a imagem foi obtida da memória do disco .

Antes de liberar o aplicativo, exclua ou configure-o false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);se não for necessário. Obrigado

Qamar
fonte
1

Não sei se essa solução é boa, mas é definitivamente A FÁCIL que acabei de usar no meu aplicativo e está funcionando bem

você carrega a imagem assim

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

Você pode conseguir bimapcoisas assim

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

Agora converta isso Bitmapem um JPGarquivo e armazene no cache, abaixo está o código completo para obter o bimapa e armazená-lo em cache

 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();
                    })

o get()método de, Piccassopor algum motivo, precisava ser chamado em um tópico separado, estou salvando essa imagem também nesse mesmo tópico.

Assim que a imagem for salva, você pode obter todos os arquivos assim

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

agora você pode encontrar o arquivo que está procurando como abaixo

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
       }
Abhinav Chauhan
fonte
0

Eu uso este código e funcionou, talvez útil para você:

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...");
                                }

                            });
                }
            });

}
Iman Marashi
fonte