Glide源码解析之SourceGenerator

SourceGenerator

在上文 Glide 源码解析之 ResourceCacheGenerator 我们分析了从磁盘获取资源,而 SourceGenerator 的任务则是从来源地获取资源,比如说传入的是 url ,则是从网络获取,这里分析的是从网络获取的情况。

在 startNext() 中,首先会判断 dataToCache 是否为 null ,这个是获取到资源后才赋值的,所以一开始是 null 。接着判断 sourceCacheGenerator 是否为 null ,这个是当 cacheData() 的时候才赋值的,所以也是 null 。

然后会进入循环中,这里的 DataFetcher 实际为 HttpUrlFetcher ,资源的提取过程就是由它执行。 HttpUrlFetcher 的具体的获取过程可以看下 Glide 源码解析之 DecodeHelper

class SourceGenerator implements DataFetcherGenerator,
        DataFetcher.DataCallback,
        DataFetcherGenerator.FetcherReadyCallback {

    private final DecodeHelper helper;
    private final FetcherReadyCallback cb;

    private int loadDataListIndex;
    private DataCacheGenerator sourceCacheGenerator;
    private Object dataToCache;
    
    @Override
    public boolean startNext() {
        //第一次执行的时候是等于 null 的
        if (dataToCache != null) {
            Object data = dataToCache;
            dataToCache = null;
            cacheData(data);
        }

        //第一次执行的时候是等于 null 的
        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;
    }
    
}
 
 

资源的提取

使用HttpURLConnection来进行网络连接,获取输入流。

最后就会把结果回调给 callback 了,这个 callback 是由 SourceGenerator 来实现的,也就是加载完后会通知到 SourceGenerator 。

    //HttpUrlFetcher
    @Override
    public void loadData(@NonNull Priority priority,
                         @NonNull DataCallback callback) {
        try {
            InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
            callback.onDataReady(result);
        } catch (IOException e) {
            callback.onLoadFailed(e);
        } 
    }
    
    private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
                                              Map headers) throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
            //重定向次数限制
            throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
            try {
                if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                    throw new HttpException("In re-direct loop");
                }
            } catch (URISyntaxException e) {
                // Do nothing, this is best effort.
            }
        }

        urlConnection = connectionFactory.build(url);   // (HttpURLConnection) url.openConnection();
        
        //添加Header
        for (Map.Entry headerEntry : headers.entrySet()) {
            urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        urlConnection.setConnectTimeout(timeout);
        urlConnection.setReadTimeout(timeout);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);

        // 关闭网络连接的重定向,如果需要重定向会递归调用 loadDataWithRedirects() 自己处理
        urlConnection.setInstanceFollowRedirects(false);

        urlConnection.connect();
        stream = urlConnection.getInputStream();
        if (isCancelled) {
            return null;
        }
        
        final int statusCode = urlConnection.getResponseCode();
        if (isHttpOk(statusCode)) {
            //返回状态码正常,则返回结果
            return getStreamForSuccessfulRequest(urlConnection);
        } else if (isHttpRedirect(statusCode)) {
            //需要重定向
            String redirectUrlString = urlConnection.getHeaderField("Location");
            if (TextUtils.isEmpty(redirectUrlString)) {
                throw new HttpException("Received empty or null redirect url");
            }
            URL redirectUrl = new URL(url, redirectUrlString);
            // 关闭输入流和连接,重定向时会重新连接
            cleanup();
            //开始重定向
            return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else if (statusCode == INVALID_STATUS_CODE) {
            throw new HttpException(statusCode);
        } else {
            throw new HttpException(urlConnection.getResponseMessage(), statusCode);
        }
    }

    //获取网络连接返回的输入流
    private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
            throws IOException {
        if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
            int contentLength = urlConnection.getContentLength();
            stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);   //new ContentLengthInputStream(other, contentLength);
        } else {
            stream = urlConnection.getInputStream();
        }
        return stream;
    }
    @Override
    public void onDataReady(Object data) {
        //默认的 DiskCacheStrategy 为 DiskCacheStrategy.AUTOMATIC ,在 BaseRequestOptions中定义
        //所以默认是会缓存远程资源的
        DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
        if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
            //把资源赋值给dataToCache
            dataToCache = data;   
            
            // 可能是在其他线程回调的,在执行接下来的任务时先切回到Glide的线程
            cb.reschedule();
        } else {
            cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
                    loadData.fetcher.getDataSource(), originalKey);
        }
    }

资源获取完成

资源获取完成后会回调到 SourceGenerator 的 onDataReady(),如果是可以进行磁盘缓存的话(默认可以)则会回调给 DecodeJob 的 reschedule(),接着交给 EngineJob 使用线程池来重新执行 DecodeJob 。最后又会执行到 SourceGenerator 的startNext()方法。

    @Override
    public void onDataReady(Object data) {
        DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
        if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
            dataToCache = data;     //赋值给dataToCache
            //可能会在其他线程回回调,在执行其他东西之前先回调到 Glide 的线程
            cb.reschedule();
        } else {
            cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
                    loadData.fetcher.getDataSource(), originalKey);
        }
    }
    
    //DecodeJob
    @Override
    public void reschedule() {
        runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
        callback.reschedule(this);
    }
    
    //EngineJob
    @Override
    public void reschedule(DecodeJob job) {
        getActiveSourceExecutor().execute(job);
    }
    
    //DecodeJob
    @Override
    public void run() {
        runWrapped();
    }
    
    private void runWrapped() {
        switch (runReason) {
            case SWITCH_TO_SOURCE_SERVICE:
                runGenerators();
                break;
        }
    }
    
    private void runGenerators() {
        boolean isStarted = false;
        while (!isCancelled && currentGenerator != null
                && !(isStarted = currentGenerator.startNext())) {
        }
    }

缓存资源

由于在上面的 onDataReady()后给 dataToCache 赋值了,所以这里会进入到 cacheData() 里面。在里面将资源写进磁盘缓存,然后给 sourceCacheGenerator 赋值为 DataCacheGenerator ,接着调用它的 startNext() 去磁盘获取资源。

为什么 SourceGenerator 获取到资源后不自己缓存后就返回还要调用 DataCacheGenerator 去再获取一遍呢?我想主要是为了使各个类遵循单一职责,DataCacheGenerator是负责从磁盘获取资源的则交给它去完成。在 onDataReady() 中,当不需要缓存的时候,SourceGenerator 也是直接回调给 DecodeJob 的。

    @Override
    public boolean startNext() {
        if (dataToCache != null) {
            Object data = dataToCache;
            dataToCache = null;
            cacheData(data);
        }

        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
            return true;
        }
    }
    
    /**
     * 将数据缓存至磁盘缓存
     */
    private void cacheData(Object dataToCache) {
        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);     //写进磁盘缓存
        } finally {
            loadData.fetcher.cleanup();
        }

        //在这里初始化sourceCacheGenerator
        sourceCacheGenerator =
                new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
    }
 

                            
                        
                    
                    
                    

你可能感兴趣的:(Glide源码解析之SourceGenerator)