Android 큰 그림 불 러 오기 최적화 - LRU 알고리즘 기반 로 컬 파일 캐 시
블 로그: Android 큰 그림 로 딩 메모리 최적화 (OutOf Memory 방지) 에 서 는 그림 을 불 러 올 때 원본 그림 을 완전히 불 러 오지 않 거나 예상 그림 의 크기 를 설명 합 니 다. 적당 한 사 이 즈 를 불 러 올 때 OOM 을 방지 합 니 다.다음은 그림 파일 의 로 컬 캐 시 를 설명 합 니 다. 네트워크 그림 은 로 컬 캐 시 를 거 쳐 야 자원 의 접근 속 도 를 높 일 수 있 습 니 다. 메모리 의 캐 시 는 SDCard 캐 시 에 맞 춰 야 장점 을 발휘 할 수 있 습 니 다.본 고 는 LRU 로 컬 캐 시 정책 을 사 용 했 습 니 다. 본 고 는 파일 의 캐 시 에 중심 을 두 었 기 때문에 메모리 의 캐 시 를 도입 하지 않 았 고 이전 블 로그 에서 내 려 온 이미지 로 딩 장점 도 발휘 하지 못 했 습 니 다. 그러나 후속 블 로그 에서 저 는 전체 프로젝트 를 계속 보완 하여 제3자 이미지 로 딩 라 이브 러 리 를 공개 하 겠 습 니 다.
LRU 알고리즘
그림 을 불 러 오 는 데 중요 한 절차 가 있 습 니 다. 네트워크 그림 의 로 컬 캐 시 입 니 다. 캐 시 된 그림 이 언제 삭 제 될 지 모 르 는 경우 가 많 습 니 다. 이 럴 때 합 리 적 인 로 컬 이미지 캐 시 정책 이 필요 합 니 다. 그림 파일 이 저장 공간 을 제한 없 이 차지 하지 않도록 저장 공간 이 부족 하고 자원 의 낭 비 를 초래 하지 않도록 해 야 합 니 다.컴퓨터 운영 체제 에서 작업 스케줄 링 에 LRU 알고리즘 을 도입 하 였 습 니 다. 。쉽게 말 하면 가장 오 랜 시간 동안 사용 되 지 않 은 자원 의 우선 순 위 를 최소 화하 고 사용 빈도 가 높 은 자원 을 우선 보장 하 는 것 이다.
그림 로 컬 캐 시 핵심 코드
설정 클래스 를 추가 합 니 다. 이 클래스 에 따라 그림 파일 의 캐 시 위치, 크기 등 매개 변 수 를 설정 할 수 있 습 니 다. 나중에 필요 할 수 있 는 설정 은 이 안에서 확장 해 야 합 니 다.
/**
* Created by CJstar on 15/8/24.
*/
public final class FileCacheOptions {
/**
* the file cache root path
*/
private String cacheRootPath;
/**
* file cache count
*/
private int maxFileCount;
/**
* file cache max size: byte
*/
private int maxCacheSize;
/**
* if it is false, will not cache files
*/
private boolean isUseFileCache = true;
public String getCacheRootPath() {
return cacheRootPath;
}
public void setCacheRootPath(String cacheRootPath) {
this.cacheRootPath = cacheRootPath;
}
public int getMaxFileCount() {
return maxFileCount;
}
public void setMaxFileCount(int maxFileCount) {
this.maxFileCount = maxFileCount;
}
/**
* cache size in bytes
* @return
*/
public int getMaxCacheSize() {
return maxCacheSize;
}
public void setMaxCacheSize(int maxCacheSize) {
this.maxCacheSize = maxCacheSize;
}
public boolean isUseFileCache() {
return isUseFileCache;
}
public void setIsUseFileCache(boolean isUseFileCache) {
this.isUseFileCache = isUseFileCache;
}
private FileCacheOptions(Builder builder){
setCacheRootPath(builder.getCacheRootPath());
setIsUseFileCache(builder.isUseFileCache());
setMaxCacheSize(builder.getMaxCacheSize());
setMaxFileCount(builder.getMaxFileCount());
}
/**
* This is the options set builder, we can create the options by this method
*/
public static class Builder{
private String cacheRootPath;
private int maxFileCount;
private int maxCacheSize;
private boolean isUseFileCache;
public Builder(){
}
public String getCacheRootPath() {
return cacheRootPath;
}
public Builder setCacheRootPath(String cacheRootPath) {
this.cacheRootPath = cacheRootPath;
return this;
}
public int getMaxFileCount() {
return maxFileCount;
}
public Builder setMaxFileCount(int maxFileCount) {
this.maxFileCount = maxFileCount;
return this;
}
public int getMaxCacheSize() {
return maxCacheSize;
}
public Builder setMaxCacheSize(int maxCacheSize) {
this.maxCacheSize = maxCacheSize;
return this;
}
public boolean isUseFileCache() {
return isUseFileCache;
}
public Builder setIsUseFileCache(boolean isUseFileCache) {
this.isUseFileCache = isUseFileCache;
return this;
}
public FileCacheOptions builder(){
return new FileCacheOptions(this);
}
}
}
다음은 핵심 처리 류:
/**
* Created by CJstar on 15/8/24.
*/
public class LRUFileCache implements FileCache {
/**
* cache config
*/
private FileCacheOptions options;
/**
* cache file suffix
*/
private static final String WHOLESALE_CONV = ".cach";
/**
* mini free space on SDCard
*/
private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10*1024*1024;
private static LRUFileCache mLRUFileCache;
public static LRUFileCache getInstance(){
if(mLRUFileCache==null){
synchronized (LRUFileCache.class){
if(mLRUFileCache==null){
mLRUFileCache = new LRUFileCache();
}
}
}
return mLRUFileCache;
}
public void setFileLoadOptions(FileCacheOptions options) {
this.options = options;
}
/**
* use default options
*/
private LRUFileCache() {
this.options = new FileCacheOptions.Builder()
.setCacheRootPath("FileCache")
.setIsUseFileCache(true)
.setMaxCacheSize(10 * 1024 * 1024)//10MB
.setMaxFileCount(100)
.builder();
}
@Override
public void addDiskFile(String key, InputStream inputStream) {
if (TextUtils.isEmpty(key) || inputStream == null) {
return;
}
String filename = convertUrlToFileName(key);
String dir = options.getCacheRootPath();
File dirFile = new File(dir);
if (!dirFile.exists())
dirFile.mkdirs();
File file = new File(dir + "/" + filename);
OutputStream outStream;
try {
if(file.exists()){
file.delete();
}
file.createNewFile();
outStream = new FileOutputStream(file);
while (inputStream.available()!=0){
outStream.write(inputStream.read());
}
outStream.flush();
outStream.close();
inputStream.close();
} catch (Throwable e) {
Log.w("LRUFileCache", e.getMessage());
}
// free the space at every time to add a new file
freeSpaceIfNeeded();
}
@Override
public File getDiskFile(String key) {
File file = new File(getFilePathByKey(key));
if(file!=null&&file.exists()){
updateFileTime(file);
}else{
file = null;
}
return file;
}
@Override
public boolean isExist(String key) {
if (URLUtil.isNetworkUrl(key)) {
return new File(options.getCacheRootPath() + "/" + convertUrlToFileName(key)).exists();
} else if (URLUtil.isFileUrl(key)) {
return new File(key).exists();
} else {
return false;
}
}
@Override
public void removeDiskFile(String key) {
File file = getDiskFile(key);
if (file != null &&file.exists()) {
file.delete();
}
}
@Override
public void removeAllDiskFiles() {
new File(options.getCacheRootPath()).delete();
}
/**
* This method will free the files which had not been used at a long time
*/
private void freeSpaceIfNeeded(){
File dir = new File(options.getCacheRootPath());
File[] files = dir.listFiles();
if(files==null){
return;
}
int dirSize = 0;
for (int i = 0; i < files.length; i++) {
if (files[i].getName().contains(WHOLESALE_CONV)) {
dirSize += files[i].length();
}
}
// if the dir size larger than max size or the free space on SDCard is less than 10MB
//free 40% space for system
if (dirSize > options.getMaxCacheSize()
|| FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
// delete 40% files by LRU
int removeFactor = (int) ((0.4 * files.length) + 1);
// sort the files by modify time
Arrays.sort(files, new FileLastModifSort());
// delete files
for (int i = 0; i < removeFactor; i++) {
if (files[i].getName().contains(WHOLESALE_CONV)) {
files[i].delete();
}
}
}
//if file count is larger than max count, delete the last
if(files.length>options.getMaxFileCount()){
Arrays.sort(files, new FileLastModifSort());
// delete files
for (int i = options.getMaxFileCount(); i < files.length; i++) {
if (files[i].getName().contains(WHOLESALE_CONV)) {
files[i].delete();
}
}
}
}
/**
* Modify the file time
*
* @param file the file which need to update time
*/
public void updateFileTime(File file) {
if(file!=null&&file.exists()){
long newModifiedTime = System.currentTimeMillis();
file.setLastModified(newModifiedTime);
}
}
/**
* get the free space on SDCard
*
* @return free size in MB
*/
private int freeSpaceOnSd() {
StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
.getPath());
double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
.getBlockSize());
return (int) sdFreeMB;
}
/**
* Get the file name by file url
*
* @param url
* @return file name
*/
private String convertUrlToFileName(String url) {
String[] strs = url.split("/");
return strs[strs.length - 1] + WHOLESALE_CONV;
}
public String getFilePathByKey(String key){
if(URLUtil.isFileUrl(key)){
return key;
}else if(URLUtil.isNetworkUrl(key)){
return options.getCacheRootPath()+"/"+convertUrlToFileName(key);
}else {
return null;
}
}
/**
* The comparator for the file modify, sort the files by modify time.
*/
private class FileLastModifSort implements Comparator {
public int compare(File arg0, File arg1) {
if (arg0.lastModified() > arg1.lastModified()) {
return 1;
} else if (arg0.lastModified() == arg1.lastModified()) {
return 0;
} else {
return -1;
}
}
}
}
전체 코드 주 소 는:https://github.com/CJstar/Android-ImageFileCache
다음은 Memory Cache 도 LRU 알고리즘 으로 이 루어 진 캐 시 이지 만 파일 캐 시 보다 복잡 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.