zoukankan      html  css  js  c++  java
  • Glide图片加载过程(简)

    96 
    iceIC 
    2018.10.25 20:53* 字数 906 阅读 529评论 0

    调研版本为4.7.1
    为了更加简单的理解,会将函数代码简化,详细代码请自行照源码对比

    Glide用法

    Glide.with(...).load(...).into(...)
    我们从into方法开始探寻之旅


    开启加载线程

    RequestBuilder.class
    
    public Target into() {
        Request request = buildRequest(...);
        requestTracker.runRequest(request); 
    }
    

    requestTracker是RequestTracker对象

    RequestTracker.class
    
    public void runRequest(Request request) {
        request.begin();
    }
    

    request是SingleRequest对象

    SingleRequest.class
    
    public void begin() {
        onSizeReady(...);
    }
    
    public void onSizeReady(...) {
        engine.load(...)
    }
    

    engine是Engine类的对象

    Engine.class
    
    void load(...) {
        engineJob.start(...)
    }
    

    engineJob是EngineJob类的对象

    EngineJob.class
    
    public void start(DecodeJob job) {
        GlideExecutor executor = ... //创建线程池
        executor.execute(job); //job是一个runnable对象,此语句开启线程
    }
    

    获取资源过程

    上面我们知道了开启线程的过程,这个线程主要就是获取二进制流并且编码成资源文件,如Bitmap、Drawable等用来在Android中显示。

    DecodeJob.class
    
    //线程从run方法开始
    public void run() {
        runWrapped();
    }
    
    private void runWrapped() {
        runGenerators();
    }
    
    private void runGenerators() {
        currentGenerator = getNextGenerator();
        //依次调用generator的startNext方法
        //startNext返回值表示是否可以获得图片的二进制流
        //如果generator能获取图片数据,那么就不需要下级的generator去尝试获取图片数据
        while (!currentGenerator.startNext()) {
            ...
            currentGenerator = getNextGenerator();
        }
    }
    
    //第一次调用该方法返回ResourceCacheGenerator
    //第二次返回DataCacheGenerator
    //第三次返回SourceGenerator(本地没有缓存时使用,下载数据并放入缓存)
    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);
        }
    }
    

    本例是一个imageView加载网络图,ResourceCacheGenerator的startNext为false,这样会调用DataCacheGenerator的startNext方法。

    DataCacheGenerator.class
    
        //startNext方法会通过循环所有的modelLoader分别调用buildLoadData方法
        //如果所有modelLoader获取的loadData都为空那么就返回false
        //这就意味着Glide将调用下一个Generator的startNext方法
        //也就是SourceGenerator的startNext方法
        public boolean startNext() {
            Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
            File cacheFile = helper.getDiskCache().get(originalKey);
    
            boolean started = false;
            while (!started && hasNextModelLoader()) {
                //我们自定义的ModelLoader就可以在这派上用场了
                ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
                ModelLoader.LoadData loadData = modelLoader.buildLoadData(...);
                if (loadData != null) {
                    started = true;
                    //本例中ByteBufferFileLoader获取的loadData不为空
                    //本例中loadData.fetcher是ByteBufferFetcher的对象
                    loadData.fetcher.loadData(this);
                }
            }
            return started;
        }
    
    ByteBufferFetcher.class
    
    public void loadData(DataFetcher.DataCallback<? super ByteBuffer> callback) {
        ByteBuffer result;
        try {
            result = ByteBufferUtil.fromFile(file);
        } catch (IOException e) {
            callback.onLoadFailed(e);
            return;
        }
        callback.onDataReady(result);
    }
    

    这里的callback是DataCacheGenerator对象,该类实现了DataFetcher.DataCallback接口,所以此处回调到DataCacheGenerator类中的onDataReady方法

    DataCacheGenerator.class
    
    public void onDataReady(Object data) {
        cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
    }
    

    cb是DecodeJob类对象

    DecodeJob.class
    
    public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
                                   DataSource dataSource, Key attemptedKey) {
                //将二进制流解码成资源文件
                decodeFromRetrievedData();
        }
    }
    
    private void decodeFromRetrievedData() {
        Resource<R> resource = null;
            resource = decodeFromData(currentFetcher, currentData, currentDataSource);
        if (resource != null) {
            notifyEncodeAndRelease(resource, currentDataSource);
        }
    }
    

    notifyEncodeAndRelease方法里主要是使用Handler回到主线程调用各种回调以及释放资源。至此,加载过程就分析完毕啦。


    在上面我们已经完整的走过了一个加载流程,但是Glide缓存的文件是什么形式的呢?如果一个图片的url被Glide加载缓存过,我们是不是可以通过该url获取缓存文件呢?获取的缓存文件是不是还是.jpg、.png等图片形式的可以直接从手机提取到电脑呢?如果你有这些疑问,那就继续往下看吧~

    Glide缓存机制

    Glide默认缓存在 data/data/包名/cache/image_manager_disk_cache 文件夹下,并且缓存文件的文件名根本不能直观上和请求的图片URL所匹配:
    类似这种021db417987c5c446d2042037c78e9d9c127432d5a8cdd2b58cd2a702fc917b3.0,后缀还是 .0 。是不是有点头大,让我们去看看文件名的生成规则吧。

     
    缓存文件夹

    Glide使用DiskLruCacheWrapper类进行缓存文件操作,我们主要看put和get方法,一个用于取缓存,一个用于存缓存。

    DiskLruCacheWrapper.class
    
    public File get(Key key) {
        String safeKey = safeKeyGenerator.getSafeKey(key);
        File result = null;
        final DiskLruCache.Value value = getDiskCache().get(safeKey);
        result = value.getFile(0);
        return result;
    }
    
    public void put(Key key, DiskCache.Writer writer) {
        String safeKey = safeKeyGenerator.getSafeKey(key);
        DiskLruCache diskCache = getDiskCache();
        DiskLruCache.Editor editor = diskCache.edit(safeKey);
        File file = editor.getFile(0);
        if (writer.write(file)) {
            editor.commit();
        }
    }
    
    

    safeKeyGenerator是SafeKeyGenerator类的对象,其getSafeKey方法主要通过图片url获取缓存文件名,getSafeKey需要传入一个Key对象,Key对象就包括了要加载的url。

    SafeKeyGenerator.class
    
    public String getSafeKey(Key key) {
        return calculateHexStringDigest(key);
    }
    
    private String calculateHexStringDigest(Key key) {
        return Util.sha256BytesToHex(container.messageDigest.digest());
    }
    

    这里打个断点让大家看看Key里都有什么成员变量


     
    Key.class

    calculateHexStringDigest方法也很简单,就是对该URL使用SHA256加密算法加密

    为了让大家理解,这里贴两张图,第一张图是将图片地址字符串进行加密计算结果图,第二张图是手机缓存文件夹图片


     

     

    那么后缀 .0 是怎么生成的呢?

    我们可以看到在调用DiskLruCacheWrapper的put函数保存缓存文件时有一句代码
    DiskLruCache.Editor editor = diskCache.edit(safeKey);

    其中diskCache是DiskLruCache对象

    DiskLruCache.class
    
    private DiskLruCache.Editor edit(String key) {
        DiskLruCache.Entry entry = lruEntries.get(key);
        if (entry == null) {
            entry = new DiskLruCache.Entry(key);
            lruEntries.put(key, entry);
        }
    }
    

    注意了,这里的lruEntries是一个LinkedHashMap,里面存的就是图片url和DiskLruCache.Entry,其中DiskLruCache.Entry就存有缓存文件名!

    可以看到Glide会优先从该Map中取,如果没有找到则自己创建DiskLruCache.Entry然后再放入Map

    我们可以直接看DiskLruCache.Entry的构造函数

    private Entry(String key) {
        cleanFiles = new File[valueCount];
    
        StringBuilder fileBuilder = new StringBuilder(key).append('.');
        for (int i = 0; i < valueCount; i++) {
            fileBuilder.append(i);
            cleanFiles[i] = new File(directory, fileBuilder.toString());
        }
    }
    

    这里cleanFiles就存放着缓存文件,其中valueCount=1,至于valueCount为什么是1是因为调用DiskLruCache的静态方法open public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) 时传的valueCount参数为1

    那么既然valueCount为1,上面代码应该可以简化了吧?

    private Entry(String key) {
        cleanFiles = new File[1];
        StringBuilder fileBuilder = new StringBuilder(key).append('.');
        fileBuilder.append(0);
        cleanFiles[0] = new File(directory, fileBuilder.toString());
    }
    

    所以看了这么多,想到怎么用url获取本地图片了嘛?
    另外,试试把 xxx.0 换成 xxx.png ~

  • 相关阅读:
    Lucene in action 笔记 case study
    关于Restful Web Service的一些理解
    Lucene in action 笔记 analysis篇
    Lucene in action 笔记 index篇
    Lucene in action 笔记 term vector
    Lucene in action 笔记 search篇
    博客园开博记录
    数论(算法概述)
    DIV, IFRAME, Select, Span标签入门
    记一个较困难的SharePoint性能问题的分析和解决
  • 原文地址:https://www.cnblogs.com/yelanggu/p/10831507.html
Copyright © 2011-2022 走看看