android DiskLruCache 분석, 하 드 디스크 캐 시

글 참고:http://blog.csdn.net/guolin_blog/article/details/28863651
작가 님 께 서 공유 해 주 셔 서 감사합니다.
    알다 시 피 LruCache 기술 은 다 중 그림 OOM 을 방지 하 는 핵심 적 인 해결 방향 이지 만 LruCache 는 메모리 에 있 는 그림 의 저장 과 방출 만 관 리 했 을 뿐 메모리 에서 그림 이 제거 되면 네트워크 에서 그림 을 다시 불 러 와 야 하 는데 시간 이 많이 걸 릴 것 이다.이에 구 글 은 또 하나의 하 드 디스크 캐 시 솔 루 션 을 제공 했다. DiskLruCache (구 글 공식 작성 은 아니 지만 공식 인증 을 받 았 다).
1, 캐 시 위치:
    DiskLruCache 는 데이터 의 캐 시 위 치 를 제한 하지 않 고 자 유 롭 게 설정 할 수 있 지만, 일반적으로 대부분의 프로그램 은 캐 시 위 치 를 / sdcard / android / data / < application package > / cache 라 는 경로 로 선택 합 니 다.이 위치 에서 선택 하 는 것 은 두 가지 장점 이 있다. 첫째, 이것 은 SD 카드 에 저 장 된 것 이기 때문에 아무리 많은 데 이 터 를 캐 시 하 더 라 도 휴대 전화의 내장 저장 공간 에 영향 을 주지 않 고 SD 카드 공간 만 충분 하면 된다.둘째, 이 경 로 는 안 드 로 이 드 시스템 에 의 해 프로그램의 캐 시 경로 로 인 정 됩 니 다. 프로그램 이 마 운 트 해제 되 었 을 때 이곳 의 데이터 도 함께 삭 제 됩 니 다. 그러면 프로그램 을 삭제 한 후에 핸드폰 에 남아 있 는 데이터 에 문제 가 발생 하지 않 습 니 다.
   경로 아래 bitmap 폴 더 아래 journal 이라는 파일 이 있 습 니 다. journal 파일 은 DiskLruCache 의 로그 파일 입 니 다. 프로그램 은 모든 그림 에 대한 작업 기록 을 이 파일 에 저장 합 니 다. journal 이라는 파일 을 보면 프로그램 이 DiskLruCache 기술 을 사용 한 다 는 것 을 상징 합 니 다.비트 맵 의 긴 파일 이름 이 캐 시 된 그림 입 니 다.
2, 코드 사용:
    (1) 먼저, DiskLRuCache 는 new 에서 인 스 턴 스 를 낼 수 없다 는 것 을 알 아야 합 니 다. 만약 에 우리 가 DiskLRuCache 의 인 스 턴 스 를 만 들 려 면 open () 방법 을 호출 해 야 합 니 다. 인 터 페 이 스 는 다음 과 같 습 니 다.
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

    첫 번 째 매개 변 수 는 데이터 의 캐 시 주 소 를 지정 합 니 다. 두 번 째 매개 변 수 는 현재 프로그램의 버 전 번 호 를 지정 합 니 다. 세 번 째 매개 변 수 는 같은 key 가 몇 개의 캐 시 파일 에 대응 할 수 있 는 지 지정 합 니 다. 기본적으로 1 을 전달 하고 네 번 째 매개 변 수 는 최대 몇 바이트 의 데 이 터 를 캐 시 할 수 있 는 지 지정 합 니 다.
    SD 가 마침 제거 되 는 것 을 방지 하기 위해 서 우 리 는 코드 를 써 서 캐 시 주 소 를 가 져 옵 니 다.
   

public File getDiskCacheDir(Context context, String uniqueName) {
	String cachePath;
	if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
			|| !Environment.isExternalStorageRemovable()) {
		cachePath = context.getExternalCacheDir().getPath();
	} else {
		cachePath = context.getCacheDir().getPath();
	}
	return new File(cachePath + File.separator + uniqueName);
}

    SD 카드 가 존재 하거나 SD 카드 가 제거 되 지 않 을 때 getExternalCacheDir () 방법 으로 캐 시 경 로 를 가 져 옵 니 다. 그렇지 않 으 면 getCacheDir () 방법 으로 캐 시 경 로 를 가 져 옵 니 다.전 자 는 / sdcard / android / data / < application package > / cache 경 로 를 얻 었 고 후 자 는 / data / data / < application package > / cache 경 로 를 얻 었 습 니 다.유 니 크 Name: 캐 시 폴 더 이름 입 니 다.
    응용 버 전 번호 가 져 오기:

public int getAppVersion(Context context) {
	try {
		PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
		return info.versionCode;
	} catch (NameNotFoundException e) {
		e.printStackTrace();
	}
	return 1;
}

    설명: 버 전 번호 가 바 뀔 때마다 캐 시 경로 에 저 장 된 모든 데 이 터 는 삭 제 됩 니 다. DiskLruCache 는 프로그램 이 버 전 업데이트 가 있 을 때 모든 데 이 터 를 인터넷 에서 다시 가 져 와 야 한다 고 생각 하기 때 문 입 니 다.
    so, open 방법:

DiskLruCache mDiskLruCache = null;
try {
	File cacheDir = getDiskCacheDir(context, "bitmap");
	if (!cacheDir.exists()) {
		cacheDir.mkdirs();
	}
	mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
} catch (IOException e) {
	e.printStackTrace();
}

    먼저 getDiskCacheDir () 방법 을 호출 하여 캐 시 주소 의 경 로 를 가 져 온 다음 이 경로 가 존재 하 는 지 판단 하고 존재 하지 않 으 면 만 듭 니 다.이 어 DiskLruCache 의 open () 방법 을 호출 하여 인 스 턴 스 를 만 들 고 네 개의 인 자 를 입력 하면 됩 니 다.
DiskLruCache 의 인 스 턴 스 가 있 으 면 우 리 는 캐 시 된 데 이 터 를 조작 할 수 있 습 니 다. 작업 유형 은 주로 기록, 접근, 제거 등 을 포함 합 니 다.
    (2) 네트워크 URL 그림 을 로 컬 캐 시 에 기록 합 니 다.
    쓰 는 동작 은 DiskLruCache. Editor 같은 종 류 를 통 해 이 루어 집 니 다.이와 유사 하 게 이 클래스 도 new 가 될 수 없습니다. DiskLruCache 의 edit () 방법 으로 인 스 턴 스 를 가 져 와 야 합 니 다.

public Editor edit(String key) throws IOException

    key 는 캐 시 파일 의 파일 이름 이 되 고 그림 의 URL 과 일일이 대응 해 야 합 니 다. 그림 URL 에 특수 문자 가 포함 되 어 있 을 수 있 기 때문에 이 문자 들 은 파일 이름 을 지 을 때 비합법적일 수 있 습 니 다.사실 가장 쉬 운 방법 은 그림 의 URL 을 MD5 인 코딩 하 는 것 입 니 다. 인 코딩 된 문자열 은 유일한 것 이 고 0 - F 와 같은 문자 만 포함 되 어 파일 의 이름 규칙 에 완전히 부합 합 니 다.
   

public String hashKeyForDisk(String key) {
	String cacheKey;
	try {
		final MessageDigest mDigest = MessageDigest.getInstance("MD5");
		mDigest.update(key.getBytes());
		cacheKey = bytesToHexString(mDigest.digest());
	} catch (NoSuchAlgorithmException e) {
		cacheKey = String.valueOf(key.hashCode());
	}
	return cacheKey;
}

private String bytesToHexString(byte[] bytes) {
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < bytes.length; i++) {
		String hex = Integer.toHexString(0xFF & bytes[i]);
		if (hex.length() == 1) {
			sb.append('0');
		}
		sb.append(hex);
	}
	return sb.toString();
}

String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
String key = hashKeyForDisk(imageUrl);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);

    DiskLruCache. Editor 의 인 스 턴 스 가 있 으 면 new Output Stream () 방법 으로 출력 흐름 을 만 든 다음 downloadUrlToStream () 에 전송 하면 캐 시 를 다운로드 하고 기록 하 는 기능 을 수행 할 수 있 습 니 다.new OutputStream () 방법 에 주의 하여 index 인 자 를 받 습 니 다. 앞에서 value Count 를 설정 할 때 1 을 지정 하기 때문에 여기 index 에서 0 을 전달 하면 됩 니 다.기록 작업 이 실 행 된 후에 우 리 는 commt () 방법 을 호출 하여 제출 해 야 기록 이 유효 하 며, abort () 방법 을 호출 하면 이번 기록 을 포기 하 는 것 을 표시 합 니 다.

new Thread(new Runnable() {
	@Override
	public void run() {
		try {
			String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
			String key = hashKeyForDisk(imageUrl);
			DiskLruCache.Editor editor = mDiskLruCache.edit(key);
			if (editor != null) {
				OutputStream outputStream = editor.newOutputStream(0);
				if (downloadUrlToStream(imageUrl, outputStream)) {
					editor.commit();
				} else {
					editor.abort();
				}
			}
//                  ,         
			mDiskLruCache.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}).start();
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
	HttpURLConnection urlConnection = null;
	BufferedOutputStream out = null;
	BufferedInputStream in = null;
	try {
		final URL url = new URL(urlString);
		urlConnection = (HttpURLConnection) url.openConnection();
		in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
		out = new BufferedOutputStream(outputStream, 8 * 1024);
		int b;
		while ((b = in.read()) != -1) {
			out.write(b);
		}
		return true;
	} catch (final IOException e) {
		e.printStackTrace();
	} finally {
		if (urlConnection != null) {
			urlConnection.disconnect();
		}
		try {
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}
		} catch (final IOException e) {
			e.printStackTrace();
		}
	}
	return false;
}

   
    (3) 캐 시 읽 기:
   

public synchronized Snapshot get(String key) throws IOException

    get () 방법 은 key 를 입력 하여 해당 하 는 캐 시 데 이 터 를 가 져 오 라 고 요구 합 니 다. 이 key 는 그림 URL 을 MD5 인 코딩 한 값 임 에 틀림없다.
    여기 서 얻 은 것 은 DiskLruCache. snapshot 대상 입 니 다. getInputStream () 방법 을 호출 하면 캐 시 파일 의 입력 흐름 을 얻 을 수 있 습 니 다. 마찬가지 로 getInputStream () 방법 도 index 인 자 를 전달 해 야 합 니 다. 여기 서 0 을 입력 하면 됩 니 다.캐 시 를 완전히 읽 고 그림 을 인터페이스 에 불 러 오 는 코드 는 다음 과 같 습 니 다.

try {
	String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
	String key = hashKeyForDisk(imageUrl);
	DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
	if (snapShot != null) {
		InputStream is = snapShot.getInputStream(0);
		Bitmap bitmap = BitmapFactory.decodeStream(is);
		mImage.setImageBitmap(bitmap);
	}
} catch (IOException e) {
	e.printStackTrace();
}

    (4) 캐 시 삭제:
    
public synchronized boolean remove(String key) throws IOException

     설명: 이 방법 은 우리 가 자주 그것 을 호출 해 서 는 안 된다.캐 시 된 데이터 가 너무 많아 서 SD 카드 의 공간 을 너무 많이 차지 하 는 문 제 를 전혀 걱정 할 필요 가 없 기 때문에 DiskLruCache 는 우리 가 open () 방법 을 호출 할 때 설정 한 캐 시 최대 값 에 따라 불필요 한 캐 시 를 자동 으로 삭제 합 니 다.키 에 대응 하 는 캐 시 내용 이 만 료 되 었 는 지 확인 해 야 합 니 다. 네트워크 에서 최신 데 이 터 를 가 져 올 때 만 reove () 방법 으로 캐 시 를 제거 해 야 합 니 다.
    (5) 기타 API:
1. size()
이 방법 은 현재 캐 시 경로 에서 모든 캐 시 데이터 의 총 바이트 수 를 되 돌려 줍 니 다. byte 단위 로 프로그램 에서 현재 캐 시 데이터 의 총 크기 를 인터페이스 에 표시 해 야 한다 면 이 방법 으로 계산 할 수 있 습 니 다.
2.flush()
이 방법 은 메모리 의 조작 기록 을 로그 파일 (즉 journal 파일) 에 동기 화 하 는 데 사 용 됩 니 다.이 방법 은 매우 중요 하 다. 왜냐하면 DiskLruCache 가 정상적으로 일 할 수 있 는 전 제 는 journal 파일 의 내용 에 의존 해 야 하기 때문이다.앞에서 캐 시 쓰기 동작 을 설명 할 때 저 는 이 방법 을 한 번 호출 한 적 이 있 습 니 다. 그러나 캐 시 를 쓸 때마다 flush () 방법 을 호출 하 는 것 이 아 닙 니 다. 자주 호출 하 는 것 은 좋 은 점 이 없고 journal 파일 을 동기 화 하 는 시간 만 추가 할 수 있 습 니 다.비교적 표준적 인 방법 은 Activity 의 onPause () 방법 에서 flush () 방법 을 한 번 호출 하면 된다.
3.close()
이 방법 은 DiskLruCache 를 닫 는 데 사 용 됩 니 다. open () 방법 과 대응 하 는 방법 입 니 다.닫 으 면 DiskLruCache 에서 캐 시 데 이 터 를 조작 하 는 방법 을 더 이상 호출 할 수 없습니다. 보통 Activity 의 onDestroy () 방법 에서 close () 방법 만 호출 해 야 합 니 다.
4.delete()
이 방법 은 모든 캐 시 데 이 터 를 삭제 하 는 데 사 용 됩 니 다. 예 를 들 어 캐 시 기능 을 수 동 으로 청소 하 는 것 은 사실 DiskLruCache 의 delete () 방법 만 호출 하면 가능 합 니 다.

좋은 웹페이지 즐겨찾기