Glide源碼解析之ResourceCacheGenerator

DataFetcherGenerator

數(shù)據(jù)提取器生成器,雖然名字是這樣叫,但是實際上在實現(xiàn)類你是看不到它生成數(shù)據(jù)提取器的。它只有兩個方法逝段,有三個類實現(xiàn)了它,分別為 ResourceCacheGenerator割捅、DataCacheGenerator奶躯、SourceGenerator。

interface DataFetcherGenerator {
    
    /**
     *  數(shù)據(jù)提取器執(zhí)行工作則返回true亿驾,否則返回false
     */
    boolean startNext();

    /**
     * 取消數(shù)據(jù)提取器的執(zhí)行
     */
    void cancel();
}

ResourceCacheGenerator

資源緩存生成器嘹黔,主要是從磁盤緩存中獲取經(jīng)過轉(zhuǎn)化過的資源。因為磁盤緩存不僅可以緩存原圖莫瞬,也可以緩存轉(zhuǎn)化過的圖片儡蔓,原圖的獲取則是由 DataCacheGenerator 實現(xiàn),由于 DataCacheGenerator 的獲取和這個類差不多就不再另寫一篇了疼邀。

首先會去磁盤緩存中獲取圖片喂江,如果有則獲取 File 類型的 ModelLoader ,并由它生成對應(yīng)的 LoadData 旁振, LoadData 又包含了 DataFetcher 获询,最終的數(shù)據(jù)提取操作就交給它。

該類使用的數(shù)據(jù)大多數(shù)由DecodeHelper提供拐袜,要是不熟悉的可以看下 Glide 源碼解析之 DecodeHelper

class ResourceCacheGenerator implements DataFetcherGenerator,
        DataFetcher.DataCallback<Object> {

    private final FetcherReadyCallback cb;
    private final DecodeHelper<?> helper;

    private int sourceIdIndex;
    private int resourceClassIndex = -1;
    private Key sourceKey;
    private List<ModelLoader<File, ?>> modelLoaders;    //默認(rèn)為 null
    
    @Override
    public boolean startNext() {
        List<Key> sourceIds = helper.getCacheKeys();    //至少會有一個 GlideUrl 的 key          
        if (sourceIds.isEmpty()) {
            return false;
        }
        
        List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();     
        if (resourceClasses.isEmpty()) {
            // helper.getTranscodeClass() 是 Drawable.class
            if (File.class.equals(helper.getTranscodeClass())) {
                return false;
            }
            throw new IllegalStateException(
                    "Failed to find any load path from " + helper.getModelClass() + " to "
                            + helper.getTranscodeClass());
        }
        
        //一開始 modelLoaders 是null的吉嚣,所以會進入循環(huán)
        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); // DrawableTransformation

            //根據(jù)當(dāng)前配置生成一個 key
            currentKey =
                    new ResourceCacheKey(
                            helper.getArrayPool(),
                            sourceId,
                            helper.getSignature(),
                            helper.getWidth(),
                            helper.getHeight(),
                            transformation,
                            resourceClass,
                            helper.getOptions());
            //獲取磁盤緩存文件
            cacheFile = helper.getDiskCache().get(currentKey);
            if (cacheFile != null) {
                sourceKey = sourceId;
                
                //獲取 File 類型的 ModelLoaders ,有 ByteBufferFileLoader蹬铺、FileLoader尝哆、UnitModelLoader
                modelLoaders = helper.getModelLoaders(cacheFile);  
                modelLoaderIndex = 0;
            }
        }

        loadData = null;
        boolean started = false;
        
        //上面對 modelLoaders 進行了賦值,所以會進入循環(huán)
        while (!started && hasNextModelLoader()) {
            ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
            
            //第一次循環(huán)由 ByteBufferFileLoader 生成的 LoadData 為 LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file))
            loadData = modelLoader.buildLoadData(cacheFile,
                    helper.getWidth(), helper.getHeight(), helper.getOptions());
            if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                started = true; //結(jié)束循環(huán)的標(biāo)志
                
                //所以這里調(diào)用的是 ByteBufferFetcher
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }

        return started;
    }
    
}

DataFetcher 的生成

首先對應(yīng) File 類型的 ModelLoader 有幾個甜攀,這里我們只看第一個 ByteBufferFileLoader 秋泄,它由 ByteBufferFileLoader 的內(nèi)部類 Factory 用工廠模式生成。接著又會調(diào)用它的 buildLoadData 方法生成參數(shù)為 ObjectKey 和 ByteBufferFetcher 的 LoadData 赴邻,所以上面最終調(diào)用的 DataFetcher 為 ByteBufferFetcher 印衔。

    // ModelLoader 是由 Factory 去 builde 出來的
    Glide(){
        registry
                .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
                .append(File.class, InputStream.class, new FileLoader.StreamFactory())
                .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
                .append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
    }

    // ByteBufferFileLoader.Factory()
    public static class Factory implements ModelLoaderFactory<File, ByteBuffer> {
    
        @NonNull
        @Override
        public ModelLoader<File, ByteBuffer> build(@NonNull MultiModelLoaderFactory multiFactory) {
          return new ByteBufferFileLoader();
        }
    
    }
  
    public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {
    
        @Override
        public LoadData<ByteBuffer> buildLoadData(@NonNull File file, int width, int height,
            @NonNull Options options) {
            return new LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
        }
        
    }

資源的提取

在 LoadData() 中調(diào)用了 ByteBufferUtil.fromFile(file) 啡捶,里面使用 NIO 的類 FileChannel 以只讀的形式進行內(nèi)存映射姥敛,這樣能加速讀取的速度。

最后就會把結(jié)果回調(diào)給 callback 了瞎暑,這個 callback 是由 ResourceCacheGenerator 來實現(xiàn)的彤敛,也就是加載完后會通知到 ResourceCacheGenerator 与帆。

    private static final class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
    
        private final File file;
    
        @Synthetic
        @SuppressWarnings("WeakerAccess")
        ByteBufferFetcher(File file) {
          this.file = file;
        }
    
        @Override
        public void loadData(@NonNull Priority priority,
            @NonNull DataCallback<? super ByteBuffer> callback) {
          ByteBuffer result;
          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);
        }
        
    }
    
    //ByteBufferUtil
    @NonNull
    public static ByteBuffer fromFile(@NonNull File file) throws IOException {
        RandomAccessFile raf = null;
        FileChannel channel = null;
        try {
            long fileLength = file.length();
            
            if (fileLength > Integer.MAX_VALUE) {
                throw new IOException("File too large to map into memory");
            }
            
            if (fileLength == 0) {
                throw new IOException("File unsuitable for memory mapping");
            }

            raf = new RandomAccessFile(file, "r");
            channel = raf.getChannel();
            return channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength).load();
        } finally {
            if (channel != null) {
                try {
                    channel.close();
                } catch (IOException e) {
                    // Ignored.
                }
            }
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    // Ignored.
                }
            }
        }
    }

資源加載完成

在 ResourceCacheGenerator 的回調(diào)中實際上調(diào)用的是 DecodeJob 實現(xiàn)的 FetcherReadyCallback 接口,這樣資源最終會交給 DecodeJob 來處理墨榄。

    @Override
    public void onDataReady(Object data) {
        cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE,
                currentKey);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玄糟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子袄秩,更是在濱河造成了極大的恐慌阵翎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件之剧,死亡現(xiàn)場離奇詭異郭卫,居然都是意外死亡,警方通過查閱死者的電腦和手機背稼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門贰军,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蟹肘,你說我怎么就攤上這事词疼。” “怎么了帘腹?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵贰盗,是天一觀的道長。 經(jīng)常有香客問我阳欲,道長童太,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任胸完,我火速辦了婚禮书释,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赊窥。我一直安慰自己爆惧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布锨能。 她就那樣靜靜地躺著扯再,像睡著了一般。 火紅的嫁衣襯著肌膚如雪址遇。 梳的紋絲不亂的頭發(fā)上熄阻,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機與錄音倔约,去河邊找鬼秃殉。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钾军。 我是一名探鬼主播鳄袍,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吏恭!你這毒婦竟也來了拗小?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤樱哼,失蹤者是張志新(化名)和其女友劉穎哀九,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搅幅,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡勾栗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盏筐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片围俘。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖琢融,靈堂內(nèi)的尸體忽然破棺而出界牡,到底是詐尸還是另有隱情,我是刑警寧澤漾抬,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布宿亡,位于F島的核電站,受9級特大地震影響纳令,放射性物質(zhì)發(fā)生泄漏挽荠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一平绩、第九天 我趴在偏房一處隱蔽的房頂上張望圈匆。 院中可真熱鬧,春花似錦捏雌、人聲如沸跃赚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纬傲。三九已至,卻和暖如春肤频,著一層夾襖步出監(jiān)牢的瞬間叹括,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工宵荒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留汁雷,地道東北人净嘀。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像摔竿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子少孝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354