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

时间:2014-06-01 10:19:55

标签: android image caching picasso

我正在使用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清单文件添加一些磁盘权限?

9 个答案:

答案 0 :(得分:220)

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

首先将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);

    }
}

将其添加到Manifest文件中,如下所示:

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

</application>

现在像往常一样使用毕加索。没有变化。

修改

如果您只想使用缓存图像。像这样调用库。我注意到,如果我们不添加networkPolicy,那么图片在完全离线的开始中就不会显示,即使它们已被缓存 。下面的代码解决了这个问题。

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

编辑#2

上面代码的问题是,如果你清除缓存,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 :(得分:45)

1)第一个问题的回答: 根据{{​​3}}

  

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

     
      
  • LRU内存缓存可用应用程序RAM的15%
  •   
  • 2%存储空间的磁盘缓存高达50MB但不低于5MB。
  •   

Disk Cache全局默认毕加索的操作仅适用于API 14 +

2)第二个问题的回答:Picasso使用HTTP客户请求Disk Cache操作所以你可以使你的自己http request header的财产Cache-Controlmax-age 并使用

创建自己的静态毕加索实例而不是默认的毕加索

1] Picasso Doc for With() method(注意:仅适用于API 13+)
2] HttpResponseCache(适用于所有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)回答第三个问题:磁盘缓存操作不需要任何磁盘权限

参考文献OkHttpClientGithub issue about disk cache已回答了两个问题 - &gt; @jake-whartonQuestion1

答案 2 :(得分: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);

答案 3 :(得分: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方法的应用程序单例中,将它与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)defalut应用程序缓存文件夹

无需权限

答案 4 :(得分:2)

我不知道该解决方案有多好,但绝对是容易 一个,我刚刚在我的应用中使用过,并且运行正常

您这样加载图像

select id,
       (sum(datediff(second, start_time, end_time) -
        datediff(second, min(start_time), max(end_time)
       ) as overlap
from t
group by id;

您可以像这样获得join

import asyncio

@asyncio.coroutine
def async_copyfile(src, dst):
    yield shutil.copyfile(src, dst)    

def generate_tasks():
    goo_dst = '/home/usr2/Pictures/GOO'
    amz_dst = '/home/usr2/Pictures/AMZ'
    os.makedirs(goo_dst,exist_ok=1)
    os.makedirs(amz_dst,exist_ok=1)
    for root, dirs, files in os.walk('/'):
        for name in files:
            path = os.path.join(root, name)
            if name.startswith('GOO_') and (name.endswith('.jpg') or name.endswith('.jpeg') or name.endswith('.png')):
                yield (path, goo_dst)
            elif name.startswith('AMZ_') and name.endswith('.jpg'):
                yield (path, amz_dst)

loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(async_copyfile(src, dst)) for src, dst in generate_tasks()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

现在将public void loadImage (){ Picasso picasso = Picasso.get(); picasso.setIndicatorsEnabled(true); picasso.load(quiz.getImageUrl()).into(quizImage); } 转换为bimap文件并存储在缓存中,下面是获取bimap并将其缓存的完整代码

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

出于某种原因需要在单独的线程上调用Bitmap的{​​{1}}方法,我也将该图像保存在同一线程上。

保存图像后,您可以获取所有类似的文件

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

答案 5 :(得分: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...");
                                }

                            });
                }
            });

}

答案 6 :(得分:0)

/** Reset the default state of the buttons */ func resetButtonStates() { for button in [YOUR_BUTTON_ARRAY_VARIABLE_HERE] { button.isSelected = false button.setTitleColor(UIColor.gray, for: .normal) } } func buttonPressed(sender:AnyObject) { guard let button = sender as? UIButton else { return } // Check wether the button is selected already, to be used as mentioned below let isAlreadySelected = sender.isSelected == true // Reset the default state to all the buttons resetButtonStates() // Now update the button state of the selected button alone if its not selected already if !isAlreadySelected { button.isSelected = true button.setTitleColor(UIColor.black, for: .normal) } else { // Do Nothing since as per your case if you selected the already selected button it should change to disable right, so the resetButtonStates() will do that. } } 中添加跟随代码,然后正常使用

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); falsepicasso.setLoggingEnabled(true);(如果不需要)。 Thankx

答案 7 :(得分:0)

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

第一季度:它没有本地磁盘缓存吗?

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

App -> Memory -> Disk -> Server

无论他们何时遇到他们的图像,他们都会使用该图像,然后停止请求流。 响应流程如何?不用担心,这是这里。

Server -> Disk -> Memory -> App

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

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

Picasso.get().setIndicatorEnabled(true);

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

  • 红色标志表示图像来自服务器。 (首次加载时不缓存)
  • 蓝色标志表示照片来自本地磁盘。 (缓存)
  • 绿色标志表示图像来自内存。 (实例缓存)

第二季度:如何启用磁盘缓存,因为我将多次使用同一图像?

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

您需要做的是禁用,当您希望图像始终保持新鲜时。有两种禁用缓存的方式。

  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

因此,您可能还需要这样做以最大程度地减少应用程序的存储使用量。

第3季度:我需要向Android清单文件添加一些磁盘权限吗?

A3 :否,但不要忘记为HTTP请求添加INTERNET权限。

答案 8 :(得分:-2)

我遇到了同样的问题并使用了Glide库。缓存是开箱即用的。 https://github.com/bumptech/glide