¿Cómo utilizo el almacenamiento en caché de disco en Picasso?


119

Estoy usando Picasso para mostrar la imagen en mi aplicación de 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);
}

He habilitado la depuración y siempre muestra rojo y verde. Pero nunca muestra amarillo

Ahora, si cargo la misma imagen la próxima vez e Internet no está disponible, la imagen no se carga.

Preguntas:

  1. ¿No tiene caché de disco local?
  2. ¿Cómo habilito el almacenamiento en caché del disco ya que usaré la misma imagen varias veces?
  3. ¿Necesito agregar algún permiso de disco al archivo de manifiesto de Android?

Tengo el mismo problema. ¡No almacenará en caché!
Jonathan

Chicos, deberían echar un vistazo a Fresco lib de Facebook. Su gestión de caché es impresionante.
Michel Fortes

Respuestas:


229

Esto es lo que hice. Funciona bien.

Primero agregue OkHttp al archivo de compilación gradle del módulo de la aplicación:

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'

Luego haz una clase ampliando 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);

    }
}

agréguelo al archivo de manifiesto de la siguiente manera:

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

</application>

Ahora use Picasso como lo haría normalmente. Sin cambios.

EDITAR:

si solo desea utilizar imágenes almacenadas en caché. Llame a la biblioteca así. He notado que si no agregamos la política de red, las imágenes no se mostrarán en un inicio completamente fuera de línea, incluso si están almacenadas en caché . El siguiente código resuelve el problema.

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

EDITAR # 2

El problema con el código anterior es que si borras la memoria caché, Picasso seguirá buscándola sin conexión en la memoria caché y fallará, el siguiente ejemplo de código examina la memoria caché local, si no se encuentra fuera de línea, se conecta y repone la memoria caché.

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. , Respondí la pregunta. Y la solución funciona. Pero hay una pequeña aclaración que puedo utilizar. Revisé la documentación de OkHttp y no mencionan la unidad de "caché". Entonces, si alguien quisiera compartir un poco de sabiduría ... esta es una buena oportunidad.
Sanket Berde

@ArtjomB. sí, eso tiene sentido. Editado
Sanket Berde

5
@SanketBerde: Gracias por la nota rápida, pero descubrí que la imagen está saliendo de la memoria solo si la aplicación se está ejecutando en segundo plano (cuando está sin conexión). si cierro la aplicación, se borran las aplicaciones en ejecución, luego abro mi aplicación nuevamente, las imágenes no se cargan desde la caché. He configurado la imagen de carga predeterminada de error que aparece. ¿Qué podría estar mal aquí?
TheDevMan

1
Quizás Picasso haya cambiado la forma en que funcionan las cosas, porque para mí, funciona bien sin okhttp y la política de red. En un nuevo comienzo, obtiene instantáneamente imágenes del disco, y cuando la red está fuera de línea, todavía las muestra bien.
zeeshan

1
Con la okhttp3.OkHttpClientbiblioteca, debe usar el OkHttp3Downloaderformulario de clase compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1) respuesta a la primera pregunta: según Picasso Doc para el método With ()

La instancia global predeterminada de Picasso devuelta con () se inicializa automáticamente con valores predeterminados que son adecuados para la mayoría de las implementaciones.

  • Caché de memoria LRU del 15% de la RAM disponible de la aplicación
  • Caché en disco de 2% de espacio de almacenamiento hasta 50 MB pero no menos de 5 MB.

Pero Disk Cache operación para Picasso predeterminado global solo está disponible en API 14+

2) respuesta de la segunda pregunta: Picassouse la HTTPsolicitud del cliente para Disk Cacheoperar para que pueda hacer su propia http request headerpropiedad Cache-Controlconmax-age Y cree su propia Instancia de Picasso estática en lugar de la Picasso predeterminada Usando

1] HttpResponseCache (Nota: funciona solo para API 13+)
2] OkHttpClient (funciona para todas las API)

Ejemplo de uso OkHttpClientpara crear su propia clase Static Picasso:

  • Primero crea una nueva clase para obtener tu propio 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;
        }
    
    } 
  • usa tu propio picassoobjeto singleton en lugar dePicasso.With()

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

3) respuesta a la tercera pregunta: no necesita ningún permiso de disco para las operaciones de caché de disco

Referencias : problema de Github sobre la caché de disco , dos preguntas han sido respondidas por @ jake-wharton -> Pregunta1 y Pregunta2


4
No, esto no funciona si la aplicación está cerrada. Después de que la aplicación se detuvo, todas las imágenes desaparecieron.
nbumakov

2
esto me está dando este error:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCULO

@CIRCLE lo siento por tarde. Para usar el ejemplo, debe descargar primero el paquete [okhttp] ( square.github.io/okhttp ) y el paquete [okio] ( github.com/square/okio ) que utilizóokhttp
ahmed hamdy el

@CIRCLE puede ser que necesite descargar el paquete [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… ) también
ahmed hamdy

Esto no me funciona. Las imágenes se recargan cada vez que me desplazo a su posición en la vistaPager
Charu

21

Para el almacenamiento en caché, usaría interceptores OkHttp para obtener control sobre la política de almacenamiento en caché. Consulte este ejemplo que se incluye en la biblioteca OkHttp.

RewriteResponseCacheControl.java

Así es como lo usaría con 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
Ya no funciona networkInterceptors () devuelve una lista inmutable.
2016

1
@noev en OkHttp 3.x puede usar el patrón del constructor (consulte github.com/square/okhttp/wiki/Interceptors ) para agregar interceptores.
Gaurav B

10

Para la versión más actualizada 2.71828 Éstas son su respuesta.

P1 : ¿No tiene caché de disco local?

R1 : Hay almacenamiento en caché predeterminado dentro de Picasso y el flujo de solicitud es como este

App -> Memory -> Disk -> Server

Donde sea que encuentren su imagen primero, usarán esa imagen y luego detendrán el flujo de solicitudes. ¿Qué pasa con el flujo de respuesta? No se preocupe, aquí está.

Server -> Disk -> Memory -> App

De forma predeterminada, se almacenarán primero en un disco local para la caché de mantenimiento extendida. Luego, la memoria, para el uso de instancia de la caché.

Puede usar el indicador incorporado en Picasso para ver dónde se forman las imágenes habilitando esto.

Picasso.get().setIndicatorEnabled(true);

Aparecerá una bandera en la esquina superior izquierda de sus imágenes.

  • La bandera roja significa que las imágenes provienen del servidor. (Sin almacenamiento en caché en la primera carga)
  • Bandera azul significa que las fotos provienen del disco local. (Almacenamiento en caché)
  • Bandera verde significa que las imágenes provienen de la memoria. (Almacenamiento en caché de instancias)

P2 : ¿Cómo habilito el almacenamiento en caché de disco si usaré la misma imagen varias veces?

A2 : No es necesario que lo habilite. Es el predeterminado.

Lo que tendrá que hacer es DESACTIVARLO cuando desee que sus imágenes estén siempre frescas. Hay 2 vías de almacenamiento en caché deshabilitado.

  1. Establecer .memoryPolicy()a no_cache y / o NO_STORE y el flujo se verá así.

NO_CACHE omitirá la búsqueda de imágenes de la memoria.

App -> Disk -> Server

NO_STORE omitirá almacenar imágenes en la memoria cuando las imágenes se carguen por primera vez.

Server -> Disk -> App
  1. Establecer .networkPolicy()a no_cache y / o NO_STORE y el flujo se verá así.

NO_CACHE omitirá la búsqueda de imágenes del disco.

App -> Memory -> Server

NO_STORE omitirá almacenar imágenes en el disco cuando la primera carga de imágenes.

Server -> Memory -> App

No puede DESACTIVAR ninguno de los dos para las imágenes sin almacenamiento en caché. Aquí hay un ejemplo.

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

El flujo sin almacenamiento en caché completo y sin almacenamiento se verá así.

App -> Server //Request

Server -> App //Response

Por lo tanto, es posible que también lo necesite para minimizar el uso de almacenamiento de su aplicación.

P3 : ¿Necesito agregar algún permiso de disco al archivo de manifiesto de Android?

A3 : No, pero no olvide agregar el permiso de INTERNET para su solicitud HTTP.


6

1) Picasso por defecto tiene caché (ver la respuesta de ahmed hamdy)

2) Si realmente debe tomar una imagen de la memoria caché del disco y luego de la red, le recomiendo que escriba su propio descargador:

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

    }
}

Y en Application singleton en el método OnCreate úselo con 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) No se necesitan permisos para defalut la carpeta de caché de la aplicación


1

Agregue el siguiente código y Application.onCreateluego úselo normalmente

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

Si primero almacena imágenes en caché, haga algo como esto en 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);
        }

y cargue sus imágenes como de costumbre o con caché de disco

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

Nota:

El color rojo indica que la imagen se obtiene de la red .

El color verde indica que la imagen se obtiene de la memoria caché .

El color azul indica que la imagen se obtuvo de la memoria del disco .

Antes de lanzar la aplicación, elimínela o configúrela false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);si no es necesario. Thankx


1

No sé qué tan buena es esa solución, pero definitivamente es LA FÁCIL que acabo de usar en mi aplicación y está funcionando bien

cargas la imagen así

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

Puedes conseguir algo bimapasí

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

Ahora Bitmapconvierta eso en un JPGarchivo y guárdelo en el caché, a continuación se muestra el código completo para obtener el bimap y almacenarlo en caché

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

el get()método de Piccassopor alguna razón necesitaba ser llamado en un hilo separado, estoy guardando esa imagen también en ese mismo hilo.

Una vez que se guarda la imagen, puede obtener todos los archivos así

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

ahora puede encontrar el archivo que busca como a continuación

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

Utilicé este código y funcionó, tal vez sea útil para ti:

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

                            });
                }
            });

}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.