글 라 이 드 여행 - DecodeJob

68213 단어 glide
머리말
glide 는 구 글 이 추천 하 는 안 드 로 이 드 이미지 로 딩 프레임 워 크 로 우수한 캐 시 정책, Activity 의 라 이 프 사이클 계승, GIF 이미지 지원 등 이 잘 알려 진 곳 이다.다음은 glide 로 그림 을 불 러 오 는 호출 입 니 다.
private void loadImage() {
    Glide.with(this)
         .load("http://pic2.orsoon.com/2017/0118/20170118011032176.jpg")
         .into(ivTest);
}

그렇다면 이 프레임 워 크 는 어떻게 실제 작 동 되 는 것 일 까? 나 는 '글 라 이 드 여행' 시리즈 블 로 그 를 통 해 가능 한 한 내 마음 을 상세 하 게 기록 할 것 이다."Glide 의 여행" 시리즈 글 모음:
  • 글 라 이 드 여행 - 레 지 스 트 리
  • 글 라 이 드 여행 - DecodeJob
  • 개술
    EngineJob (com. bumptech. glide. load. engine. decodeJob) 은 전체 glide 가 그림 디 코딩, 캐 시 등 일련의 동작 을 시작 하 는 엔진 입 니 다.
    소스 코드 분석
    우선, DecodeJob 에서 변 수 를 초기 화 하 는 값 은 com. bumptech. glide. request. singleRequest 에서 유래 한 것 입 니 다.
    package com.bumptech.glide.request;
    
    ...
    
    public final class SingleRequest<R> implements Request,
            SizeReadyCallback,
            ResourceCallback,
            FactoryPools.Poolable {
    
        ...
    
        @Override
        public void onSizeReady(int width, int height) {
    
            ...
    
            loadStatus = engine.load(
                    glideContext,
                    model,
                    requestOptions.getSignature(),
                    this.width,
                    this.height,
                    requestOptions.getResourceClass(),
                    transcodeClass,
                    priority,
                    requestOptions.getDiskCacheStrategy(),
                    requestOptions.getTransformations(),
                    requestOptions.isTransformationRequired(),
                    requestOptions.getOptions(),
                    requestOptions.isMemoryCacheable(),
                    requestOptions.getUseUnlimitedSourceGeneratorsPool(),
                    requestOptions.getOnlyRetrieveFromCache(),
                    this);
    
            ...
    
        }
    
        ...
    
    }

    먼저 디 코딩 작업 을 수행 하 는 입구 함 수 를 살 펴 보 겠 습 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        @Override
        public void run() {
            try {
                if (isCancelled) {
                    notifyFailed();
                    return;
                }
                runWrapped();
            } catch (RuntimeException e) {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "DecodeJob threw unexpectedly"
                            + ", isCancelled: " + isCancelled
                            + ", stage: " + stage, e);
                }
                if (stage != Stage.ENCODE) {
                    notifyFailed();
                }
                if (!isCancelled) {
                    throw e;
                }
            }
        }
    
        ...
    
    }

    즉, 일반적인 상황 에서 runWrapped () 로 바로 이동 합 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        private void runWrapped() {
            switch (runReason) {
                case INITIALIZE:
                    stage = getNextStage(Stage.INITIALIZE);
                    currentGenerator = getNextGenerator();
                    runGenerators();
                    break;
                case SWITCH_TO_SOURCE_SERVICE:
                    runGenerators();
                    break;
                case DECODE_DATA:
                    decodeFromRetrievedData();
                    break;
                default:
                    throw new IllegalStateException("Unrecognized run reason: " + runReason);
            }
        }
    
        ...
    
    }

    runReason 은 초기 상태 에서 INITIALIZE 로 설정 되 었 습 니 다. 이 럴 때 getNextGenerator (Stage. INITIALIZE) 와 getNextGenerator () 를 다시 보 겠 습 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        private DataFetcherGenerator getNextGenerator() {
            switch (stage) {
                case RESOURCE_CACHE:
                    return new ResourceCacheGenerator(decodeHelper, this);
                case DATA_CACHE:
                    return new DataCacheGenerator(decodeHelper, this);
                case SOURCE:
                    return new SourceGenerator(decodeHelper, this);
                case FINISHED:
                    return null;
                default:
                    throw new IllegalStateException("Unrecognized stage: " + stage);
            }
        }
    
        ...
    
        private Stage getNextStage(Stage current) {
            switch (current) {
                case INITIALIZE:
                    return diskCacheStrategy.decodeCachedResource()
                            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
                case RESOURCE_CACHE:
                    return diskCacheStrategy.decodeCachedData()
                            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
                case DATA_CACHE:
                    return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
                case SOURCE:
                case FINISHED:
                    return Stage.FINISHED;
                default:
                    throw new IllegalArgumentException("Unrecognized stage: " + current);
            }
        }
    
        ...
    
    }

    기본적으로 diskCache Strategy 의 값 은 DiskCache Strategy. AUTOMATIC 입 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    public abstract class DiskCacheStrategy {
    
        ...
    
        public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
            @Override
            public boolean isDataCacheable(DataSource dataSource) {
                return dataSource == DataSource.REMOTE;
            }
    
            @Override
            public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
                                            EncodeStrategy encodeStrategy) {
                return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
                        || dataSource == DataSource.LOCAL)
                        && encodeStrategy == EncodeStrategy.TRANSFORMED;
            }
    
            @Override
            public boolean decodeCachedResource() {
                return true;
            }
    
            @Override
            public boolean decodeCachedData() {
                return true;
            }
        };
    
        ...
    
    }

    위 에서 분석 할 수 있 듯 이 runReason 이 INITIALIZE 인 상황 에서 stage 는 이때 RESOURCE 이다.CACHE, currentGenerator 는 ResourceCacheGenerator 의 인 스 턴 스 로 runWrapped () 로 돌아 가 runGenerators () 를 호출 합 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        private void runGenerators() {
            currentThread = Thread.currentThread();
            startFetchTime = LogTime.getLogTime();
            boolean isStarted = false;
            while (!isCancelled && currentGenerator != null
                    && !(isStarted = currentGenerator.startNext())) {
                stage = getNextStage(stage);
                currentGenerator = getNextGenerator();
    
                if (stage == Stage.SOURCE) {
                    reschedule();
                    return;
                }
            }
            if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
                notifyFailed();
            }
        }
    
        ...
    
    }

    이 함수 소스 코드 를 통 해 알 수 있 듯 이 완전 하 게 실 행 된 상황 에서 ResourceCacheGenerator, DataCacheGenerator 와 SourceGenerator 의 startNext () 를 순서대로 호출 합 니 다. 그 다음 에 세 개의 DataFetcherGenerator 의 실현 류 가 어떻게 진행 되 는 지 살 펴 보 겠 습 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback {
    
        ...
    
        @Override
        public boolean startNext() {
            List sourceIds = helper.getCacheKeys();
            if (sourceIds.isEmpty()) {
                return false;
            }
            List> resourceClasses = helper.getRegisteredResourceClasses();
            while (modelLoaders == null || !hasNextModelLoader()) {
                resourceClassIndex++;
                if (resourceClassIndex >= resourceClasses.size()) {
                    sourceIdIndex++;
                    if (sourceIdIndex >= sourceIds.size()) {
                        return false;
                    }
                    resourceClassIndex = 0;
                }
    
                Key sourceId = sourceIds.get(sourceIdIndex);
                Class> resourceClass = resourceClasses.get(resourceClassIndex);
                Transformation> transformation = helper.getTransformation(resourceClass);
    
                currentKey = new ResourceCacheKey(sourceId, helper.getSignature(), helper.getWidth(),
                        helper.getHeight(), transformation, resourceClass, helper.getOptions());
                cacheFile = helper.getDiskCache().get(currentKey);
                if (cacheFile != null) {
                    this.sourceKey = sourceId;
                    modelLoaders = helper.getModelLoaders(cacheFile);
                    modelLoaderIndex = 0;
                }
            }
    
            ...
    
        }
    
        ...
    
    }

    이전에 불 러 오지 않 았 던 그림 을 요청 하 는 것 이 분명 하기 때문에 ROM 캐 시 에서 해당 하 는 캐 시 파일 을 찾 을 수 없 기 때문에 최종 적 으로 는
    if (sourceIdIndex >= sourceIds.size()) {
        return false;
    }

    false 로 돌아 갑 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback {
    
        ...
    
        @Override
        public boolean startNext() {
            while (modelLoaders == null || !hasNextModelLoader()) {
                sourceIdIndex++;
                if (sourceIdIndex >= cacheKeys.size()) {
                    return false;
                }
    
                Key sourceId = cacheKeys.get(sourceIdIndex);
                Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
                cacheFile = helper.getDiskCache().get(originalKey);
                if (cacheFile != null) {
                    this.sourceKey = sourceId;
                    modelLoaders = helper.getModelLoaders(cacheFile);
                    modelLoaderIndex = 0;
                }
            }
    
            ...
    
        }
    
        ...
    
    }

    여기 도 ROM 캐 시 파일 을 먼저 가 져 와 야 하기 때문에 리 소스 CacheGenerator 의 결과 와 같 습 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class SourceGenerator implements DataFetcherGenerator,
            DataFetcher.DataCallback,
            DataFetcherGenerator.FetcherReadyCallback {
    
        ...
    
        @Override
        public boolean startNext() {
            if (dataToCache != null) {
                Object data = dataToCache;
                dataToCache = null;
                cacheData(data);
            }
    
            if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
                return true;
            }
            sourceCacheGenerator = null;
    
            loadData = null;
            boolean started = false;
            while (!started && hasNextModelLoader()) {
                loadData = helper.getLoadData().get(loadDataListIndex++);
                if (loadData != null
                        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
                        || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
                    started = true;
                    loadData.fetcher.loadData(helper.getPriority(), this);
                }
            }
            return started;
        }
    
        ...
    
    }

    처음 실행 할 때 dataToCache 는 null 이 어야 합 니 다. sourceCacheGenerator 도 마찬가지 입 니 다. 이때 등 록 된 ModelLoader 인 스 턴 스 에 따라 만 든 LoadData 인 스 턴 스 의 DataFetcher 에 따라 원 격 그림 의 원본 데 이 터 를 불 러 옵 니 다. 등록 항목 과 결합 하여 HttpUrlFetcher 의 loadData 방법 을 실행 합 니 다.
    package com.bumptech.glide.load.data;
    
    ...
    
    public class HttpUrlFetcher implements DataFetcher<InputStream> {
    
        ...
    
        @Override
        public void loadData(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,
            DataFetcherGenerator.FetcherReadyCallback {
    
        ...
    
        @Override
        public void onDataReady(Object data) {
            DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
            if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
                dataToCache = data;
                cb.reschedule();
            } else {
                cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
                        loadData.fetcher.getDataSource(), originalKey);
            }
        }
    
        ...
    
    }

    이렇게 되면 dataToCache 를 통 해 그림 의 원본 데 이 터 를 임시 저장 한 다음 에 reschedule () 을 실행 하면 DecodeJob 으로 이동 합 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        @Override
        public void reschedule() {
            runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
            callback.reschedule(this);
        }
    
        ...
    
    }

    EngineJob 의 reschedule 방법 으로 계속 이동 합 니 다:
    package com.bumptech.glide.load.engine;
    
    ...
    
    class EngineJob implements DecodeJob.Callback, Poolable {
    
        ...
    
        @Override
        public void reschedule(DecodeJob> job) {
            if (isCancelled) {
                MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget();
            } else {
                getActiveSourceExecutor().execute(job);
            }
        }
    
        ...
    
    }

    다음은 스 레 드 를 전환 하여 작업 을 하고 DecodeJob 의 입구 함수 에서 작업 을 계속 하 는 것 입 니 다. 다만, 이번 runReason 의 값 은 SWITCH 로 바 뀌 었 습 니 다.TO_SOURCE_SERVICE, runGenerators () 로 실 행 됩 니 다. 현재 currentGenerator 의 인 스 턴 스 는 SourceGenerator 입 니 다. 그러면 startNext () 로 실 행 될 때 현재 dataToCache 는 요청 한 그림 의 원본 데 이 터 를 저장 하 였 기 때문에 파일 캐 시 를 진행 합 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class SourceGenerator implements DataFetcherGenerator,
            DataFetcher.DataCallback,
            DataFetcherGenerator.FetcherReadyCallback {
    
        ...
    
        private void cacheData(Object dataToCache) {
            long startTime = LogTime.getLogTime();
            try {
                Encoder encoder = helper.getSourceEncoder(dataToCache);
                DataCacheWriter writer =
                        new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
                originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
                helper.getDiskCache().put(originalKey, writer);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Finished encoding source to cache"
                            + ", key: " + originalKey
                            + ", data: " + dataToCache
                            + ", encoder: " + encoder
                            + ", duration: " + LogTime.getElapsedMillis(startTime));
                }
            } finally {
                loadData.fetcher.cleanup();
            }
    
            sourceCacheGenerator =
                    new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
        }
    
        ...
    
    }

    helper. getDiskCache (). put (originalKey, writer) 에서 캐 시 를 한 것 이 분명 합 니 다.
    package com.bumptech.glide.load.engine.cache;
    
    ...
    
    public class DiskLruCacheWrapper implements DiskCache {
    
        ...
    
        @Override
        public void put(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) {
                        throw new 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;
    
    ...
    
    public class StreamEncoder implements Encoder<InputStream> {
        private static final String TAG = "StreamEncoder";
        private final ArrayPool byteArrayPool;
    
        public StreamEncoder(ArrayPool byteArrayPool) {
            this.byteArrayPool = byteArrayPool;
        }
    
        @Override
        public boolean encode(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 () 를 실행 해 야 합 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback {
    
        ...
    
        @Override
        public boolean startNext() {
            while (modelLoaders == null || !hasNextModelLoader()) {
                sourceIdIndex++;
                if (sourceIdIndex >= cacheKeys.size()) {
                    return false;
                }
    
                Key sourceId = cacheKeys.get(sourceIdIndex);
                Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
                cacheFile = helper.getDiskCache().get(originalKey);
                if (cacheFile != null) {
                    this.sourceKey = sourceId;
                    modelLoaders = helper.getModelLoaders(cacheFile);
                    modelLoaderIndex = 0;
                }
            }
    
            loadData = null;
            boolean started = false;
            while (!started && hasNextModelLoader()) {
                ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
                loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(),
                        helper.getHeight(), helper.getOptions());
                if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                    started = true;
                    loadData.fetcher.loadData(helper.getPriority(), this);
                }
            }
            return started;
        }
    
        ...
    
    }

    이 제 는 그림 의 캐 시 파일 을 찾 을 수 있 을 것 입 니 다. 캐 시 파일 이 비어 있 지 않 을 때 다음 과 같은 말 이 있 습 니 다.
    modelLoaders = helper.getModelLoaders(cacheFile);

    그러면 제 앞의 글 라 이 드 여행 인 레 지 스 트 리 에서 얻 는 방법 에 따라 다음 과 같은 대상 을 포함 한 집합 으로 돌아 갈 것 입 니 다.
    1. com.bumptech.glide.load.model.ByteBufferFileLoader.Factory#build(MultiModelLoaderFactory)
    2. com.bumptech.glide.load.model.FileLoader.StreamFactory#build(MultiModelLoaderFactory)
    3. com.bumptech.glide.load.model.FileLoader.FileDescriptorFactory#build(MultiModelLoaderFactory)
    4. com.bumptech.glide.load.model.UnitModelLoader.Factory#build(MultiModelLoaderFactory)

    조건 이 충 족 될 때 까지 집합 대상 의 buildLoadData 방법 을 순서대로 실행 합 니 다.분명히 정상 적 인 상황 에서 첫 번 째 ByteBufferFileLoader 를 실행 하면 조건 을 만족 시 킬 수 있 습 니 다. 그러면 이 럴 때 실 행 된 소스 코드 를 보 세 요.
    package com.bumptech.glide.load.model;
    
    ...
    
    public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {
    
        ...
    
        @Override
        public LoadData buildLoadData(File file, int width, int height, Options options) {
            return new LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
        }
    
        ...
    
        private static class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
    
            ...
    
        }
    
        ...
    
    }

    로드 데이터 의 원본 코드 를 다시 보 겠 습 니 다.
    package com.bumptech.glide.load.model;
    
    ...
    
    public interface ModelLoader<Model, Data> {
        class LoadData {
            public final Key sourceKey;
            public final List alternateKeys;
            public final DataFetcher fetcher;
    
            public LoadData(Key sourceKey, DataFetcher fetcher) {
                this(sourceKey, Collections.emptyList(), fetcher);
            }
    
            public LoadData(Key sourceKey, List alternateKeys, DataFetcher fetcher) {
                this.sourceKey = Preconditions.checkNotNull(sourceKey);
                this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
                this.fetcher = Preconditions.checkNotNull(fetcher);
            }
        }
    
        ...
    
    }

    그러면 아래 의 이 집행 절 차 는 소통 된다.
    loadData.fetcher.loadData(helper.getPriority(), this);

    ByteBufferFetcher \ # loadData (Priority, DataCallback super ByteBuffer >) 로 직접 실행
    package com.bumptech.glide.load.model;
    
    ...
    
    public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {
    
        ...
    
        private static class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
    
            ...
    
            @Override
            public void loadData(Priority priority, DataCallback super ByteBuffer> callback) {
                ByteBuffer result = null;
                try {
                    result = ByteBufferUtil.fromFile(file);
                } catch (IOException e) {
                    if (Log.isLoggable(TAG, Log.DEBUG)) {
                        Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
                    }
                    callback.onLoadFailed(e);
                    return;
                }
    
                callback.onDataReady(result);
            }
    
            ...
    
        }
    
        ...
    
    }

    잘 실행 되면 DataCacheGenerator \ # onDataReady (Object) 를 직접 되 돌려 줍 니 다. 이 전송 참 조 는 ByteBuffer 형식의 매개 변수 임 을 알 수 있 습 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback {
    
        ...
    
        DataCacheGenerator(List cacheKeys, DecodeHelper> helper, FetcherReadyCallback cb) {
            this.cacheKeys = cacheKeys;
            this.helper = helper;
            this.cb = cb;
        }
    
        ...
    
        @Override
        public void onDataReady(Object data) {
            cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
        }
    
        @Override
        public void onLoadFailed(Exception e) {
            cb.onDataFetcherFailed(sourceKey, e, loadData.fetcher, DataSource.DATA_DISK_CACHE);
        }
    }

    이로써 DataFetcherGenerator 의 프로 세 스 가 정리 되 고 잘 실행 되면 DecodeJob \ # onDataFetcherReady (Key, Object, DataFetcher >, DataSource, Key) 를 되 돌리 기 시작 합 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        @Override
        public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher> fetcher,
                                       DataSource dataSource, Key attemptedKey) {
            this.currentSourceKey = sourceKey;
            this.currentData = data;
            this.currentFetcher = fetcher;
            this.currentDataSource = dataSource;
            this.currentAttemptingKey = attemptedKey;
            if (Thread.currentThread() != currentThread) {
                runReason = RunReason.DECODE_DATA;
                callback.reschedule(this);
            } else {
                decodeFromRetrievedData();
            }
        }
    
        ...
    
        private void decodeFromRetrievedData() {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Retrieved data", startFetchTime,
                        "data: " + currentData
                                + ", cache key: " + currentSourceKey
                                + ", fetcher: " + currentFetcher);
            }
            Resource resource = null;
            try {
                resource = decodeFromData(currentFetcher, currentData, currentDataSource);
            } catch (GlideException e) {
                e.setLoggingDetails(currentAttemptingKey, currentDataSource);
                exceptions.add(e);
            }
            if (resource != null) {
                notifyEncodeAndRelease(resource, currentDataSource);
            } else {
                runGenerators();
            }
        }
    
        private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
            if (resource instanceof Initializable) {
                ((Initializable) resource).initialize();
            }
    
            Resource result = resource;
            LockedResource lockedResource = null;
            if (deferredEncodeManager.hasResourceToEncode()) {
                lockedResource = LockedResource.obtain(resource);
                result = lockedResource;
            }
    
            notifyComplete(result, dataSource);
    
            stage = Stage.ENCODE;
            try {
                if (deferredEncodeManager.hasResourceToEncode()) {
                    deferredEncodeManager.encode(diskCacheProvider, options);
                }
            } finally {
                if (lockedResource != null) {
                    lockedResource.unlock();
                }
                onEncodeComplete();
            }
        }
    
        private  Resource decodeFromData(DataFetcher> fetcher, Data data,
                                                  DataSource dataSource) throws GlideException {
            try {
                if (data == null) {
                    return null;
                }
                long startTime = LogTime.getLogTime();
                Resource result = decodeFromFetcher(data, dataSource);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Decoded result " + result, startTime);
                }
                return result;
            } finally {
                fetcher.cleanup();
            }
        }
    
        private  Resource decodeFromFetcher(Data data, DataSource dataSource)
                throws GlideException {
            LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
            return runLoadPath(data, dataSource, path);
        }
    
        private  Resource runLoadPath(Data data, DataSource dataSource,
                                                             LoadPath path) throws GlideException {
            DataRewinder rewinder = glideContext.getRegistry().getRewinder(data);
            try {
                return path.load(rewinder, options, width, height,
                        new DecodeCallback(dataSource));
            } finally {
                rewinder.cleanup();
            }
        }
    
        ...
    
    }

    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;
    
    ...
    
    public class LoadPath<Data, ResourceType, Transcode> {
    
        ...
    
        public Resource load(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 Resource loadWithExceptionList(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) {
                throw new 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;
    
    ...
    
    public class DecodePath<DataType, ResourceType, Transcode> {
    
        ...
    
        public Resource decode(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 Resource decodeResource(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 Resource decodeResourceWithList(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) {
                throw new GlideException(failureMessage, new ArrayList<>(exceptions));
            }
            return result;
        }
    
        ...
    
    }

    decoderesource With List 방법 에서 decoders 는 제 가 앞에서 얻 은 것 입 니 다.
    1. new DecodePath(ByteBuffer, GifDrawable, Drawable, [ByteBufferGifDecoder], UnitTranscoder, exceptionListPool)
    2. new DecodePath(ByteBuffer, Bitmap, BitmapDrawable, [ByteBufferBitmapDecoder], BitmapDrawableTranscoder, exceptionListPool)
    3. new DecodePath(ByteBuffer, BitmapDrawable, Drawable, [BitmapDrawableDecoder], UnitTranscoder, exceptionListPool)

    그 중 하나의 DecodePath 인 스 턴 스 의 네 번 째 는 중 괄호 로 묶 은 매개 변수 로 집합 을 표시 합 니 다. 즉, 위의 세 개의 DecodePath 인 스 턴 스 의 decoders 에서 실행 가능 한 대상 을 찾 을 때 까지 입 니 다.
    package com.bumptech.glide.load.resource.gif;
    
    ...
    
    public class ByteBufferGifDecoder implements ResourceDecoder<ByteBuffer, GifDrawable> {
    
        ...
    
        @Override
        public boolean handles(ByteBuffer source, Options options) throws IOException {
            return !options.get(DISABLE_ANIMATION)
                    && ImageHeaderParserUtils.getType(parsers, source) == ImageHeaderParser.ImageType.GIF;
        }
    
        ...
    
    }

    원 격 그림 이 GIF 형식 이 아니라면 이 대상 은 계속 실행 할 수 없 을 것 입 니 다.
    package com.bumptech.glide.load.resource.bitmap;
    
    ...
    
    public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
        private final Downsampler downsampler;
    
        public ByteBufferBitmapDecoder(Downsampler downsampler) {
            this.downsampler = downsampler;
        }
    
        @Override
        public boolean handles(ByteBuffer source, Options options) throws IOException {
            return downsampler.handles(source);
        }
    
        @Override
        public Resource decode(ByteBuffer source, int width, int height, Options options)
                throws IOException {
            InputStream is = ByteBufferUtil.toStream(source);
            return downsampler.decode(is, width, height, options);
        }
    }

    Dwonsaampler 의 소스 코드 를 추가 합 니 다.
    package com.bumptech.glide.load.resource.bitmap;
    
    ...
    
    public final class Downsampler {
    
        ...
    
        public boolean handles(ByteBuffer byteBuffer) {
            // We expect downsampler to handle any available type Android supports.
            return true;
        }
    
        public Resource decode(InputStream is, int outWidth, int outHeight,
                                       Options options) throws IOException {
            return decode(is, outWidth, outHeight, options, EMPTY_CALLBACKS);
        }
    
        public Resource decode(InputStream is, int requestedWidth, int requestedHeight,
                                       Options options, DecodeCallbacks callbacks) throws IOException {
            Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"
                    + " mark()");
    
            byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
            BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
            bitmapFactoryOptions.inTempStorage = bytesForOptions;
    
            DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
            DownsampleStrategy downsampleStrategy = options.get(DOWNSAMPLE_STRATEGY);
            boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
    
            try {
                Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
                        downsampleStrategy, decodeFormat, requestedWidth, requestedHeight,
                        fixBitmapToRequestedDimensions, callbacks);
                return BitmapResource.obtain(result, bitmapPool);
            } finally {
                releaseOptions(bitmapFactoryOptions);
                byteArrayPool.put(bytesForOptions, byte[].class);
            }
        }
    
        ...
    
    }

    분명히, 여 기 는 우리 의 특정한 규격 의 그림 을 되 돌려 주 었 습 니 다! 리 소스 형식 으로 되 돌아 가면 BitmapDrawable Decoder 를 사용 하지 않 습 니 다. 다음은 DecodePath 를 돌아 보 겠 습 니 다.
    package com.bumptech.glide.load.engine;
    
    ...
    
    public class DecodePath<DataType, ResourceType, Transcode> {
    
        ...
    
        public Resource decode(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);
        }
    
        ...
    
    }

    com. bumptech. glide. load. engine. DecodeJob. DecodeCallback \ # onResource Decoded (Resource)
    package com.bumptech.glide.load.engine;
    
    ...
    
    class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
            Runnable, Comparable>, Poolable {
    
        ...
    
        private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {
            private final DataSource dataSource;
    
            @Synthetic
            DecodeCallback(DataSource dataSource) {
                this.dataSource = dataSource;
            }
    
            @Override
            public Resource onResourceDecoded(Resource decoded) {
                Class resourceSubClass = getResourceClass(decoded);
                Transformation appliedTransformation = null;
                Resource transformed = decoded;
                if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
                    appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
                    transformed = appliedTransformation.transform(decoded, width, height);
                }
                // TODO: Make this the responsibility of the Transformation.
                if (!decoded.equals(transformed)) {
                    decoded.recycle();
                }
    
                final EncodeStrategy encodeStrategy;
                final ResourceEncoder encoder;
                if (decodeHelper.isResourceEncoderAvailable(transformed)) {
                    encoder = decodeHelper.getResultEncoder(transformed);
                    encodeStrategy = encoder.getEncodeStrategy(options);
                } else {
                    encoder = null;
                    encodeStrategy = EncodeStrategy.NONE;
                }
    
                Resource result = transformed;
                boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
                if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
                        encodeStrategy)) {
                    if (encoder == null) {
                        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
                    }
                    final Key key;
                    if (encodeStrategy == EncodeStrategy.SOURCE) {
                        key = new DataCacheKey(currentSourceKey, signature);
                    } else if (encodeStrategy == EncodeStrategy.TRANSFORMED) {
                        key = new ResourceCacheKey(currentSourceKey, signature, width, height,
                                appliedTransformation, resourceSubClass, options);
                    } else {
                        throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
                    }
    
                    LockedResource lockedResult = LockedResource.obtain(transformed);
                    deferredEncodeManager.init(key, encoder, lockedResult);
                    result = lockedResult;
                }
                return result;
            }
    
            @SuppressWarnings("unchecked")
            private Class getResourceClass(Resource resource) {
                return (Class) resource.get().getClass();
            }
        }
    
        ...
    
    }

    위의 빗질 결 과 를 보면 위의 범 형 Z 는 비트 맵 유형 임 을 알 수 있다.
    see you again

    좋은 웹페이지 즐겨찾기