美文网首页
Glide图片加载源码浅析

Glide图片加载源码浅析

作者: 源来是你啊 | 来源:发表于2018-09-01 17:13 被阅读0次

前言

图片加载框架,相对于UniversalImageLoader,Picasso,它还支持video,Gif,SVG格式,支持缩略图请求,旨在打造更好的列表图片滑动体验。Glide有生命周期的概念(主要是对请求进行pause,resume,clear),而且其生命周期与Activity/Fragment的生命周期绑定,支持Volley,OkHttp,并提供了相应的integration libraries,内存方面也更加友好

使用

Glide基本使用:

Glide.with(this).load("https://...").into(imageView);

Glide调用基本分为三个步骤:with、load、into,那么我们就分为这三个步骤展开讨论。

with

with方法传入context作为参数,所以主要是初始化创建重要对象。with有许多重载,一般就通过参数分为两类:
applicationContext和非applicationContext。


public static RequestManager with(Context context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }

public static RequestManager with(Activity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

public static RequestManager with(android.app.Fragment fragment) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(fragment);
    }

由此可见他们都是相同的作用,都是获取一个RequestManager实例,但既然要分开调用,说明他们所获取的RequestManager又有所区别。

那么看一下它们的get方法:
参数为applicationContext

public RequestManager get(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if (Util.isOnMainThread() && !(context instanceof Application)) {
            if (context instanceof FragmentActivity) {
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) {
                return get(((ContextWrapper) context).getBaseContext());
            }
        }

        return getApplicationManager(context);
    }

参数为非applicationContext

public RequestManager get(Activity activity) {
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            android.app.FragmentManager fm = activity.getFragmentManager();
            return fragmentGet(activity, fm);
        }
    }

以上可知,以applicationContext为参数的最终调用getApplicationManager方法获取;
以非applicationContext为参数的最终调用fragmentGet方法获取;

private RequestManager getApplicationManager(Context context) {
        // Either an application context or we're on a background thread.
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }

        return applicationManager;
    }

当以applicationContext为参数时,用同步锁获取单例,并且添加了ApplicationLifecycle生命周期的监听,所以这个RequestManager实例的生命周期会和application相同。

RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
        //在fragementManager中找到RequestManagerFragment
        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
            //若为空,则在pendingRequestManagerFragments集合中找到RequestManagerFragment
            current = pendingRequestManagerFragments.get(fm);
            if (current == null) {
                //若为空,则创建一个隐藏的RequestManagerFragment并绑定提交
                current = new RequestManagerFragment();
                pendingRequestManagerFragments.put(fm, current);
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
            }
        }
        return current;
    }

RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        RequestManagerFragment current = getRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            //创建RequestManager并与requestManagerFragment绑定
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

当传入参数为非applicationContext时,Glide会创建一个隐藏的requestManagerFragment,继承自fragment并依附于activity或fragment上,这样requestManagerFragement就和activity或fragment的生命周期一致。而requestManagerFragment中保存了requestManager对象,那么说明requestManager的生命周期和activity或fragement生命周期一致。

也就是说,如果当某个Glide加载图片请求是在Activity或fragment中进行的,当这个请求还没加载完成时,这个activity或fragemnt就暂停或销毁了,那么这个Glide图片加载请求也会随之暂停或消亡。很好的避免了资源浪费和内存泄漏。

而在传入参数为appplicationContext的Glide加载请求就会随着整个程序的推出而消亡。

这是一个很棒的设计思路,值得学习。

至此,Glide的第一步with的工作已经完成。

load

load方法里面其实也是在做一些准备工作,主要创建一些加载图片处理图片的类。不过在此之前,需要了解一下Glide加载图片的流程和几个基本概念。

Model:资源模型,表示数据来源。比如图片url,本地图片以及资源id;

Data:表示从网络或本地加载的实体图片数据流,一般是inputStream;

ModelLoader:指将model加工为Data的加载器;

Resource:将Data实体数据流解码后的Bitmap;

ResourceDecoder:将Data解码为bitmap的解码器;

TransfromedResource:将bitmap加工后(例如裁剪,缩略,模糊)后所转化的bitmap

TranscodedResource:由于图片的类型有bitmap、drawable等等,类型不同意,于是Glide就将他们统一转化为GlideBitmapDrawable,统称TranscodedResource;

Target:Glide需要将图片显示到目标上,例如ImageView,所以Glide把显示目标封装为Target;

Glide图片记载流程

由上图我们可以看到,整个加载图片的流程为:
原始图片资源模型通过ModelLoader加载后变为Data数据流inputstream;然后经由ResourceDecoder解码为Resource再经由Transfrom转换加工变为TransfromedResource;之后统一类型变为TranscodedResource,最后显示到Target之上;

接下来我们再转换一下视角看一看load方法:


public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }

public DrawableTypeRequest<Uri> load(Uri uri) {
        return (DrawableTypeRequest<Uri>) fromUri().load(uri);
    }

public DrawableTypeRequest<File> load(File file) {
        return (DrawableTypeRequest<File>) fromFile().load(file);
    }

public DrawableTypeRequest<Integer> load(Integer resourceId) {
        return (DrawableTypeRequest<Integer>) fromResource().load(resourceId);
    }

由以上代码可以看出,加载图片的数据源有多种;但最终都会被转换为DrawableTypeRequest,这个类即为我们图片请求类;

public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType>

它继承自DrawableRequestBuilder,有名称可知,它是一个构建器,里面应该都是配置属性参数的方法;

那么还是来看一看fromString做了哪些工作:

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));
    }

我们重点看看最后一行,代码中new DrawableTypeRequest创建了一个图片类型的请求:
而传入参数modelClass即为资源模型, streamModelLoader即为data模型加载器(将model变为inputstream), fileDescriptorModelLoader也是模型加载器(将model变为ParcelFileDescriptor);最后将DrawableTypeRequest作为参数传入apply方法中并返回DrawableTypeRequest;

然后看下 fromString().load(string)中的load方法:
在GenericRequestBuilder类(即DrawableTypeRequest父类)中:

public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
        this.model = model;
        isModelSet = true;
        return this;
    }

就是指定一下资源模型,综上所述在Glide.load方法中,创建了对应类型的图片加载请求对象并设置相关属性值。

into

从上面的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");
        }

        if (!isTransformationSet && view.getScaleType() != null) {
            switch (view.getScaleType()) {
                case CENTER_CROP:
                    applyCenterCrop();
                    break;
                case FIT_CENTER:
                case FIT_START:
                case FIT_END:
                    applyFitCenter();
                    break;
                //$CASES-OMITTED$
                default:
                    // Do nothing.
            }
        }

        return into(glide.buildImageViewTarget(view, transcodeClass));

这段代码主要判断是否为主线程,然后设置了图片的显示样式,最后调用创建了ImageViewTarget并调用into方法;
glide.buildImageViewTarget方法最终点调用:

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)");
        }
    }

也就是说根据传入参数的转传类型返回相应的目标类型,所以asBiymap、asGif函数调用时决定的就是这个;

下面看into方法:

public <Y extends Target<TranscodeType>> Y into(Y target) {
        Util.assertMainThread();
        if (target == null) {
            throw new IllegalArgumentException("You must pass in a non null Target");
        }
        if (!isModelSet) {
            throw new IllegalArgumentException("You must first set a model (try #load())");
        }

        Request previous = target.getRequest();

        if (previous != null) {
            previous.clear();
            requestTracker.removeRequest(previous);
            previous.recycle();
        }

        Request request = buildRequest(target);
        target.setRequest(request);
        lifecycle.addListener(target);
        requestTracker.runRequest(request);

        return target;
    }

into方法作用比较明显,首先通过target获取之前的请求,并加以回收便于下次复用;之后创建新的请求并设置到target中;

这时有两个比较重要的方法:buildRequest和runRequest方法,buildRequest方法最终创建了一个图片加载请求,那么我们看看runRequest方法干了啥:

public void runRequest(Request request) {
        requests.add(request);
        if (!isPaused) {
            request.begin();
        } else {
            pendingRequests.add(request);
        }
    }

首先它将request加入requests队列中,然后判断状态开始执行请求;

@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));
        }
    }

我们看到在测量好图片大小之后begin会调用onSizeReady这个方法,继续追踪onSizeReady

@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));
        }
    }

我们发现这个函数里最最重要的函数engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
它就是用来开启加载我们的图片请求的函数,并会返回一个加载状态;那么让我们看看

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();
        //生成图片加载请求所对应的唯一key;
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());
        //通过key查找内存缓存(LruCache算法)中是否含有此图片(内存缓存)
        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;
        }
        //通过key查找内存缓存(弱引用)中是否含有此图片(内存缓存)
        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);
        //创建Runnable,准备加载图片
        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);
    }

那么继续追踪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方法得到一个resource,猜测他是加载本地文件缓存和网络缓存:

private Resource<?> decode() throws Exception {
        if (isDecodingFromCache()) {
            return decodeFromCache();//加载本地文件缓存
        } else {
            return decodeFromSource();//加载网络数据
        }
    }

果然,decodeFromCache是加载我们本地缓存文件数据;而decodeFromSource是加载图片网络数据;

最后当然会执行子线程回调onLoadFailed和onLoadComplete,这两个函数的处理操作实在EngineJob当中,EngineJon当中有一个主线程Handler处理子线程加载状态的消息:


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();
    }

private void handleExceptionOnMainThread() {
        if (isCancelled) {
            return;
        } else if (cbs.isEmpty()) {
            throw new IllegalStateException("Received an exception without any callbacks to notify");
        }
        hasException = true;

        listener.onEngineJobComplete(key, null);

        for (ResourceCallback cb : cbs) {
            if (!isInIgnoredCallbacks(cb)) {
                cb.onException(exception);
            }
        }
    }

这里面主要处理两件事:删除或存储缓存与图片的显示;

至此Glide加载图片的三大步骤执行完毕。

相关文章

网友评论

      本文标题:Glide图片加载源码浅析

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