Làm cách nào để sử dụng bộ nhớ đệm đĩa trong Picasso?


119

Tôi đang sử dụng Picasso để hiển thị hình ảnh trong ứng dụng Android của mình:

/**
* 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);
}

Tôi đã bật gỡ lỗi và nó luôn hiển thị màu đỏ và xanh lục. Nhưng không bao giờ hiển thị màu vàng

Bây giờ nếu tôi tải cùng một hình ảnh vào lần sau và không có internet, hình ảnh sẽ không được tải.

Câu hỏi:

  1. Nó không có bộ nhớ cache trên đĩa cục bộ?
  2. Làm cách nào để kích hoạt bộ nhớ đệm đĩa vì tôi sẽ sử dụng cùng một hình ảnh nhiều lần.
  3. Tôi có cần thêm một số quyền đĩa vào tệp kê khai android không?

Tôi đang gặp vấn đề tương tự. Nó sẽ không lưu vào bộ nhớ cache!
Jonathan

Các bạn, các bạn nên xem qua facebook của Fresco lib. Quản lý bộ nhớ cache của nó thật tuyệt vời.
Michel Fortes

Câu trả lời:


229

Đây là những gì tôi đã làm. Hoạt động tốt.

Đầu tiên hãy thêm OkHttp vào tệp bản dựng gradle của mô-đun ứng dụng:

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'

Sau đó tạo một lớp mở rộng 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);

    }
}

thêm nó vào tệp kê khai như sau:

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

</application>

Bây giờ sử dụng Picasso như bạn thường làm. Không thay đổi.

BIÊN TẬP:

nếu bạn chỉ muốn sử dụng hình ảnh được lưu trong bộ nhớ cache. Gọi thư viện như thế này. Tôi nhận thấy rằng nếu chúng tôi không thêm networkPolicy, hình ảnh sẽ không hiển thị khi bắt đầu ngoại tuyến hoàn toàn ngay cả khi chúng được lưu vào bộ nhớ đệm . Đoạn mã dưới đây giải quyết vấn đề.

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

CHỈNH SỬA # 2

vấn đề với đoạn mã trên là nếu bạn xóa bộ nhớ cache, Picasso sẽ tiếp tục tìm kiếm nó ngoại tuyến trong bộ nhớ cache và không thành công, ví dụ mã sau đây sẽ xem xét bộ nhớ cache cục bộ, nếu không tìm thấy ngoại tuyến, nó sẽ trực tuyến và bổ sung bộ nhớ 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. , Tôi đã trả lời câu hỏi. Và giải pháp thực sự hoạt động. Nhưng có một chút làm rõ này tôi có thể sử dụng. Tôi đã xem qua tài liệu OkHttp và họ không đề cập đến đơn vị "bộ nhớ cache". Vì vậy, nếu có ai muốn chia sẻ một số thông thái ... đây là một cơ hội tốt.
Sanket Berde

@ArtjomB. vâng, điều đó có ý nghĩa. Đã chỉnh sửa!
Sanket Berde

5
@SanketBerde: Cảm ơn bạn đã ghi chú nhanh nhưng tôi đã phát hiện ra hình ảnh chỉ xuất hiện từ bộ nhớ nếu Ứng dụng đang chạy trong nền (khi ngoại tuyến). nếu tôi đóng ứng dụng, tức là xóa các ứng dụng đang chạy, sau đó mở lại ứng dụng của tôi, hình ảnh không tải từ Cache. Tôi đã đặt lỗi tải hình ảnh mặc định sắp xuất hiện. Điều gì có thể sai ở đây?
TheDevMan

1
Có lẽ Picasso đã thay đổi cách mọi thứ hoạt động, bởi vì đối với tôi, nó hoạt động tốt mà không cần okhttp và chính sách mạng. Khi bắt đầu mới, nó ngay lập tức lấy hình ảnh từ đĩa và khi mạng ngoại tuyến, nó vẫn hiển thị chúng tốt.
zeeshan

1
Với okhttp3.OkHttpClientthư viện, bạn phải sử dụng OkHttp3Downloaderbiểu mẫu lớp compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1) câu trả lời của câu hỏi đầu tiên: theo phương pháp Picasso Doc for With ()

Phiên bản Picasso mặc định chung được trả về từ with () được tự động khởi tạo với các giá trị mặc định phù hợp với hầu hết các triển khai.

  • Bộ nhớ đệm LRU bộ nhớ đệm 15% RAM ứng dụng khả dụng
  • Bộ nhớ đệm trên đĩa 2% không gian lưu trữ lên đến 50MB nhưng không dưới 5MB.

Nhưng Disk Cache hoạt động cho Picasso mặc định toàn cầu chỉ khả dụng trên API 14+

2) câu trả lời của câu hỏi thứ hai: Picassosử dụng HTTPyêu cầu của khách hàng để Disk Cachehoạt động Vì vậy, bạn có thể tạo http request headertài sản Cache-Controlmax-age của riêng bạn với Và tạo Phiên bản Picasso tĩnh của riêng bạn thay vì Picasso mặc định Bằng cách sử dụng

1] HttpResponseCache (Lưu ý: Chỉ hoạt động cho API 13+)
2] OkHttpClient (Hoạt động cho tất cả các API)

Ví dụ để sử dụng OkHttpClientđể tạo lớp Picasso tĩnh của riêng bạn:

  • Đầu tiên, hãy tạo một lớp mới để lấy picassođối tượng singleton của riêng bạn

    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;
        }
    
    } 
  • sử dụng picassođối tượng singleton của riêng bạn Thay vìPicasso.With()

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

3) câu trả lời cho câu hỏi thứ ba: bạn không cần bất kỳ quyền nào trên đĩa cho các hoạt động của Bộ nhớ đệm đĩa

Tài liệu tham khảo : Vấn đề trên Github về bộ nhớ cache trên đĩa , hai câu hỏi đã được trả lời bởi @ jake-wharton -> Question1Question2


4
Không, điều này không hoạt động nếu ứng dụng đã bị đóng. Sau khi ứng dụng bị dừng tất cả các hình ảnh biến mất.
nbumakov

2
đây là lỗi này cho tôi:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE

@CIRCLE xin lỗi vì đã trễ, Để sử dụng Ví dụ, trước tiên bạn cần tải xuống gói [okhttp] ( square.github.io/okhttp ) và gói [okio] ( github.com/square/okio ) được sử dụng bởiokhttp
ahmed hamdy

@CIRCLE có thể bạn cũng cần tải xuống gói [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… )
ahmed hamdy

Điều này không làm việc cho tôi. Hình ảnh đang được tải lại mỗi lần khi tôi di chuyển đến vị trí của họ trong ViewPager
Charu

21

Đối với bộ nhớ đệm, tôi sẽ sử dụng bộ chặn OkHttp để giành quyền kiểm soát chính sách bộ nhớ đệm. Hãy xem mẫu này có trong thư viện OkHttp.

RewriteResponseCacheControl.java

Đây là cách tôi sử dụng nó với 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
Không hoạt động nữa networkInterceptors () trả về một danh sách bất biến.
noev

1
@noev trong OkHttp 3.x, bạn có thể sử dụng mẫu trình tạo (xem github.com/square/okhttp/wiki/Interceptors ) để thêm các bộ đánh chặn.
Gaurav B

10

Đối với phiên bản cập nhật nhất 2.71828 Đây là câu trả lời của bạn.

Q1 : Nó không có bộ đệm đĩa cục bộ?

A1 : Có bộ nhớ đệm mặc định trong Picasso và luồng yêu cầu giống như thế này

App -> Memory -> Disk -> Server

Bất cứ nơi nào họ gặp hình ảnh của họ trước, họ sẽ sử dụng hình ảnh đó và sau đó dừng luồng yêu cầu. Còn về luồng phản hồi? Đừng lo lắng, nó đây.

Server -> Disk -> Memory -> App

Theo mặc định, chúng sẽ lưu trữ vào đĩa cục bộ trước tiên cho bộ nhớ đệm lưu giữ mở rộng. Sau đó là bộ nhớ, ví dụ như việc sử dụng bộ nhớ cache.

Bạn có thể sử dụng chỉ báo tích hợp trong Picasso để xem hình ảnh hình thành ở đâu bằng cách bật tính năng này.

Picasso.get().setIndicatorEnabled(true);

Nó sẽ hiển thị một lá cờ ở góc trên bên trái của ảnh của bạn.

  • Cờ đỏ có nghĩa là hình ảnh đến từ máy chủ. (Không có bộ nhớ đệm ở lần tải đầu tiên)
  • Cờ xanh có nghĩa là ảnh đến từ đĩa cục bộ. (Bộ nhớ đệm)
  • Cờ xanh có nghĩa là hình ảnh đến từ ký ức. (Bộ nhớ đệm phiên bản)

Câu hỏi 2 : Làm cách nào để kích hoạt bộ nhớ đệm đĩa vì tôi sẽ sử dụng cùng một hình ảnh nhiều lần?

A2 : Bạn không cần phải kích hoạt nó. Đó là mặc định.

Điều bạn cần làm là TẮT nó khi bạn muốn hình ảnh của mình luôn mới. Có 2 cách để tắt bộ nhớ đệm.

  1. Đặt .memoryPolicy()thành NO_CACHE và / hoặc NO_STORE và quy trình sẽ như thế này.

NO_CACHE sẽ bỏ qua việc tìm kiếm hình ảnh từ bộ nhớ.

App -> Disk -> Server

NO_STORE sẽ bỏ qua hình ảnh lưu trữ trong bộ nhớ khi hình ảnh tải đầu tiên.

Server -> Disk -> App
  1. Đặt .networkPolicy()thành NO_CACHE và / hoặc NO_STORE và quy trình sẽ như thế này.

NO_CACHE sẽ bỏ qua việc tìm kiếm hình ảnh từ đĩa.

App -> Memory -> Server

NO_STORE sẽ bỏ qua hình ảnh lưu trữ trong đĩa khi hình ảnh tải đầu tiên.

Server -> Memory -> App

Bạn không thể TẮT nếu hoàn toàn không có ảnh trong bộ nhớ đệm. Đây là một ví dụ.

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

Dòng hoàn toàn không có bộ nhớ đệm và không có lưu trữ sẽ trông như thế này.

App -> Server //Request

Server -> App //Response

Vì vậy, bạn có thể cần điều này để giảm thiểu việc sử dụng bộ nhớ ứng dụng của mình.

Câu hỏi 3 : Tôi có cần thêm một số quyền đĩa vào tệp kê khai android không?

A3 : Không, nhưng đừng quên thêm quyền INTERNET cho yêu cầu HTTP của bạn.


6

1) Picasso theo mặc định có bộ nhớ cache (xem câu trả lời của ahmed hamdy)

2) Nếu bạn thực sự phải lấy hình ảnh từ bộ nhớ cache của đĩa và mạng, tôi khuyên bạn nên viết trình tải xuống của riêng bạn:

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

    }
}

Và trong Application singleton trong phương thức OnCreate sử dụng nó với 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) Không có quyền cần thiết cho thư mục bộ đệm ẩn ứng dụng khửalut


1

Thêm mã theo dõi vào Application.onCreaterồi sử dụng bình thường

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

Nếu bạn lưu hình ảnh vào bộ nhớ cache trước thì hãy làm như thế này trong 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);
        }

và tải hình ảnh của bạn như bình thường hoặc bằng bộ nhớ đệm đĩa

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

Ghi chú:

Màu đỏ cho biết rằng hình ảnh được tải từ mạng .

Màu xanh lá cây cho biết rằng hình ảnh được tìm nạp từ bộ nhớ đệm .

Màu xanh lam cho biết rằng hình ảnh được tìm nạp từ bộ nhớ đĩa .

Trước khi phát hành ứng dụng, hãy xóa hoặc đặt nó false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);nếu không cần thiết. Thankx


1

Tôi không biết giải pháp đó tốt như thế nào nhưng nó chắc chắn là giải pháp DỄ DÀNG NHẤT mà tôi vừa sử dụng trong ứng dụng của mình và nó đang hoạt động tốt

bạn tải hình ảnh như vậy

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

Bạn có thể nhận được bimapnhư vậy

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

Bây giờ hãy giấu nó Bitmapvào một JPGtệp và lưu trữ trong bộ nhớ cache, dưới đây là mã hoàn chỉnh để lấy bimap và lưu vào bộ nhớ đệm

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

các get()phương pháp Piccassođối với một số lý do cần phải kêu gọi các chủ đề riêng biệt, tôi đang tiết kiệm hình ảnh mà còn về cùng chủ đề.

Sau khi hình ảnh được lưu, bạn có thể nhận được tất cả các tệp như vậy

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

bây giờ bạn có thể tìm thấy tệp bạn đang tìm kiếm như bên dưới

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

Tôi sử dụng mã này và đã hoạt động, có thể hữu ích cho bạn:

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

                            });
                }
            });

}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.