简体   繁体   English

如何在 Picasso 中使用磁盘缓存?

[英]How do I use disk caching in Picasso?

I am using Picasso to display image in my android app:我正在使用 Picasso 在我的 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);
}

I have enable debugging and it always shows either red and green .But never shows yellow我启用了调试,它总是显示红色和绿色。但从不显示黄色

Now if i load same image next time and internet is not available the image is not loaded.现在,如果我下次加载相同的图像并且互联网不可用,则不会加载图像。

Questions:问题:

  1. Does it not have local disk cache?它没有本地磁盘缓存吗?
  2. How do i enable disk caching as i will be using same image multiple times.我如何启用磁盘缓存,因为我将多次使用相同的图像。
  3. Do i need to add some disk permission to android manifest file?我需要为android清单文件添加一些磁盘权限吗?

This is what I did.这就是我所做的。 Works well.效果很好。

First add the OkHttp to the gradle build file of the app module:首先将 OkHttp 添加到 app 模块的 gradle 构建文件中:

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'

Then make a class extending Application然后创建一个扩展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);

    }
}

add it to the Manifest file as follows :将其添加到 Manifest 文件中,如下所示:

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

</application>

Now use Picasso as you normally would.现在像往常一样使用毕加索。 No changes.没有变化。

EDIT:编辑:

if you want to use cached images only.如果您只想使用缓存图像。 Call the library like this.像这样调用图书馆。 I've noticed that if we don't add the networkPolicy, images won't show up in an fully offline start even if they are cached .我注意到,如果我们不添加 networkPolicy,即使图像被缓存图像也不会在完全脱机启动时显示 The code below solves the problem.下面的代码解决了这个问题。

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

EDIT #2编辑#2

the problem with the above code is that if you clear cache, Picasso will keep looking for it offline in cache and fail, the following code example looks at the local cache, if not found offline, it goes online and replenishes the cache.上面代码的问题是,如果你清除缓存,Picasso会一直在缓存中离线查找并失败,下面的代码示例查看本地缓存,如果离线找不到,则在线并补充缓存。

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

1) answer of first question : according to Picasso Doc for With() method 1)第一个问题的答案:根据Picasso Doc for With() 方法

The global default Picasso instance returned from with() is automatically initialized with defaults that are suitable to most implementations.从 with() 返回的全局默认 Picasso 实例会自动使用适合大多数实现的默认值进行初始化。

  • LRU memory cache of 15% the available application RAM可用应用程序 RAM 的 15% 的 LRU 内存缓存
  • Disk cache of 2% storage space up to 50MB but no less than 5MB. 2% 存储空间的磁盘缓存,最高 50MB 但不低于 5MB。

But Disk Cache operation for global Default Picasso is only available on API 14+但是全局默认毕加索的Disk Cache操作仅在 API 14+ 上可用

2) answer of second Question : Picasso use the HTTP client request to Disk Cache operation So you can make your own http request header has property Cache-Control with max-age And create your own Static Picasso Instance instead of default Picasso By using 2)第二个问题的答案: Picasso使用HTTP客户端请求Disk Cache操作所以你可以让你自己的http request header具有属性Cache-Controlmax-age并创建你自己的静态毕加索实例而不是默认毕加索通过使用

1] HttpResponseCache (Note: Works only for API 13+ ) 1] HttpResponseCache (注意:仅适用于 API 13+)
2] OkHttpClient (Works for all APIs) 2] OkHttpClient (适用于所有 API)

Example for using OkHttpClient to create your own Static Picasso class:使用OkHttpClient创建您自己的静态毕加索类的示例

  • First create a new class to get your own singleton picasso object首先创建一个新类来获取自己的单身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; } }
  • use your own singleton picasso object Instead of Picasso.With()使用你自己的单例picasso对象而不是Picasso.With()

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

3) answer for third question : you do not need any disk permissions for disk Cache operations 3)回答第三个问题:磁盘缓存操作不需要任何磁盘权限

References : Github issue about disk cache , two Questions has been answered by @jake-wharton -> Question1 and Question2参考关于磁盘缓存的 Github 问题@jake-wharton已经回答了两个问题 -> Question1Question2

For caching, I would use OkHttp interceptors to gain control over caching policy.对于缓存,我会使用 OkHttp拦截器来控制缓存策略。 Check out this sample that's included in the OkHttp library.查看包含在 OkHttp 库中的这个示例。

RewriteResponseCacheControl.java 重写ResponseCacheControl.java

Here's how I'd use it with 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);

For the most updated version 2.71828 These are your answer.对于最新版本 2.71828 这些就是您的答案。

Q1 : Does it not have local disk cache? Q1 : 它没有本地磁盘缓存吗?

A1 : There is default caching within Picasso and the request flow just like this A1 : Picasso 中有默认缓存,请求流程就像这样

App -> Memory -> Disk -> Server

Wherever they met their image first, they'll use that image and then stop the request flow.无论他们在哪里首先遇到他们的图像,他们都会使用该图像,然后停止请求流。 What about response flow?响应流呢? Don't worry, here it is.别担心,它来了。

Server -> Disk -> Memory -> App

By default, they will store into a local disk first for the extended keeping cache.默认情况下,它们将首先存储到本地磁盘中以用于扩展保持缓存。 Then the memory, for the instance usage of the cache.然后是内存,用于缓存的实例使用。

You can use the built-in indicator in Picasso to see where images form by enabling this.您可以使用 Picasso 中的内置指示器通过启用它来查看图像的形成位置。

Picasso.get().setIndicatorEnabled(true);

It will show up a flag on the top left corner of your pictures.它会在图片的左上角显示一个标志。

  • Red flag means the images come from the server.红旗是指图像来自服务器。 (No caching at first load) (第一次加载时不缓存)
  • Blue flag means the photos come from the local disk.蓝旗意味着这些照片来自于本地磁盘。 (Caching) (缓存)
  • Green flag means the images come from the memory.绿旗表示图像来自内存。 (Instance Caching) (实例缓存)

Q2 : How do I enable disk caching as I will be using the same image multiple times? Q2 :我将多次使用同一个图像,如何启用磁盘缓存?

A2 : You don't have to enable it. A2 :您不必启用它。 It's the default.这是默认设置。

What you'll need to do is DISABLE it when you want your images always fresh.当您希望图像始终保持新鲜时,您需要做的是禁用它。 There is 2-way of disabled caching.有两种禁用缓存的方式。

  1. Set .memoryPolicy() to NO_CACHE and/or NO_STORE and the flow will look like this..memoryPolicy()设置为NO_CACHE和/或NO_STORE ,流程将如下所示。

NO_CACHE will skip looking up images from memory. NO_CACHE将跳过从内存中查找图像。

App -> Disk -> Server

NO_STORE will skip store images in memory when the first load images. NO_STORE将在第一次加载图像时跳过将图像存储在内存中。

Server -> Disk -> App
  1. Set .networkPolicy() to NO_CACHE and/or NO_STORE and the flow will look like this..networkPolicy()设置为NO_CACHE和/或NO_STORE ,流程将如下所示。

NO_CACHE will skip looking up images from disk. NO_CACHE将跳过从磁盘查找图像。

App -> Memory -> Server

NO_STORE will skip store images in the disk when the first load images. NO_STORE将在第一次加载图像时跳过存储在磁盘中的图像。

Server -> Memory -> App

You can DISABLE neither for fully no caching images.您可以DISABLE两者都不完全不缓存图像。 Here is an example.这是一个例子。

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

The flow of fully no caching and no storing will look like this.完全无缓存和无存储的流程将如下所示。

App -> Server //Request

Server -> App //Response

So, you may need this to minify your app storage usage also.因此,您可能还需要它来减少您的应用程序存储使用量。

Q3 : Do I need to add some disk permission to android manifest file?问题 3 :我需要为 android manifest 文件添加一些磁盘权限吗?

A3 : No, but don't forget to add the INTERNET permission for your HTTP request. A3 : 不,但不要忘记为您的 HTTP 请求添加 INTERNET 权限。

1) Picasso by default has cache (see ahmed hamdy answer) 1) Picasso 默认有缓存(见 ahmed hamdy 的回答)

2) If your really must take image from disk cache and then network I recommend to write your own downloader: 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;
        }

    }
}

And in Application singleton in method OnCreate use it with picasso:在方法 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) No permissions needed for defalut application cache folder 3)默认应用程序缓存文件夹不需要权限

I don't know how good that solution is but it is definitely THE EASY ONE i just used in my app and it is working fine我不知道该解决方案有多好,但它绝对是我刚刚在我的应用程序中使用的最简单的解决方案,并且运行良好

you load the image like that你像这样加载图像

public void loadImage (){
Picasso picasso = Picasso.get(); 
picasso.setIndicatorsEnabled(true);
picasso.load(quiz.getImageUrl()).into(quizImage);
}

You can get the bimap like that你可以得到这样的bimap

Bitmap bitmap = Picasso.get().load(quiz.getImageUrl()).get();

Now covert that Bitmap into a JPG file and store in the in the cache, below is complete code for getting the bimap and caching it现在将该Bitmap转换为JPG文件并存储在缓存中,下面是获取双图并缓存它的完整代码

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

try {
      Bitmap bitmap = Picasso.get().load(uri).get();
      FileOutputStream fOut = new FileOutputStream(file);                                        
      bitmap.compress(Bitmap.CompressFormat.JPEG, 100,new FileOutputStream(file));
fOut.flush();
fOut.close();
    }
catch (Exception e) {
  e.printStackTrace();
    }
   }
};
     thread.start();
  })

the get() method of Piccasso need to be called on separate thread , i am saving that image also on that same thread. Piccassoget()方法需要在单独的线程上调用,我也在同一个线程上保存该图像。

Once the image is saved you can get all the files like that保存图像后,您可以获得所有文件

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

now you can find the file you are looking for like below现在你可以找到你正在寻找的文件,如下所示

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.get() // if file found then load it
                            .load(file)
                            .into(mThumbnailImage);
                    return; // return 
                }
        // fetch it over the internet here because the file is not found
       }

Add followning code in Application.onCreate then use it normalApplication.onCreate添加以下代码然后正常使用

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

If you cache images first then do something like this in ProductImageDownloader.doBackground如果您先缓存图像,然后在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);
        }

and load your images like normal or with disk caching并像平常一样加载您的图像或使用磁盘缓存

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

Note:笔记:

Red color indicates that image is fetched from network .红色表示图像是从网络获取的。

Green color indicates that image is fetched from cache memory .绿色表示图像是从缓存中获取的。

Blue color indicates that image is fetched from disk memory .蓝色表示图像是从磁盘内存中获取的。

Before releasing the app delete or set it false picasso.setLoggingEnabled(true);在发布应用程序之前删除或将其设置为false picasso.setLoggingEnabled(true); , picasso.setIndicatorsEnabled(true); , picasso.setIndicatorsEnabled(true); if not required.如果不需要。 Thankx谢谢

I use this code and worked, maybe useful for you:我使用此代码并工作,可能对您有用:

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

                            });
                }
            });

}

I had the same problem and used Glide library instead.我遇到了同样的问题,而是使用了 Glide 库。 Cache is out of the box there.缓存是开箱即用的。 https://github.com/bumptech/glide https://github.com/bumptech/glide

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM