머리말 glide 는 구 글 이 추천 하 는 안 드 로 이 드 이미지 로 딩 프레임 워 크 로 우수한 캐 시 정책, Activity 의 라 이 프 사이클 계승, GIF 이미지 지원 등 이 잘 알려 진 곳 이다.다음은 glide 로 그림 을 불 러 오 는 호출 입 니 다.
그렇다면 이 프레임 워 크 는 어떻게 실제 작 동 되 는 것 일 까? 나 는 '글 라 이 드 여행' 시리즈 블 로 그 를 통 해 가능 한 한 내 마음 을 상세 하 게 기록 할 것 이다."Glide 의 여행" 시리즈 글 모음:
글 라 이 드 여행 - 레 지 스 트 리
글 라 이 드 여행 - DecodeJob
개술 EngineJob (com. bumptech. glide. load. engine. decodeJob) 은 전체 glide 가 그림 디 코딩, 캐 시 등 일련의 동작 을 시작 하 는 엔진 입 니 다. 소스 코드 분석 우선, DecodeJob 에서 변 수 를 초기 화 하 는 값 은 com. bumptech. glide. request. singleRequest 에서 유래 한 것 입 니 다.
위 에서 분석 할 수 있 듯 이 runReason 이 INITIALIZE 인 상황 에서 stage 는 이때 RESOURCE 이다.CACHE, currentGenerator 는 ResourceCacheGenerator 의 인 스 턴 스 로 runWrapped () 로 돌아 가 runGenerators () 를 호출 합 니 다.
이 함수 소스 코드 를 통 해 알 수 있 듯 이 완전 하 게 실 행 된 상황 에서 ResourceCacheGenerator, DataCacheGenerator 와 SourceGenerator 의 startNext () 를 순서대로 호출 합 니 다. 그 다음 에 세 개의 DataFetcherGenerator 의 실현 류 가 어떻게 진행 되 는 지 살 펴 보 겠 습 니 다.
package com.bumptech.glide.load.engine;
...
class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback
이전에 불 러 오지 않 았 던 그림 을 요청 하 는 것 이 분명 하기 때문에 ROM 캐 시 에서 해당 하 는 캐 시 파일 을 찾 을 수 없 기 때문에 최종 적 으로 는
if (sourceIdIndex >= sourceIds.size()) {
returnfalse;
}
false 로 돌아 갑 니 다.
package com.bumptech.glide.load.engine;
...
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback
여기 도 ROM 캐 시 파일 을 먼저 가 져 와 야 하기 때문에 리 소스 CacheGenerator 의 결과 와 같 습 니 다.
package com.bumptech.glide.load.engine;
...
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback
처음 실행 할 때 dataToCache 는 null 이 어야 합 니 다. sourceCacheGenerator 도 마찬가지 입 니 다. 이때 등 록 된 ModelLoader 인 스 턴 스 에 따라 만 든 LoadData 인 스 턴 스 의 DataFetcher 에 따라 원 격 그림 의 원본 데 이 터 를 불 러 옵 니 다. 등록 항목 과 결합 하여 HttpUrlFetcher 의 loadData 방법 을 실행 합 니 다.
package com.bumptech.glide.load.data;
...
publicclassHttpUrlFetcherimplementsDataFetcher<InputStream> {
...
@OverridepublicvoidloadData(Priority priority, DataCallback super InputStream> callback) {
long startTime = LogTime.getLogTime();
final InputStream result;
try {
result = loadDataWithRedirects(glideUrl.toURL(), 0/*redirects*/, null/*lastUrl*/,
glideUrl.getHeaders());
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
return;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
+ " ms and loaded " + result);
}
callback.onDataReady(result);
}
...
}
이 안에서 네트워크 를 통 해 그림 의 원본 데 이 터 를 요청 하 는 스 트림 InputStream (요청 이 잘 되면) 을 얻 은 다음 Source Generator 의 onData Ready 방법 을 되 돌려 줍 니 다.
package com.bumptech.glide.load.engine;
...
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback
이렇게 되면 dataToCache 를 통 해 그림 의 원본 데 이 터 를 임시 저장 한 다음 에 reschedule () 을 실행 하면 DecodeJob 으로 이동 합 니 다.
다음은 스 레 드 를 전환 하여 작업 을 하고 DecodeJob 의 입구 함수 에서 작업 을 계속 하 는 것 입 니 다. 다만, 이번 runReason 의 값 은 SWITCH 로 바 뀌 었 습 니 다.TO_SOURCE_SERVICE, runGenerators () 로 실 행 됩 니 다. 현재 currentGenerator 의 인 스 턴 스 는 SourceGenerator 입 니 다. 그러면 startNext () 로 실 행 될 때 현재 dataToCache 는 요청 한 그림 의 원본 데 이 터 를 저장 하 였 기 때문에 파일 캐 시 를 진행 합 니 다.
package com.bumptech.glide.load.engine;
...
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback
helper. getDiskCache (). put (originalKey, writer) 에서 캐 시 를 한 것 이 분명 합 니 다.
package com.bumptech.glide.load.engine.cache;
...
publicclassDiskLruCacheWrapperimplementsDiskCache {
...
@Overridepublicvoidput(Key key, Writer writer) {
writeLocker.acquire(key);
try {
String safeKey = safeKeyGenerator.getSafeKey(key);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
}
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
thrownew IllegalStateException("Had two simultaneous puts for: " + safeKey);
}
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to put to disk cache", e);
}
}
} finally {
writeLocker.release(key);
}
}
...
}
writer. write (file) 의 기록 파일 작업 을 다시 봅 니 다.
package com.bumptech.glide.load.model;
...
publicclassStreamEncoderimplementsEncoder<InputStream> {privatestaticfinal String TAG = "StreamEncoder";
privatefinal ArrayPool byteArrayPool;
publicStreamEncoder(ArrayPool byteArrayPool) {
this.byteArrayPool = byteArrayPool;
}
@Overridepublicbooleanencode(InputStream data, File file, Options options) {
byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
boolean success = false;
OutputStream os = null;
try {
os = new FileOutputStream(file);
int read;
while ((read = data.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
os.close();
success = true;
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to encode data onto the OutputStream", e);
}
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Do nothing.
}
}
byteArrayPool.put(buffer, byte[].class);
}
return success;
}
}
그림 의 원본 데이터 흐름 을 해당 캐 시 파일 에 기록 한 것 임 을 알 수 있 습 니 다. 특정 규격 (너비 와 색상 모드 지정) 의 그림 캐 시 파일 은 요?서 두 르 지 마 세 요. SourceGenerator 의 cacheData 방법 은 마지막 으로 sourceCacheGenerator 에 인 스 턴 스 를 만 들 었 습 니 다. 즉, DataCacheGenerator 의 start Next () 를 실행 해 야 합 니 다.
조건 이 충 족 될 때 까지 집합 대상 의 buildLoadData 방법 을 순서대로 실행 합 니 다.분명히 정상 적 인 상황 에서 첫 번 째 ByteBufferFileLoader 를 실행 하면 조건 을 만족 시 킬 수 있 습 니 다. 그러면 이 럴 때 실 행 된 소스 코드 를 보 세 요.
package com.bumptech.glide.load.model;
...
publicclassByteBufferFileLoaderimplementsModelLoader<File, ByteBuffer> {
...
@Overridepublic LoadDatabuildLoadData(File file, int width, int height, Options options) {
returnnew LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
}
...
privatestaticclassByteBufferFetcherimplementsDataFetcher<ByteBuffer> {
...
}
...
}
decodeFromFetcher (Data, DataSource) 의 내부 로 실 행 될 때 ByteBuffer 형식 에 따라 LoadPath 의 대상 을 전 삼 으로 되 돌려 야 합 니 다. 이 전 삼 에 대한 획득 은 제 전문 Glide 여행 인 Registry 에서 상세 하 게 정리 되 었 고 rewinder 는 ByteBufferRewinder 의 인 스 턴 스 입 니 다. 다음은 com. bumptech. glide. load. engine. LoadPath \ # load 를 실행 합 니 다.(DataRewinder, Options, int, int, DecodePath.DecodeCallback)
package com.bumptech.glide.load.engine;
...
publicclassLoadPath<Data, ResourceType, Transcode> {
...
public Resourceload(DataRewinder rewinder, Options options, int width,
int height, DecodePath.DecodeCallback decodeCallback) throws GlideException {
List exceptions = listPool.acquire();
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, exceptions);
} finally {
listPool.release(exceptions);
}
}
private ResourceloadWithExceptionList(DataRewinder rewinder, Options options,
int width, int height, DecodePath.DecodeCallback decodeCallback,
List exceptions) throws GlideException {
int size = decodePaths.size();
Resource result = null;
for (int i = 0; i < size; i++) {
DecodePath path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
thrownew GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
...
}
보 이 는 바 와 같이, 주로 com. bumptech. glide. load. engine. decodePath \ # decode (DataRewinder, int, int, Options, DecodeCallback) 를 실행 해 야 합 니 다.
package com.bumptech.glide.load.engine;
...
publicclassDecodePath<DataType, ResourceType, Transcode> {
...
public Resourcedecode(DataRewinder rewinder, int width, int height,
Options options, DecodeCallback callback) throws GlideException {
Resource decoded = decodeResource(rewinder, width, height, options);
Resource transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed);
}
private ResourcedecodeResource(DataRewinder rewinder, int width,
int height, Options options) throws GlideException {
List exceptions = listPool.acquire();
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
private ResourcedecodeResourceWithList(DataRewinder rewinder, int width,
int height, Options options, List exceptions) throws GlideException {
Resource result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
thrownew GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
...
}
decoderesource With List 방법 에서 decoders 는 제 가 앞에서 얻 은 것 입 니 다.