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-Controlmax-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- > Question1Question2 가 두 가지 질문에 답변했습니다.


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.