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?

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

@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


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

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.


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


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


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
       }

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

                            });
                }
            });

}

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.