Picasso에서 디스크 캐싱을 어떻게 사용합니까?


119

내 Android 앱에 이미지를 표시하기 위해 Picasso를 사용하고 있습니다.

/**
* 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 매니페스트 파일에 디스크 권한을 추가해야합니까?

나는 같은 문제가 있습니다. 캐시되지 않습니다!
— Jonathan

여러분, facebook´s Fresco lib를 보셔야합니다. 캐시 관리는 굉장합니다.
— Michel Fortes

답변:


229

이것이 내가 한 일입니다. 잘 작동합니다.

먼저 앱 모듈의 gradle 빌드 파일에 OkHttp를 추가합니다.

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>

이제 평소처럼 Picasso를 사용하십시오. 변경 사항 없음.

편집하다:

캐시 된 이미지 만 사용하려는 경우. 이렇게 도서관에 전화하십시오. 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");
            }
        });
    }
});

@ArtjomB. , 나는 질문에 대답했다. 그리고 솔루션이 작동합니다. 하지만 제가 사용할 수있는 약간의 설명이 있습니다. OkHttp 문서를 살펴 봤는데 "캐시"단위는 언급하지 않았습니다. 그러니 누군가가 지혜를 나누고 싶다면 ... 이것은 좋은 기회입니다.
— Sanket Berde 2015 년

@ArtjomB. 그렇습니다. 편집!
— Sanket Berde 2015 년

5
@SanketBerde : 빠른 메모에 감사하지만 앱이 백그라운드에서 실행중인 경우 (오프라인 상태 일 때)에만 이미지가 메모리에서 나오는 것을 알아 냈습니다. 앱을 닫으면 실행중인 앱이 지워지고 내 앱을 다시 열면 이미지가 캐시에서로드되지 않습니다. 오는 오류 기본 로딩 이미지를 설정했습니다. 여기서 무엇이 잘못 되었을까요?
— TheDevMan 2015-08-31

1
아마도 Picasso가 작동 방식을 변경했을 수 있습니다. 왜냐하면 나에게는 okhttp 및 네트워크 정책 없이도 잘 작동하기 때문입니다. 새로 시작하면 즉시 디스크에서 이미지를 가져오고 네트워크가 오프라인 상태 일 때도 여전히 잘 표시됩니다.
— zeeshan

1
함께 okhttp3.OkHttpClient도서관 당신은 사용해야하는 OkHttp3Downloader클래스 양식 compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
— 척

46

1) 첫 번째 질문에 대한 답변 : Picasso Doc for With () 메서드 에 따름

with ()에서 반환 된 전역 기본 Picasso 인스턴스는 대부분의 구현에 적합한 기본값으로 자동 초기화됩니다.

  • 사용 가능한 애플리케이션 RAM의 15 %의 LRU 메모리 캐시
  • 2 % 스토리지 공간의 디스크 캐시는 최대 50MB이지만 5MB 이상입니다.

그러나 Disk Cache 전역 Default Picasso에 대한 작업은 API 14 이상에서만 사용할 수 있습니다.

2) 두 번째 질문의 답 : Picasso사용 HTTP에 대한 클라이언트 요청 Disk Cache작업을 그래서 당신은 당신의 자신 할 수는 http request header속성이 Cache-Control과max-age 그리고 사용하여 대신 기본의 피카소를 자신의 정적 피카소 인스턴스를 생성

1] HttpResponseCache (참고 : API 13 이상에서만 작동)
2] OkHttpClient (모든 API에서 작동)

OkHttpClient고유 한 정적 Picasso 클래스를 만드는 데 사용 하는 예 :

  • 먼저 자신의 싱글 톤 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- > Question1 및 Question2 가 두 가지 질문에 답변했습니다.


4
아니요, 앱이 닫혀 있으면 작동하지 않습니다. 앱이 강제 종료 된 후 모든 이미지가 사라졌습니다.
— nbumakov

2
이것은 나에게이 오류를줍니다 :FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
— CIRCLE

@CIRCLE 늦게 죄송합니다. 사용 예, 먼저 [okhttp] ( square.github.io/okhttp ) 패키지와 [okio] ( github.com/square/okio ) 패키지 를 다운로드해야합니다.okhttp
— ahmed hamdy

@CIRCLE는 [okhttp - URLConnection의 (다운로드 할 필요가있을 수 있습니다 mvnrepository.com/artifact/com.squareup.okhttp/... ) 패키지도
— 아메드 hamdy

이것은 나를 위해 작동하지 않습니다. 내가 스크롤 할 때 이미지 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 ()는 불변 목록을 반환합니다.
— noev

1
OkHttp 3.x의 @noev에서는 빌더 패턴 ( github.com/square/okhttp/wiki/Interceptors 참조 )을 사용하여 인터셉터를 추가 할 수 있습니다 .
— Gaurav B

10

최신 버전 2.71828에 대한 답변입니다.

Q1 : 로컬 디스크 캐시가 없습니까?

A1 : Picasso 내에 기본 캐싱이 있으며 이와 같은 요청 흐름이 있습니다.

App -> Memory -> Disk -> Server

먼저 이미지를 만난 곳에서 해당 이미지를 사용한 다음 요청 흐름을 중지합니다. 응답 흐름은 어떻습니까? 걱정하지 마세요. 여기 있습니다.

Server -> Disk -> Memory -> App

기본적으로 확장 유지 캐시를 위해 먼저 로컬 디스크에 저장됩니다. 그런 다음 캐시의 인스턴스 사용량에 대한 메모리입니다.

피카소의 내장 표시기를 사용하여 이미지가 형성되는 위치를 확인할 수 있습니다.

Picasso.get().setIndicatorEnabled(true);

사진의 왼쪽 상단에 깃발이 표시됩니다.

  • 빨간색 플래그는 이미지가 서버에서 온다는 의미입니다. (첫 번째로드시 캐싱 없음)
  • 파란색 플래그는 사진이 로컬 디스크에서 가져온 것을 의미합니다. (캐싱)
  • 녹색 플래그는 이미지가 메모리에서 온다는 것을 의미합니다. (인스턴스 캐싱)

Q2 : 동일한 이미지를 여러 번 사용할 것이므로 디스크 캐싱을 활성화하려면 어떻게해야합니까?

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

따라서 앱 스토리지 사용량을 최소화하기 위해 필요할 수도 있습니다.

Q3 : 안드로이드 매니페스트 파일에 디스크 권한을 추가해야합니까?

A3 : 아니요,하지만 HTTP 요청에 대한 INTERNET 권한을 추가하는 것을 잊지 마십시오.


6

1) Picasso에는 기본적으로 캐시가 있습니다 (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 singleton에서 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) 기본 애플리케이션 캐시 폴더에 필요한 권한 없음


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);필요하지 않은 경우. Thankx


1

그 솔루션이 얼마나 좋은지는 모르겠지만 내 앱에서 방금 사용한 EASY ONE 이며 잘 작동합니다.

당신은 그런 이미지를로드합니다

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

이제 은밀한 Bitmap로를JPG 파일 하고 캐시에 저장합니다. 아래는 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...");
                                }

                            });
                }
            });

}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.