안 드 로 이 드 개발 노트 (77) 이미지 캐 시 알고리즘
16121 단어 android그림 캐 시imagecachePicasso
캐 시 정책 이미지 캐 시 는 일반적으로 메모리, 디스크 파일, 네트워크 그림 으로 나 뉜 다.정상 적 인 상황 에서 app 은 먼저 메모리 에 가서 그림 을 찾 고 찾 으 면 메모리 에 있 는 그림 을 직접 표시 합 니 다.메모리 가 찾 지 못 하면 디스크 에서 찾 고 찾 으 면 디스크 그림 을 읽 고 표시 합 니 다.디스크 도 찾 지 못 하면 uri 에 따라 인터넷 으로 그림 을 다운로드 하고 다운로드 에 성공 하면 그림 을 표시 해 야 합 니 다.3 급 캐 시 를 통 해 네트워크 속도 가 느 리 거나 네트워크 가 끊 겨 도 app 은 일부 그림 을 신속하게 불 러 와 사용자 체험 을 향상 시 킬 수 있 습 니 다.
메모리 캐 시 데이터 구 조 는 맵 표 HashMap 을 사용 하여 유일한 uri 를 통 해 이미지 의 Bitmap 대상 을 찾 을 수 있 습 니 다.대기 열 알고리즘 은 일반적으로 먼저 FIFO 정책 을 사용 합 니 다. FIFO 는 대기 열 양 끝 을 조작 해 야 하 는 것 을 고려 하여 대기 열 상단 에서 넘 치 는 그림 을 제거 하고 새로 추 가 된 그림 을 대기 열 말단 에 추가 하기 때문에 대기 열 캐 시 는 양 끝 대기 열 LinkedList 를 사용 합 니 다.매 핑 테이블 과 더 블 엔 드 대기 열 에 대한 소 개 는 '안 드 로 이 드 개발 노트 (26) 자바 의 용기 류' 를 참조 합 니 다. 또한 더 블 엔 드 대기 열 을 동시에 조작 하여 불필요 한 자원 충돌 을 일 으 키 는 것 을 방지 하기 위해 관련 방법 을 설명 할 때 synchronized 키 워드 를 추가 해 야 합 니 다.
디스크 작업 은 두 조각 으로 나 뉘 는데 하 나 는 그림 파일 을 만 드 는 캐 시 디 렉 터 리 입 니 다. 먼저 캐 시 디 렉 터 리 가 존재 하 는 지 확인 하고 존재 하지 않 으 면 디 렉 터 리 를 만 듭 니 다.그 다음 에 해시 값 에 따라 그림 파일 이 존재 하 는 지 확인 하고 존재 하면 그림 을 읽 으 며 존재 하지 않 으 면 네트워크 로 이동 합 니 다.디 렉 터 리 와 파일 에 대한 소 개 는 '안 드 로 이 드 개발 노트 (32) 파일 기초 작업' 을 참조 합 니 다.다른 하 나 는 파일 에서 Bitmap 대상 을 읽 고 쓰 는 것 이 며, 그림 파일 의 읽 기와 쓰기 동작 은 '안 드 로 이 드 개발 노트 (33) 텍스트 파일 과 그림 파일 의 읽 기와 쓰기' 를 참조 합 니 다.
정책 그림 을 다운로드 하여 메모리 와 디스크 에서 찾 을 수 없 으 니 네트워크 에서 그림 을 가 져 올 수 밖 에 없습니다.http 주소 에 따라 그림 을 가 져 오 는 것 은 GET 방식 을 사용 하고 구체 적 인 인 인 코딩 은 을 참조 합 니 다.
액세스 네트워크 는 비동기 작업 에 속 하기 때문에 메 인 스 레 드 에서 직접 처리 할 수 없 기 때문에 스 레 드 를 따로 열 어야 합 니 다. 비동기 방식 을 소통 하 는 Handler 는 을 참조 합 니 다.또한 이미지 캐 시가 여러 장의 그림 을 동시에 방문 할 수 있 음 을 고려 하여 효율 을 높이 기 위해 스 레 드 탱크 를 도입 하고 스 레 드 탱크 대상 이 이미지 다운로드 작업 을 통일 적 으로 관리 해 야 한다. 스 레 드 탱크 의 소 개 는 을 참조 한다.
디 스 플레이 전략 과 관련 최 적 화 는 천신만고 끝 에 그림 을 3 급 캐 시 에서 찾 아 냈 습 니 다. 지금 은 ImageView 컨트롤 에 그림 을 표시 하려 면 보통 페 이 드 페 이 드 애니메이션 효 과 를 사용 합 니 다. 갑 작 스 럽 지 않 고 페 이 드 애니메이션 의 용법 은 을 참조 합 니 다.메모리 에 이 그림 이 존재 한다 면 페 이 드 애니메이션 에 들 어가 지 않 아 도 됩 니 다.네트워크 에서 그림 을 가 져 와 야 사용자 가 기 다 려 야 하 는 상황 에서 페이드아웃 효과 가 필요 합 니 다.
또한 사용자 체험 을 향상 시 키 기 위해 그림 을 불 러 오기 전에 원래 의 그림 위치 에 자리 잡 은 그림 을 먼저 놓는다.그림 불 러 오 는 데 실 패 했 을 경우 원본 그림 위치 에서 잘못된 그림 이나 기본 그림 을 알려 줍 니 다.이 자리 잡 은 그림 과 오류 그림 은 캐 시 정 보 를 설정 할 때 설정 할 수 있 습 니 다.
그림 캐 시 는 성능 을 향상 시 키 는 동시에 메모리 누 출 예방 을 잊 지 마 세 요.Handler 대상 과 Bitmap 대상 모두 메모리 누 출 위험 이 존재 하기 때문에 우 리 는 Handler 대상 의 인용 을 신속하게 방출 하고 Bitmap 대상 의 데 이 터 를 신속하게 회수 하 며 구체 적 인 최적화 처 리 는 을 참조 해 야 한다.
코드 예제 다음은 그림 캐 시 를 위 한 간단 한 구현 코드 예 입 니 다.
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
public class ImageCache {
private final static String TAG = "ImageCache";
//
private HashMap<String, Bitmap> mImageMap = new HashMap<String, Bitmap>();
//uri
private HashMap<String, ImageView> mViewMap = new HashMap<String, ImageView>();
// , FIFO , ,
private LinkedList<String> mUriList = new LinkedList<String>();
private ImageCacheConfig mConfig;
private String mDir = "";
private ThreadPoolExecutor mPool;
private static Handler mMyHandler;
private static ImageCache mCache = null;
private static Context mContext;
public static ImageCache getInstance(Context context) {
if (mCache == null) {
mCache = new ImageCache();
mCache.mContext = context;
}
return mCache;
}
public ImageCache initConfig(ImageCacheConfig config) {
mCache.mConfig = config;
mCache.mDir = mCache.mConfig.mDir;
if (mCache.mDir==null || mCache.mDir.length()<=0) {
mCache.mDir = Environment.getExternalStorageDirectory() + "/image_cache";
}
Log.d(TAG, "mDir="+mCache.mDir);
// ,
File dir = new File(mCache.mDir);
if (dir.exists() != true) {
dir.mkdirs();
}
mCache.mPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(mCache.mConfig.mThreadCount);
mCache.mMyHandler = new MyHandler((Activity)mCache.mContext);
return mCache;
}
public void show(String uri, ImageView iv) {
if (mConfig.mBeginImage != 0) {
iv.setImageResource(mConfig.mBeginImage);
}
mViewMap.put(uri, iv);
if (mImageMap.containsKey(uri) == true) {
mCache.render(uri, mImageMap.get(uri));
} else {
String path = getFilePath(uri);
if ((new File(path)).exists() == true) {
Bitmap bitmap = ImageUtil.openBitmap(path);
if (bitmap != null) {
mCache.render(uri, bitmap);
} else {
mPool.execute(new MyRunnable(uri));
}
} else {
mPool.execute(new MyRunnable(uri));
}
}
}
private String getFilePath(String uri) {
String file_path = String.format("%s/%d.jpg", mDir, uri.hashCode());
return file_path;
}
private static class MyHandler extends Handler {
public static WeakReference<Activity> mActivity;
public MyHandler(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity act = mActivity.get();
if (act != null) {
ImageData data = (ImageData) (msg.obj);
if (data!=null && data.bitmap!=null) {
mCache.render(data.uri, data.bitmap);
} else {
mCache.showError(data.uri);
}
}
}
}
private class MyRunnable implements Runnable {
private String mUri;
public MyRunnable(String uri) {
mUri = uri;
}
@Override
public void run() {
Activity act = MyHandler.mActivity.get();
if (act != null) {
Bitmap bitmap = ImageHttp.getImage(mUri);
if (bitmap != null) {
if (mConfig.mSize != null) {
bitmap = Bitmap.createScaledBitmap(bitmap, mConfig.mSize.x, mConfig.mSize.y, false);
}
ImageUtil.saveBitmap(getFilePath(mUri), bitmap);
}
ImageData data = new ImageData(mUri, bitmap);
Message msg = mMyHandler.obtainMessage();
msg.obj = data;
mMyHandler.sendMessage(msg);
}
}
};
private void render(String uri, Bitmap bitmap) {
ImageView iv = mViewMap.get(uri);
if (mConfig.mFadeInterval <= 0) {
iv.setImageBitmap(bitmap);
} else {
// ,
if (mImageMap.containsKey(uri) == true) {
iv.setImageBitmap(bitmap);
} else {
iv.setAlpha(0.0f);
AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
alphaAnimation.setDuration(mConfig.mFadeInterval);
alphaAnimation.setFillAfter(true);
iv.setImageBitmap(bitmap);
iv.setAlpha(1.0f);
iv.setAnimation(alphaAnimation);
alphaAnimation.start();
mCache.refreshList(uri, bitmap);
}
}
}
private synchronized void refreshList(String uri, Bitmap bitmap) {
if (mUriList.size() >= mConfig.mMemoryFileCount) {
String out_uri = mUriList.pollFirst();
mImageMap.remove(out_uri);
}
mImageMap.put(uri, bitmap);
mUriList.addLast(uri);
}
private void showError(String uri) {
ImageView iv = mViewMap.get(uri);
if (mConfig.mErrorImage != 0) {
iv.setImageResource(mConfig.mErrorImage);
}
}
public void clear() {
for (Map.Entry<String, Bitmap> item_map : mImageMap.entrySet()) {
Bitmap bitmap = item_map.getValue();
bitmap.recycle();
}
mCache = null;
}
}
다음은 이 캐 시 호출 코드 예제 입 니 다.
ImageCacheConfig config = new ImageCacheConfig.Builder()
.setBeginImage(R.drawable.bliss)
.setErrorImage(R.drawable.error)
.setFadeInterval(2000)
.build();
ImageCache.getInstance(this).initConfig(config).show(file, iv_hello);
picasso picasso 는 Square 회사 가 오픈 한 안 드 로 이 드 이미지 캐 시 라 이브 러 리 로 사용 이 상대 적 으로 간단 하 며, 일반적으로 코드 한 마디 만 있 으 면 그림 을 다운로드 하여 보기 에 표시 할 수 있 습 니 다.
Picasso Picasso 는 주요 처리 클래스 입 니 다. 자주 사용 하 는 방법 은 다음 과 같 습 니 다.
with: 정적 방법.기본 인 스 턴 스 를 초기 화 합 니 다.
setSingleton Instance: 정적 방법.설 정 된 인 스 턴 스 를 지정 합 니 다.
setIndicators Enabled: 플래그 를 사용 할 수 있 는 지 설정 합 니 다.사실은 개발 모델 로 그림 왼쪽 상단 에 삼각 표 지 를 표시 하고 녹색 은 그림 을 메모리 에서 추출 하 는 것 을 나타 내 며 파란색 은 디스크 에서 추출 하 는 것 을 나타 내 고 빨간색 은 네트워크 에서 추출 하 는 것 을 나타 낸다.
setLoggingEnabled: 로 그 를 사용 할 수 있 는 지 설정 합 니 다.
load: 지정 한 위치 에서 그림 을 불 러 옵 니 다.이 방법 은 후속 처리 에 사용 할 수 있 도록 RequestCreator 대상 을 되 돌려 줍 니 다.
cancelRequest: 지정 한 컨트롤 의 그림 로드 요청 을 취소 합 니 다.
shutdown: Picasso 를 닫 습 니 다.
RequestCreator RequestCreator 대상 은 Picasso 의 load 방법 에서 유래 되 었 습 니 다. 주로 그림 의 전시 작업 을 처리 합 니 다. 자주 사용 하 는 방법 은 다음 과 같 습 니 다.
placeholder: 그림 을 불 러 오기 전에 차지 하 는 그림 을 지정 합 니 다.
error: 그림 불 러 오 는 데 실패 한 자리 잡 은 그림 을 지정 합 니 다.
resize: 그림 크기 를 지정 합 니 다.
centerCrop: 그림 을 가운데 로 지정 할 때 재단 합 니 다.
center Inside: 내부 가운데 그림 을 지정 합 니 다.
rotate: 그림 의 회전 각 도 를 지정 합 니 다.
config: 그림 의 색상 모드 를 지정 합 니 다.
noFade: 페 이 드 페 이 드 애니메이션 을 표시 하지 않 을 것 을 지정 합 니 다.기본적으로 디 스 플레이 애니메이션 이 있 습 니 다.
into: 그림 에 표 시 된 컨트롤 을 지정 합 니 다.
캐 시 디 렉 터 리 picasso 를 설정 하면 네트워크 그림 을 불 러 올 수 있 을 뿐만 아니 라 자원 그림 도 불 러 올 수 있 습 니 다 (assets 와 drawable 포함).또한 picasso 의 그림 캐 시 디 렉 터 리 를 사용자 정의 하려 면 다음 과 같이 설정 할 수 있 습 니 다.
private void setImageCacheDir() {
String imageCacheDir = Environment.getExternalStorageDirectory() + "/picasso_image";
tv_hello.setText(imageCacheDir);
Picasso picasso = new Picasso.Builder(this).downloader(
new OkHttpDownloader(new File(imageCacheDir))).build();
Picasso.setSingletonInstance(picasso);
}
주의해 야 할 것 은 picasso 는 okhttp 에 의존 하고 okhttp 는 okio 에 의존 하기 때문에 picasso 의 모든 기능 (예 를 들 어 캐 시 디 렉 터 리 를 사용자 정의 할 때 OkHttpDownloader 를 사용) 을 사용 하려 면 picasso, okhttp, okio 세 개의 jar 패 키 지 를 동시에 가 져 와 야 합 니 다.
코드 예제 다음은 picasso 에서 자주 사용 하 는 장면 에서 의 코드 예 입 니 다.
//
Picasso.with(this).load(url).into(iv_hello);
//
Picasso.with(this).load(url).resize(512, 384).centerCrop().into(iv_hello);
//
Picasso.with(this).load(url).placeholder(R.drawable.bliss).error(R.drawable.error).into(iv_hello);
Universal - Image - Loader Universal - Image - Loader 는 광범 위 하 게 응용 되 는 이미지 로드 프레임 워 크 로 Picasso 보다 기능 이 풍부 하 며 물론 사용 하기에 도 복잡 할 수 있 습 니 다.
ImageLoader Universal 은 캐 시 그림 을 두 가지 과정 으로 나 누 었 습 니 다. Load 로드, Display 디 스 플레이 입 니 다.로드 정 보 는 ImageLoader Configuration 류 에서 처리 되 고 디 스 플레이 정 보 는 DisplayImageOptions 류 에서 처리 되 며 마지막 으로 ImageLoader 에서 통일 적 으로 설정 하고 표시 합 니 다.ImageLoader 의 일반적인 방법 은 다음 과 같 습 니 다.
getInstance: 정적 방법.ImageLoader 의 인 스 턴 스 를 가 져 옵 니 다.
init: 로 딩 정 보 를 초기 화 합 니 다.
display Image: 지정 한 컨트롤 ImageView 에 그림 을 표시 하고 표시 정 보 를 지정 합 니 다.
cancelDisplayTask: 지정 한 컨트롤 에 있 는 그림 표시 작업 을 취소 합 니 다.
loadImage: 지정 한 컨트롤 ImageView 에 그림 을 불 러 옵 니 다. 그림 을 불 러 오 는 모니터 를 설정 할 수 있 습 니 다.
ImageLoader Configuration 로 딩 정 보 를 설정 할 때 작성 자 모드 를 사용 합 니 다. 주로 스 레 드, 메모리, 디스크 의 관련 처 리 를 지정 합 니 다. 상세 한 방법 은 다음 과 같 습 니 다.
File imageCacheDir = new File(Environment.getExternalStorageDirectory() + "/universal_image");
ImageLoaderConfiguration config = new ImageLoaderConfiguration
.Builder(this)
.threadPoolSize(3) //
.threadPriority(Thread.NORM_PRIORITY - 2) //
.denyCacheImageMultipleSizesInMemory() //
// .taskExecutor(new Executor() {
// @Override
// public void execute(Runnable command) {
// }
// }) // ,
// .taskExecutorForCachedImages(new Executor() {
// @Override
// public void execute(Runnable command) {
// }
// }) // ,
.tasksProcessingOrder(QueueProcessingType.FIFO) // , FIFO。FIFO ,LIFO
.memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024)) //
.memoryCacheSize(2 * 1024 * 1024) //
.memoryCacheSizePercentage(13) //
.memoryCacheExtraOptions(480, 800) //
.diskCache(new UnlimitedDiskCache(imageCacheDir)) //
.diskCacheSize(50 * 1024 * 1024) //
.diskCacheFileCount(100) //
.diskCacheFileNameGenerator(new Md5FileNameGenerator())// , 。HashCodeFileNameGenerator ,Md5FileNameGenerator MD5
.diskCacheExtraOptions(480, 800, new BitmapProcessor() {
@Override
public Bitmap process(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
return BitmapFactory.decodeStream(bais, null, null);
}} ) // ,
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // , createSimple
.imageDownloader(new BaseImageDownloader(this, 5 * 1000, 30 * 1000)) // , 5 , 20 。 ,
.imageDecoder(new BaseImageDecoder(false)) // ,
.writeDebugLogs() // 。
.build(); //
DisplayImageOptions 디 스 플레이 정 보 는 주로 디 스 플레이 모드 와 차지 하 는 그림 을 지정 합 니 다. ImageLoader 의 displayImage 와 loadImage 방법, 그리고 ImageLoader Configuration 의 default DisplayImageOptions 방법 에 사용 할 수 있 습 니 다.상세 한 방법 사용 예 는 다음 과 같다.
DisplayImageOptions options = new DisplayImageOptions.Builder()
.cacheInMemory(true) // , false
.cacheOnDisk(true) // , false
.resetViewBeforeLoading(false) // , false
.displayer(new FadeInBitmapDisplayer(3000)) //
.imageScaleType(ImageScaleType.EXACTLY) //
.bitmapConfig(Bitmap.Config.ARGB_8888) //
.showImageOnLoading(R.drawable.bliss) //
.showImageForEmptyUri(R.drawable.error)// Uri
.showImageOnFail(R.drawable.error) // /
.build(); //
자원 그림 을 불 러 오 는 것 은 네트워크 그림 을 불 러 오 는 것 을 제외 하고 Universal 도 자원 류 그림 을 불 러 오 는 것 을 지원 합 니 다. ContentProvider, assets 와 drawable 세 가지 자원 그림 을 포함 합 니 다.구체 적 인 방법 은 다음 과 같다.
1. ContentProvider 그림 불 러 오기
String contentUrl = "content://media/external/audio/albumart/13";
2. assets 그림 불 러 오기String assetsUrl = Scheme.ASSETS.wrap("image.png");
3. drawable 그림 불 러 오기String drawableUrl = Scheme.DRAWABLE.wrap(""+R.drawable.image);
특히 drawable 의 로 딩 방식 에 주의 하 세 요. 인터넷 에서 많은 사람들 이 Scheme. DRAWABLE. wrap ("R. drawable. image") 을 돌 립 니 다. 그러나 이런 표기 법 은 문제 가 있 습 니 다. 실행 할 때 "java. lang. NumberFormat Exception: Invalid int:" R. drawable. image "를 잘못 보고 합 니 다.보아하니 공 부 는 보기 만 하면 안 되 는 것 같다. 부화뇌동 하 는 사람 이 가장 실 수 를 하기 쉬 운 지, 아니면 스스로 뛰 어 다 녀 봐 야 이렇게 하면 되 는 지 안 되 는 지 알 수 있다.
코드 예제 다음은 유 니 버 설 - Image - Loader 의 자주 사용 되 는 장면 에서 의 코드 예 입 니 다.
//
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).build();
mLoader.init(config);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.displayer(new FadeInBitmapDisplayer(3000)) //
.build();
mLoader.displayImage(url, iv_hello, options);
//
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).build();
mLoader.init(config);
ImageSize size = new ImageSize(512, 384);
mLoader.loadImage(url, size, new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
iv_hello.setImageBitmap(loadedImage);
}
});
이것 을 누 르 면 Android 개발 노트 의 전체 디 렉 터 리 를 볼 수 있 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.