美文网首页安卓开发博客
第三方开源库 Glide - 源码分析(补)

第三方开源库 Glide - 源码分析(补)

作者: 红橙Darren | 来源:发表于2017-12-29 22:55 被阅读1117次

    首先,在真正开始看源码之前,我们需要有个心里准备,决心是今天我们一定要搞懂它,不然充满好奇的进来,一脸蒙蔽的出去。带大家看了很多的第三方开源库和 Android 源码,又讲了 23 种设计模式基础,将这些结合起来,再去分析一些第三方库应该是 soEasy。只是希望我们自己能够去读懂,因为以后我们还会用到其他一些第三方框架。只有我们自己习得了看源码的能力才是最重要的。

    其次,像 Glide 、Picasso、ImageLoader 这些访问网络的框架,其内部实现的原理应该大致差不多,线程池,连接网络,解析图片,缓存图片,显示图片。那像你这么说是不是开发中选择哪个都行?看心情选择第三方框架肯定是不行的。

    最后,我们分析源码最好先从最基本的入手,调用简单方法一个方法一个方法走流程,最后再去抓细节。如果 UML 图你还没忘记还会画,那么可以画个 UML 时序图,便于我们走源代码流程。这篇文章的篇幅会很长请调整好呼吸:

    1.Glide.with()


    Glide.with() 的有很多重载方法,参数的类型都有 Context、Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 为什么要重载这么多方法,我们可以猜猜。其次,很多哥们在实际的开发过程中,可能都是传递的上下文 Context ,那么从现在开始最好不要这么传了。我选择其中一种分析一下:

        public static RequestManager with(FragmentActivity activity) {
            RequestManagerRetriever retriever = RequestManagerRetriever.get();
            return retriever.get(activity);
        }
    
        public RequestManager get(FragmentActivity activity) {
            if (Util.isOnBackgroundThread()) {
                return get(activity.getApplicationContext());
            } else {
                assertNotDestroyed(activity);
                FragmentManager fm = activity.getSupportFragmentManager();
                return supportFragmentGet(activity, fm);
            }
        }
    
        RequestManager supportFragmentGet(Context context, FragmentManager fm) {
            SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
            RequestManager requestManager = current.getRequestManager();
            if (requestManager == null) {
                requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
                current.setRequestManager(requestManager);
            }
            return requestManager;
        }
    
        SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
            SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
            if (current == null) {
                current = pendingSupportRequestManagerFragments.get(fm);
                if (current == null) {
                    // 创建一个 Fragment 绑定到 Activity 上面
                    current = new SupportRequestManagerFragment();
                    pendingSupportRequestManagerFragments.put(fm, current);
                    fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                    handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
                }
            }
            return current;
        }
    

    上面是我挑的一些主要代码,主要的还是在 getSupportRequestManagerFragment 这个方法,这个方法干了啥?其实蛮好理解的,就是去创建一个 Fragment 然后把创建的 Fragment 绑定到 Activity 上面,搞这个干什么?其实是为了绑定监听 Activity 的生命周期,比如 RecyclerView 一个列表都在加载图片,这个时候我退出当前 Activity 应当停止访问网络加载图片,绑定生命周期就有这个好处。到底是怎么做监听的?看下源码:

        RequestManager supportFragmentGet(Context context, FragmentManager fm) {
            SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
            RequestManager requestManager = current.getRequestManager();
            if (requestManager == null) {
                requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
                current.setRequestManager(requestManager);
            }
            return requestManager;
        }
    
        RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
                RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
    
            // If we're the application level request manager, we may be created on a background thread. In that case we
            // cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
            // ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
            if (Util.isOnBackgroundThread()) {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        lifecycle.addListener(RequestManager.this);
                    }
                });
            } else {
                // 在这里注册为了 this 
                lifecycle.addListener(this);
            }
            lifecycle.addListener(connectivityMonitor);
        }
    

    LifecycleListener 是个接口有三个方法,onStart() , onStop(),onDestroy() 对应 RequestManager 中的实现分别是:

         /**
         * Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
         * permission is present) and restarts failed or paused requests.
         */
        @Override
        public void onStart() {
            // onStart might not be called because this object may be created after the fragment/activity's onStart method.
            resumeRequests();
        }
    
        /**
         * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
         * permission is present) and pauses in progress loads.
         */
        @Override
        public void onStop() {
            pauseRequests();
        }
    
        /**
         * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed
         * requests.
         */
        @Override
        public void onDestroy() {
            requestTracker.clearRequests();
        }
    

    所以总结一下Glide.with() 这方法返回的是一个 RequestManager 主要用来监听一些生命周期,以此来管理加载请求 Request 。拿 FragmentActivity 来说当 Activity 退出了会调用 onDestroy() -> requestTracker.clearRequests(); 会清理所有的图片加载请求,并且一个 FragmentActivity 只有一个 SupportRequestManagerFragment 和一个 RequestManager ,怎么看出来的?不是有那么多的 if else 判断代码,当然细节我们不用太关注,看下时序图:


    Glide.with()

    2.Glide.with().load()

        public DrawableTypeRequest<String> load(String string) {
            return (DrawableTypeRequest<String>) fromString().load(string);
        }
    
        public DrawableTypeRequest<String> fromString() {
            return loadGeneric(String.class);
        }
    
        private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
            ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
            ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
                    Glide.buildFileDescriptorModelLoader(modelClass, context);
            if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
                throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
                        + " which there is a registered ModelLoader, if you are using a custom model, you must first call"
                        + " Glide#register with a ModelLoaderFactory for your custom model class");
            }
    
            return optionsApplier.apply(
                    new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                            glide, requestTracker, lifecycle, optionsApplier));
        }
    

    Glide.buildStreamModelLoader 和 Glide.buildFileDescriptorModelLoader 这两个方法看不太懂先放一边,待会再回来找他,先记住他的变量名 streamModelLoader 和 fileDescriptorModelLoader,接下来创建了一个 DrawableTypeRequest 返回。这个方法比较简单,时序图:

    Glide.with().load()

    3.Glide.with().load().into()

        public Target<TranscodeType> into(ImageView view) {
            Util.assertMainThread();
            if (view == null) {
                throw new IllegalArgumentException("You must pass in a non null View");
            }
    
            return into(glide.buildImageViewTarget(view, transcodeClass));
        }
    
        public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
            if (GlideDrawable.class.isAssignableFrom(clazz)) {
                return (Target<Z>) new GlideDrawableImageViewTarget(view);
            } else if (Bitmap.class.equals(clazz)) {
                return (Target<Z>) new BitmapImageViewTarget(view);
            } else if (Drawable.class.isAssignableFrom(clazz)) {
                return (Target<Z>) new DrawableImageViewTarget(view);
            } else {
                throw new IllegalArgumentException("Unhandled class: " + clazz
                        + ", try .as*(Class).transcode(ResourceTranscoder)");
            }
        }
    
        public <Y extends Target<TranscodeType>> Y into(Y target) {
            Util.assertMainThread();
            // 通过 target 构建一个 Request 
            Request request = buildRequest(target);
            target.setRequest(request);
            lifecycle.addListener(target);
            // 看样子要执行
            requestTracker.runRequest(request);
            return target;
        }
    

    transcodeClass 是在构造函数里面赋值初始化的,也就是在第二步 load 的时候赋值的是 GlideDrawable.class 那么 buildTarget 方法返回的应该是 GlideDrawableImageViewTarget 对象,通过 Target
    构建了一个 Request ,最后调用 requestTracker.runRequest(request); 接下来肯定要分析一下怎么构建 buildRequest 的:

        private Request buildRequest(Target<TranscodeType> target) {
            if (priority == null) {
                priority = Priority.NORMAL;
            }
            return buildRequestRecursive(target, null);
        }
        
        // 这里省略了一个处理缩略图的方法,感兴趣可以自己研究一下
        
        // obtainRequest
        private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
                RequestCoordinator requestCoordinator) {
            // 构建了一个 GenericRequest 参数很多,但我肯定都知道是啥意思
            return GenericRequest.obtain(
                    loadProvider,
                    model,
                    signature,
                    context,
                    priority,
                    target,
                    sizeMultiplier,
                    placeholderDrawable,
                    placeholderId,
                    errorPlaceholder,
                    errorId,
                    fallbackDrawable,
                    fallbackResource,
                    requestListener,
                    requestCoordinator,
                    glide.getEngine(),
                    transformation,
                    transcodeClass,
                    isCacheable,
                    animationFactory,
                    overrideWidth,
                    overrideHeight,
                    diskCacheStrategy);
        }
    

    buildRequest 构建的是一个 GenericRequest 实例,传递的参数很多但是我们肯定都认识,比如 placeholderId,errorId,transformation,diskCacheStrategy 等等,接下来看下真正的 runRequest() 方法:

        /**
         * Starts tracking the given request.
         */
        public void runRequest(Request request) {
            requests.add(request);
            if (!isPaused) {
                request.begin();
            } else {
                pendingRequests.add(request);
            }
        }
    
       @Override
        public void begin() {
            startTime = LogTime.getLogTime();
            if (model == null) {
                 // 异常报错显示错误图片
                onException(null);
                return;
            }
    
            status = Status.WAITING_FOR_SIZE;
            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
                onSizeReady(overrideWidth, overrideHeight);
            } else {
                target.getSize(this);
            }
    
            if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
                target.onLoadStarted(getPlaceholderDrawable());
            }
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logV("finished run method in " + LogTime.getElapsedMillis(startTime));
            }
        }
    
        @Override
        public void onSizeReady(int width, int height) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
            }
            if (status != Status.WAITING_FOR_SIZE) {
                return;
            }
            status = Status.RUNNING;
    
            width = Math.round(sizeMultiplier * width);
            height = Math.round(sizeMultiplier * height);
    
            ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
            final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
    
            if (dataFetcher == null) {
                // 异常报错显示错误图片
                onException(new Exception("Failed to load model: \'" + model + "\'"));
                return;
            }
            ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
            }
            loadedFromMemoryCache = true;
            loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
                    priority, isMemoryCacheable, diskCacheStrategy, this);
            loadedFromMemoryCache = resource != null;
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
            }
        }
    

    渐渐的开始复杂起来了,modelLoader 和 transcoder 都是通过 loadProvider 获取的,那么我们必须要了解一下 loadProvider 是怎么实例化的,他是在 obtainRequest 的时候传递初始化的,是在第二步 load 的时候在 DrawableTypeRequest 的构造方法中调用 buildProvider 构建的,是一个 FixedLoadProvider 。接下来看下 engine.load() 方法:

        public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
                DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
                Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
            Util.assertMainThread();
            long startTime = LogTime.getLogTime();
    
            final String id = fetcher.getId();
            EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                    loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                    transcoder, loadProvider.getSourceEncoder());
    
            EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
            if (cached != null) {
                cb.onResourceReady(cached);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Loaded resource from cache", startTime, key);
                }
                return null;
            }
    
            EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
            if (active != null) {
                cb.onResourceReady(active);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Loaded resource from active resources", startTime, key);
                }
                return null;
            }
    
            EngineJob current = jobs.get(key);
            if (current != null) {
                current.addCallback(cb);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Added to existing load", startTime, key);
                }
                return new LoadStatus(cb, current);
            }
    
            EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
            DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
                    transcoder, diskCacheProvider, diskCacheStrategy, priority);
            EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
            jobs.put(key, engineJob);
            engineJob.addCallback(cb);
            engineJob.start(runnable);
    
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Started new load", startTime, key);
            }
            return new LoadStatus(cb, engineJob);
        }
    

    有一部分代码是处理缓存的,我们主要关注看下 DecodeJob 调用 start 方法,让线程池去执行 EngineRunnable 的 run 方法:

        @Override
        public void run() {
            if (isCancelled) {
                return;
            }
    
            Exception exception = null;
            Resource<?> resource = null;
            try {
                resource = decode();
            } catch (Exception e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Exception decoding", e);
                }
                exception = e;
            }
    
            if (isCancelled) {
                // 如果被取消了,回收资源,不在往下执行
                if (resource != null) {
                    resource.recycle();
                }
                return;
            }
    
            if (resource == null) {
                // 失败
                onLoadFailed(exception);
            } else {
                // 处理成功
                onLoadComplete(resource);
            }
        }
    
        private Resource<?> decode() throws Exception {
            // 判断缓存
            if (isDecodingFromCache()) {
                return decodeFromCache();
            } else {
                return decodeFromSource();
            }
        }
    
        private Resource<?> decodeFromSource() throws Exception {
            return decodeJob.decodeFromSource();
        }
    

    最终调用的是 decodeJob.decodeFromSource() 方法,走了半天还没找到联网解析流的代码,应该在 decodeJob 里面吧?点进去看下:

        public Resource<Z> decodeFromSource() throws Exception {
            Resource<T> decoded = decodeSource();
            return transformEncodeAndTranscode(decoded);
        }
    
        private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
            long startTime = LogTime.getLogTime();
            Resource<T> transformed = transform(decoded);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transformed resource from source", startTime);
            }
    
            writeTransformedToCache(transformed);
    
            startTime = LogTime.getLogTime();
            Resource<Z> result = transcode(transformed);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transcoded transformed from source", startTime);
            }
            return result;
        }
    
        private Resource<T> decodeSource() throws Exception {
            Resource<T> decoded = null;
            try {
                long startTime = LogTime.getLogTime();
                final A data = fetcher.loadData(priority);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Fetched data", startTime);
                }
                if (isCancelled) {
                    return null;
                }
                decoded = decodeFromSourceData(data);
            } finally {
                fetcher.cleanup();
            }
            return decoded;
        }
    

    decodeFromSource()方法,其实它的工作分为两部,第一步是调用decodeSource()方法来获得一个Resource对象,第二步是调用transformEncodeAndTranscode()方法来处理这个Resource对象。在 decodeSource() 方法中调用了 fetcher.loadData(priority) 点击发现是个抽象方法,我们必须要找到实现类,要找到 fetcher 这个实现类那么先得找到 loadProvider 这个实现类,是在第二个 load 步骤中的 ImageVideoModelLoader 这个类,找到 getResourceFetcher() 返回的就是 fetcher 是 ImageVideoFetcher 类,找到 loadData() 方法如下:

            @Override
            public ImageVideoWrapper loadData(Priority priority) throws Exception {
                InputStream is = null;
                if (streamFetcher != null) {
                    try {
                        is = streamFetcher.loadData(priority);
                    } catch (Exception e) {
                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
                            Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
                        }
                        if (fileDescriptorFetcher == null) {
                            throw e;
                        }
                    }
                }
                ParcelFileDescriptor fileDescriptor = null;
                if (fileDescriptorFetcher != null) {
                    try {
                        fileDescriptor = fileDescriptorFetcher.loadData(priority);
                    } catch (Exception e) {
                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
                            Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
                        }
                        if (is == null) {
                            throw e;
                        }
                    }
                }
                return new ImageVideoWrapper(is, fileDescriptor);
            }
    

    发现其方法内部又调用了 streamFetcher.loadData() 方法,streamFetcher 又是调用了 streamLoader.getResourceFetcher() 获取的,所以我们必须要找到 streamLoader , 它其实是第二步中通过 Glide.buildStreamModelLoader() 方法获取的,我们看下:

    public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass) {
            ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
            if (result != null) {
                // We've already tried to create a model loader and can't with the currently registered set of factories,
                // but we can't use null to demonstrate that failure because model loaders that haven't been requested
                // yet will be null in the cache. To avoid this, we use a special signal model loader.
                if (NULL_MODEL_LOADER.equals(result)) {
                    return null;
                } else {
                    return result;
                }
            }
    
            final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
            if (factory != null) {
                result = factory.build(context, this);
                cacheModelLoader(modelClass, resourceClass, result);
            } else {
                // We can't generate a model loader for the given arguments with the currently registered set of factories.
                cacheNullLoader(modelClass, resourceClass);
            }
            return result;
        }
    

    modelClass 是 String.class,resourceClass 是 InputStream.class 通过 getFactory 方法获取到 ModelLoaderFactory 对象,调用 factory.build() 方法构建了一个 ModelLoader 对象,是一个 StreamStringLoader 对象,过程有点曲折,但是琢磨琢磨还是能找到的。回到上面找 getResourceFetcher() 方法返回的是 HttpUrlFetcher 对象,接下来看他的 loadData() 方法:

        @Override
        public InputStream loadData(Priority priority) throws Exception {
            return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
        }
    
        private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
                throws IOException {
            if (redirects >= MAXIMUM_REDIRECTS) {
                throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
            } else {
                // Comparing the URLs using .equals performs additional network I/O and is generally broken.
                // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
                try {
                    if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                        throw new IOException("In re-direct loop");
                    }
                } catch (URISyntaxException e) {
                    // Do nothing, this is best effort.
                }
            }
            urlConnection = connectionFactory.build(url);
            for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
              urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
            }
            urlConnection.setConnectTimeout(2500);
            urlConnection.setReadTimeout(2500);
            urlConnection.setUseCaches(false);
            urlConnection.setDoInput(true);
    
            // Connect explicitly to avoid errors in decoders if connection fails.
            urlConnection.connect();
            if (isCancelled) {
                return null;
            }
            final int statusCode = urlConnection.getResponseCode();
            if (statusCode / 100 == 2) {
                return getStreamForSuccessfulRequest(urlConnection);
            } else if (statusCode / 100 == 3) {
                String redirectUrlString = urlConnection.getHeaderField("Location");
                if (TextUtils.isEmpty(redirectUrlString)) {
                    throw new IOException("Received empty or null redirect url");
                }
                URL redirectUrl = new URL(url, redirectUrlString);
                return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
            } else {
                if (statusCode == -1) {
                    throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
                }
                throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
            }
        }
    
        private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
                throws IOException {
            if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
                int contentLength = urlConnection.getContentLength();
                stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
            } else {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
                }
                stream = urlConnection.getInputStream();
            }
            return stream;
        }
    

    上面这个代码一般都能读懂了,就是通过 urlConnection 联网请求数据,这里返回的是一个 InputStream 并没有做任何的一些其他处理,所以我们才刚刚开始,我其实还是比较关心他怎么处理 InputStream 的,因为开发中经常用到,刚好可以借鉴一下。接着回到上面去看下 decodeFromSourceData 这个方法:

        private Resource<T> decodeFromSourceData(A data) throws IOException {
            final Resource<T> decoded;
            if (diskCacheStrategy.cacheSource()) {
                decoded = cacheAndDecodeSourceData(data);
            } else {
                long startTime = LogTime.getLogTime();
                decoded = loadProvider.getSourceDecoder().decode(data, width, height);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Decoded from source", startTime);
                }
            }
            return decoded;
        }
    

    调用了 loadProvider.getSourceDecoder().decode() 方法来进行解码。loadProvider 就是刚才在 onSizeReady() 方法中得到的FixedLoadProvider,而 getSourceDecoder() 得到的则是一个 GifBitmapWrapperResourceDecoder 对象,也就是要调用这个对象的 decode() 方法来对图片进行解码。那么我们来看下 GifBitmapWrapperResourceDecoder 的代码:

        // @see ResourceDecoder.decode
        @Override
        public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
            ByteArrayPool pool = ByteArrayPool.get();
            byte[] tempBytes = pool.getBytes();
    
            GifBitmapWrapper wrapper = null;
            try {
                wrapper = decode(source, width, height, tempBytes);
            } finally {
                pool.releaseBytes(tempBytes);
            }
            return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
        }
    
        private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
            final GifBitmapWrapper result;
            if (source.getStream() != null) {
                result = decodeStream(source, width, height, bytes);
            } else {
                result = decodeBitmapWrapper(source, width, height);
            }
            return result;
        }
    
        private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
                throws IOException {
            InputStream bis = streamFactory.build(source.getStream(), bytes);
            bis.mark(MARK_LIMIT_BYTES);
            ImageHeaderParser.ImageType type = parser.parse(bis);
            bis.reset();
    
            GifBitmapWrapper result = null;
            if (type == ImageHeaderParser.ImageType.GIF) {
                result = decodeGifWrapper(bis, width, height);
            }
            // Decoding the gif may fail even if the type matches.
            if (result == null) {
                // We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
                // pass in a new source containing the buffered stream rather than the original stream.
                ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
                result = decodeBitmapWrapper(forBitmapDecoder, width, height);
            }
            return result;
        }
    
        private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
            GifBitmapWrapper result = null;
    
            Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
            if (bitmapResource != null) {
                result = new GifBitmapWrapper(bitmapResource, null);
            }
    
            return result;
        }
    

    decodeStream 会判断是不是 Gif , 如果不是 Gif 会调用 decodeBitmapWrapper 方法,这个时候会调用 bitmapDecoder.decode() 方法,这个方法应该要去处理图片了?赶紧找到实现类 StreamBitmapDecoder 看下:

        @Override
        public Resource<Bitmap> decode(InputStream source, int width, int height) {
            Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
            return BitmapResource.obtain(bitmap, bitmapPool);
        }
    
        public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
            final ByteArrayPool byteArrayPool = ByteArrayPool.get();
            final byte[] bytesForOptions = byteArrayPool.getBytes();
            final byte[] bytesForStream = byteArrayPool.getBytes();
            final BitmapFactory.Options options = getDefaultOptions();
    
            // Use to fix the mark limit to avoid allocating buffers that fit entire images.
            RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
                    is, bytesForStream);
            // Use to retrieve exceptions thrown while reading.
            // TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
            // if a Bitmap is partially decoded, consider removing.
            ExceptionCatchingInputStream exceptionStream =
                    ExceptionCatchingInputStream.obtain(bufferedStream);
            // Use to read data.
            // Ensures that we can always reset after reading an image header so that we can still attempt to decode the
            // full image even when the header decode fails and/or overflows our read buffer. See #283.
            MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
            try {
                exceptionStream.mark(MARK_POSITION);
                int orientation = 0;
                try {
                    orientation = new ImageHeaderParser(exceptionStream).getOrientation();
                } catch (IOException e) {
                    if (Log.isLoggable(TAG, Log.WARN)) {
                        Log.w(TAG, "Cannot determine the image orientation from header", e);
                    }
                } finally {
                    try {
                        exceptionStream.reset();
                    } catch (IOException e) {
                        if (Log.isLoggable(TAG, Log.WARN)) {
                            Log.w(TAG, "Cannot reset the input stream", e);
                        }
                    }
                }
    
                options.inTempStorage = bytesForOptions;
    
                final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
                final int inWidth = inDimens[0];
                final int inHeight = inDimens[1];
    
                final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
                final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
    
                final Bitmap downsampled =
                        downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
                                decodeFormat);
    
                // BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
                // and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
                // we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
                final Exception streamException = exceptionStream.getException();
                if (streamException != null) {
                    throw new RuntimeException(streamException);
                }
    
                Bitmap rotated = null;
                if (downsampled != null) {
                    rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
    
                    if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
                        downsampled.recycle();
                    }
                }
    
                return rotated;
            } finally {
                byteArrayPool.releaseBytes(bytesForOptions);
                byteArrayPool.releaseBytes(bytesForStream);
                exceptionStream.release();
                releaseOptions(options);
            }
        }
    

    到这里我们就真正开始解析 Bitmap 了,就是从 InputStream 中去获取 Bitmap 对象,这些代码还是比较容易看懂的,Downsampler.decode() 返回的是一个 Bitmap 对象,StreamBitmapDecoder.decode() 对 Bitmap 又进行了一层包裹,返回的是 BitmapResource 。接下来我们回到 GifBitmapWrapperResourceDecoder.decode 中返回的是 GifBitmapWrapperResource , 接着往回走就回到了 DecodeJob.transformEncodeAndTranscode() 中:

        private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
            long startTime = LogTime.getLogTime();
            Resource<T> transformed = transform(decoded);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transformed resource from source", startTime);
            }
    
            writeTransformedToCache(transformed);
    
            startTime = LogTime.getLogTime();
            Resource<Z> result = transcode(transformed);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transcoded transformed from source", startTime);
            }
            return result;
        }
    
        private Resource<T> transform(Resource<T> decoded) {
            if (decoded == null) {
                return null;
            }
    
            Resource<T> transformed = transformation.transform(decoded, width, height);
            if (!decoded.equals(transformed)) {
                decoded.recycle();
            }
            return transformed;
        }
    
        private Resource<Z> transcode(Resource<T> transformed) {
            if (transformed == null) {
                return null;
            }
            return transcoder.transcode(transformed);
        }
    

    上面主要分为三步,transform(),writeTransformedToCache() 写入缓存,transcode() 这里要注意了是把 泛型 T 转成了 Z 。transcode() 方法中又是调用了 transcoder 的 transcode() 方法,发现又找不到代码了,所以我们有得回去找 transcoder 实现类,这也是说我们为什么最好要会画 UML 图,因为代码架构复杂的情况下,我们在里面游几天都上不来。还是回去找找,是在第二步 load 的时候创建的 glide.buildTranscoder(resourceClass, transcodedClass) 是 GifBitmapWrapperDrawableTranscoder :

        @SuppressWarnings("unchecked")
        @Override
        public Resource<GlideDrawable> transcode(Resource<GifBitmapWrapper> toTranscode) {
            GifBitmapWrapper gifBitmap = toTranscode.get();
            Resource<Bitmap> bitmapResource = gifBitmap.getBitmapResource();
    
            final Resource<? extends GlideDrawable> result;
            if (bitmapResource != null) {
                result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
            } else {
                result = gifBitmap.getGifResource();
            }
            // This is unchecked but always safe, anything that extends a Drawable can be safely cast to a Drawable.
            return (Resource<GlideDrawable>) result;
        }
    
        @Override
        public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
            GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
            return new GlideBitmapDrawableResource(drawable, bitmapPool);
        }
    

    这里主要是把 GifBitmapWrapper 转成了 GlideDrawable ,为什么这么弄肯定是为了后面方便显示,因为 Gif 资源返回的已经是 GifDrawable 了并不需要转换,而图片资源返回的是 Bitmap ,做一次转换之后统一转成 GlideBitmapDrawable 这样方便后面做显示,那么显示我们要回到 EngineRunnable 中的 run 方法了:

        @Override
        public void run() {
            if (isCancelled) {
                return;
            }
    
            Exception exception = null;
            Resource<?> resource = null;
            try {
                resource = decode();
            } catch (Exception e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Exception decoding", e);
                }
                exception = e;
            }
    
            if (isCancelled) {
                if (resource != null) {
                    resource.recycle();
                }
                return;
            }
    
            if (resource == null) {
                onLoadFailed(exception);
            } else {
                onLoadComplete(resource);
            }
        }
    

    现在我们来总结一下 decode() 方法都干了啥,首先是 DecodeJob.decodeFromSource() -> fetcher.loadData() 去联网获取 InputStream -> loadProvider.getSourceDecoder().decode() -> bitmapDecoder.decode() 解析 InputStream 压缩旋转等等返回 Bitmap -> DecodeJob.transformEncodeAndTranscode() 去转换保证返回数据一致 GlideDrawable 。接下来只需要看下 onLoadComplete 应该就要完了:

        private void onLoadComplete(Resource resource) {
            manager.onResourceReady(resource);
        }
    
        @Override
        public void onResourceReady(final Resource<?> resource) {
            this.resource = resource;
            MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
        }
    
        private static class MainThreadCallback implements Handler.Callback {
    
            @Override
            public boolean handleMessage(Message message) {
                if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
                    EngineJob job = (EngineJob) message.obj;
                    if (MSG_COMPLETE == message.what) {
                        job.handleResultOnMainThread();
                    } else {
                        job.handleExceptionOnMainThread();
                    }
                    return true;
                }
    
                return false;
            }
        }
    
        private void handleResultOnMainThread() {
            if (isCancelled) {
                resource.recycle();
                return;
            } else if (cbs.isEmpty()) {
                throw new IllegalStateException("Received a resource without any callbacks to notify");
            }
            engineResource = engineResourceFactory.build(resource, isCacheable);
            hasResource = true;
    
            // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
            // synchronously released by one of the callbacks.
            engineResource.acquire();
            listener.onEngineJobComplete(key, engineResource);
    
            for (ResourceCallback cb : cbs) {
                if (!isInIgnoredCallbacks(cb)) {
                    engineResource.acquire();
                    cb.onResourceReady(engineResource);
                }
            }
            // Our request is complete, so we can release the resource.
            engineResource.release();
        }
    

    这里主要是切换到主线程,然后循环 cbs 调用 cb 的 onResourceReady() 方法,那么 cb 又是啥?其实就是 GenericRequest.this,看下 onResourceReady():

        @Override
        public void onResourceReady(Resource<?> resource) {
            if (resource == null) {
                onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
                        + " inside, but instead got null."));
                return;
            }
    
            Object received = resource.get();
            if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
                releaseResource(resource);
                onException(new Exception("Expected to receive an object of " + transcodeClass
                        + " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
                        + " inside Resource{" + resource + "}."
                        + (received != null ? "" : " "
                            + "To indicate failure return a null Resource object, "
                            + "rather than a Resource object containing null data.")
                ));
                return;
            }
    
            if (!canSetResource()) {
                releaseResource(resource);
                // We can't set the status to complete before asking canSetResource().
                status = Status.COMPLETE;
                return;
            }
    
            onResourceReady(resource, (R) received);
        }
    
        /**
         * Internal {@link #onResourceReady(Resource)} where arguments are known to be safe.
         *
         * @param resource original {@link Resource}, never <code>null</code>
         * @param result object returned by {@link Resource#get()}, checked for type and never <code>null</code>
         */
        private void onResourceReady(Resource<?> resource, R result) {
            // We must call isFirstReadyResource before setting status.
            boolean isFirstResource = isFirstReadyResource();
            status = Status.COMPLETE;
            this.resource = resource;
    
            if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
                    isFirstResource)) {
                GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
                target.onResourceReady(result, animation);
            }
    
            notifyLoadSuccess();
    
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
                        + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
            }
        }
    

    最终调用了 target 的 onResourceReady() 方法,那么 target 又是谁呢?不知道我们是否还记得这代码into(glide.buildImageViewTarget(view, transcodeClass)) 在之前就分析过的 GlideDrawableImageViewTarget :

        @Override
        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
            if (!resource.isAnimated()) {
                //TODO: Try to generalize this to other sizes/shapes.
                // This is a dirty hack that tries to make loading square thumbnails and then square full images less costly
                // by forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions.
                // If a drawable is replaced in an ImageView by another drawable with different intrinsic dimensions,
                // the ImageView requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers
                // lots of these calls and causes significant amounts of jank.
                float viewRatio = view.getWidth() / (float) view.getHeight();
                float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
                if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
                        && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
                    resource = new SquaringDrawable(resource, view.getWidth());
                }
            }
            super.onResourceReady(resource, animation);
            this.resource = resource;
            resource.setLoopCount(maxLoopCount);
            resource.start();
        }
    
        @Override
        public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
            if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
                setResource(resource);
            }
        }
    
        @Override
        protected void setResource(GlideDrawable resource) {
            view.setImageDrawable(resource);
        }
    
    Glide.with().load().into()

    走下流程其实还是非常简单的,就是需要大家多花些时间多点耐心,这第一步才刚刚开始,如果第一步迈不过去,后面我们就无法分析细节,比如缓存的处理,架构的模式,图片和Gif以及视频的解析处理,等等。源码也不能白看,很多思想是要用到实际项目中的,后面文章我将陆陆续续去分析。还是那句话 Java基础和设计模式基础打牢,自己习得了看源码的能力才是最重要的。

    所有分享大纲:Android进阶之旅 - 系统架构篇

    视频讲解地址:https://pan.baidu.com/s/1pMLehUr

    相关文章

      网友评论

      • 泽毛:麻烦问下,时序图是用什么软件画的呢?
        红橙Darren:@泽毛 我用的是starUml,Windows和mac都能用,powerdesigner 也行

      本文标题:第三方开源库 Glide - 源码分析(补)

      本文链接:https://www.haomeiwen.com/subject/exfvgxtx.html