美文网首页
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