如何在毕加索中使用磁盘缓存?


119

我正在使用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);
}

我已启用调试,并且始终显示红色和绿色。但是从不显示黄色

现在,如果我下次加载同一张图片,并且互联网不可用,则无法加载图片。

问题:

  1. 它没有本地磁盘缓存吗?
  2. 我如何启用磁盘缓存,因为我将多次使用相同的映像。
  3. 我是否需要向Android清单文件添加一些磁盘权限?

我有同样的问题。它不会缓存!
乔纳森

伙计们,您应该看一下facebook的壁画库。它的缓存管理很棒。
米歇尔·福特

Answers:


229

这就是我所做的。效果很好。

首先将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'

然后进行课堂扩展 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

1
使用okhttp3.OkHttpClient图书馆,您必须使用OkHttp3Downloader课堂形式 compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1)第一个问题的答案:根据Picasso Doc for With()方法

从with()返回的全局默认Picasso实例会自动使用适合大多数实现的默认值进行初始化。

  • LRU内存缓存为可用应用程序RAM的15%
  • 2%存储空间的磁盘缓存,最大为50MB,但不少于5MB。

但是 Disk Cache全局默认毕加索的操作仅在API 14+上可用

2)第二个问题的答案:Picasso使用HTTP客户端请求进行Disk Cache操作,这样您就可以自己制作http request headerCache-Control使用来max-age 创建 has属性,并创建自己的静态Picasso实例而不是默认Picasso。

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)回答第三个问题:磁盘缓存操作不需要任何磁盘权限

参考Github有关磁盘缓存的问题,@ jake-wharton- > Question1Question2已回答两个问题


4
不,如果应用已关闭,则无法使用。强制停止应用后,所有图像均消失。
nbumakov 2015年

2
这是给我这个错误:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE

@CIRCLE很抱歉,要使用示例,您需要先下载okhttp
ahmed hamdy

@CIRCLE可能是您也需要下载[okhttp-urlconnection](mvnrepository.com/artifact/com.squareup.okhttp/…)软件包
ahmed hamdy 2015年

这对我不起作用。图像被每次刷新时我滚动到他们的位置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()返回一个不可变的列表。
2013年

1
在OkHttp 3.x中,@ noev可以使用构建器模式(请参见github.com/square/okhttp/wiki/Interceptors)添加拦截器。
Gaurav B

10

对于最新版本2.71828,这是您的答案。

Q1:它没有本地磁盘缓存吗?

A1:毕加索内有默认缓存,请求流就是这样

App -> Memory -> Disk -> Server

无论他们在哪里遇到图像,都将使用该图像,然后停止请求流。响应流程如何?别担心,这里。

Server -> Disk -> Memory -> App

默认情况下,它们将首先存储到本地磁盘中以用于扩展保留缓存。然后是内存,以供实例使用缓存。

您可以通过启用此功能,在Picasso中使用内置指示器来查看图像的形成位置。

Picasso.get().setIndicatorEnabled(true);

它将在图片的左上角显示一个标志。

  • 红色标志表示图像来自服务器。(第一次加载时不缓存)
  • 蓝色标志意味着该照片来自本地磁盘。(正在缓存)
  • 绿色标记表示图像来自内存。(实例缓存)

问题2:如何启用磁盘缓存,因为我将多次使用同一映像?

A2:您不必启用它。这是默认值。

什么你需要做的是DISABLE,当你想你的形象永远是新鲜的吧。有2种禁用缓存的方式。

  1. 设置.memoryPolicy()NO_CACHE和/或NO_STORE,流将如下所示。

NO_CACHE将跳过从内存中查找图像。

App -> Disk -> Server

第一次加载图像时,NO_STORE将跳过将图像存储在内存中。

Server -> Disk -> App
  1. 设置.networkPolicy()NO_CACHE和/或NO_STORE,流将如下所示。

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答案)

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的Application单例中,将其与毕加索一起使用:

        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

我不知道这种解决方案有多好,但绝对是最简单的一种我在我的应用程序中使用过的,并且运行良好

你像这样加载图像

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

现在将其隐藏BitmapJPG文件中并存储在缓存中,以下是获取bimap并将其缓存的完整代码

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

                            });
                }
            });

}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.