美文网首页
Glide源码笔记

Glide源码笔记

作者: 无为3 | 来源:发表于2018-04-13 13:09 被阅读0次

    Glide几个基本概念

    1.Model:表示数据来源,可以是url,本地文件,资源id等
    2.Data:从数据源获取Model后,加工成原始数据,一般就是inputstream,glide称之为Data;而从数据源中获取原始数据的功能叫做ModelLoader。
    3.Resource:对刚获取的原始数据解码,例如将inputstream解码成bitmap,解码之后的资源称之为Resource,而负责解码功能的角色ResourceDecode
    4.TransformedResource:Resource进行变化,例如图片进行裁剪,用ResourceTransform这个功能转换,转换后的资源称为TransformedResource
    5.TranscodedResource:转码;glide支持gif,但是解码后的bitmap跟gifDrawable类型不是统一,为了类型统一把bitmap转换为glideBitmapDrawable,负责转码角色叫Transcode,转码完成后的角色叫做TranscodedResource
    6.Target:最终将图片显示到目标上imageview,将显示的目标封装成Target


    glide流程.png

    Glide使用方法

    Glide.with(context).load(url).into(imageview);//简单使用
                Glide.with(activity)
                        .load(url)
                        .placeholder(R.drawable.block_canary_icon)
                        .error(R.drawable.block_canary_icon)
                        .override(300,300)//指定图片的尺寸
                        .fitCenter()
                        .centerCrop()
                        .skipMemoryCache(true)//跳过内存缓存
                        .crossFade(1000)//设置渐变式显示的时间
                        .diskCacheStrategy(DiskCacheStrategy.NONE)//跳过磁盘缓存
                        .diskCacheStrategy(DiskCacheStrategy.SOURCE)//仅仅只缓存原来的全分辨率图像
                        .diskCacheStrategy(DiskCacheStrategy.RESULT)//仅仅缓存最终的图像
                        .diskCacheStrategy(DiskCacheStrategy.ALL)//缓存所有版本的图像
                        .priority(Priority.HIGH)//指定优先级,作为一个准则,并尽可能的处理这些请求
                        .into(iv);
    

    with方法

    按最简单的使用方式查看流程

    Glide.with(context).load(url).into(imageview);

    with方法可以传不同类型的参数,activity,fragment等,为了就是让Glide的生命周期我们安卓组件相挂钩。大致可以分两种,一种是Application,一种是非Application

    public static RequestManager with(Context context) {
    //RequestManagerRetriever就是获取RequestManager的,而RequestManager可以理解为我们处理图片加载请求的管理者
            RequestManagerRetriever retriever = RequestManagerRetriever.get();
            return retriever.get(context);
        }
    
    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());
                }
            }
    //这是Application参数
            return getApplicationManager(context);
        }
    
    //单例创建,Glide不需要做特殊处理,不需要跟生命周期挂钩
    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;
        }
    
    

    application参数比较简单,下面看下非application参数,例如Activity

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        public RequestManager get(Activity activity) {
        //不在主线程或者sdk小于3.0直接走application流程
            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);
            }
        }
    
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
          //创建空fragement,添加没有页面的Fragment用于监听activity的生命周期
            RequestManagerFragment current = getRequestManagerFragment(fm);
           //RequestManager 不仅仅能管理请求,也能实现界面生命周期的监听
            RequestManager requestManager = current.getRequestManager();
            if (requestManager == null) {
            //current.getLifecycle()这个跟fragement的生命周期绑定
                requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
    //将空的RequestManagerFragment 跟requestManager绑定(一一对应),监听activity生命周期来管理图片加载
                current.setRequestManager(requestManager);
            }
            return requestManager;
        }
    
    //通过lifecycle绑定生命周期
    public class RequestManagerFragment extends Fragment {
        private final ActivityFragmentLifecycle lifecycle;
        @Override
        public void onStart() {
            super.onStart();
            lifecycle.onStart();
        }
    
        @Override
        public void onStop() {
            super.onStop();
            lifecycle.onStop();
        }
    
    }
    

    总结:1.获取RequestManager 来管理我们的请求
    2.Glide使用RequestManagerFragment 这个无UI的fragment,让它绑定Activity,由它监听生命周期
    3.RequestManager通过RequestManagerFragment中的lifecycle监听生命周期

    load方法

    load方法在RequestManager中

    public DrawableTypeRequest<String> load(String string) {
    //DrawableTypeRequest是加载图片的所有request请求
            return (DrawableTypeRequest<String>) fromString().load(string);
        }
    
    //继承DrawableRequestBuilder,就是builder模式
    public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
    
    public BitmapTypeRequest<ModelType> asBitmap() {
            return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
                    fileDescriptorModelLoader, optionsApplier));
        }
    
    public GifTypeRequest<ModelType> asGif() {
            return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
        }
    }
    
    public class DrawableRequestBuilder<ModelType>
            extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
            implements BitmapOptions, DrawableOptions {
          //有很多的方法,都是builder模式中的,例如fitCenter(),centerCrop(),into()等
    }
    
    //Glide配置参数的最终类
    public class GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> implements Cloneable {
          // 跟踪图片请求的周期,还可以取消重启一些失败的图片请求
            protected final RequestTracker requestTracker;
            private int placeholderId;
            private int errorId;
            .......
    }
    

    看下创建DrawableTypeRequest的fromString()方法

    public DrawableTypeRequest<String> fromString() {
            return loadGeneric(String.class);
        }
    
    private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
    //创建两个ModelLoader,用于从数据源加载数据
            ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
            ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
                    Glide.buildFileDescriptorModelLoader(modelClass, context);
            return optionsApplier.apply(
                    new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                            glide, requestTracker, lifecycle, optionsApplier));
        }
    

    总结:load方法做的也是一些初始化工作,获取一个DrawableTypeRequest对象,通过这个对象就可以获取图片请求的request,从而进行我们实际的图片加载请求

    into方法

    真正的逻辑在GenericRequestBuilder类中实现.

    1.封装 target

    首先看下target类,我们穿进去的imageview等会封装成target

    public interface Target<R> extends LifecycleListener {
            void onLoadStarted(Drawable placeholder);
        void onLoadFailed(Exception e, Drawable errorDrawable);
        void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation);
        void onLoadCleared(Drawable placeholder);
        void getSize(SizeReadyCallback cb);
        void setRequest(Request request);
        Request getRequest();
    }
    
    //这个跟我们的activity等的生命周期绑定
    public interface LifecycleListener {
        void onStart();
        void onStop();
        void onDestroy();
    }
    
    
      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.
                }
            }
          //buildImageViewTarget创建target对象,如果调用了asBitmap返回BitmapImageViewTarget,没有可能返回GlideDrawableImageViewTarget
            return into(glide.buildImageViewTarget(view, transcodeClass));
        }
    

    2.request建立和begin方法

    通过上面的into方法,我们封装了target并且调用下面的into方法,这个方法主要是做两个步骤
    1.创建我们加载图片的request,在创建这个request之前需要对我们旧的target绑定的request删除,再新建一个target绑定到request
    2.发送request,交给我们的requestTracker(request的管理和跟踪器 );执行request.begin,重点是onSizeReady方法,里面开始图片的加载

    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())");
            }
          //获取target当前绑定的request对象,也就是旧的request
            Request previous = target.getRequest();
    
            if (previous != null) {
              //资源回收,设置状态等
                previous.clear();
               //将glide监听request暂停
                requestTracker.removeRequest(previous);
              //成员变量赋值为null,REQUEST_POOL.offer(this),request不用后将它放到请求池供复用
                previous.recycle();
            }
            
            // 创建需要的request对象,以前listview中item错位问题,给我们的view setTag来将我们的
            //view跟图片绑定;Glide解决方案一样,通过View settag方法将imageview跟图片的request
            //请求绑定在一起, 这样很容易判断当前Imageview是否复用,复用之前我们会先取消request请          
            //求,从而避免图片错位
       (1) Request request = buildRequest(target);
            //把request跟target绑定,通过view.setTag(request)
            target.setRequest(request);
            lifecycle.addListener(target);
        (2) requestTracker.runRequest(request);
    
            return target;
        }
    

    (1).Request request = buildRequest(target);加下来看下如果创建request;主要也是一些参数的赋值

       private Request buildRequest(Target<TranscodeType> target) {
            if (priority == null) {
                priority = Priority.NORMAL;
            }
            return buildRequestRecursive(target, null);
        }
    
        private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
                ......//跟缩略图有关的一些分支,略
                return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
        }
    
        private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
                RequestCoordinator requestCoordinator) {
            return GenericRequest.obtain(......//好多好多参数);
        }
    
        public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(....){
        //从线程池中获取一个request,能复用则复用
           GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
          if (request == null) {
                request = new GenericRequest<A, T, Z, R>();
            }
          // 一些初始化操作
            request.init(......);
        }
    
    

    (2) requestTracker.runRequest(request);接下来看下这个方法

    private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
    private final List<Request> pendingRequests = new ArrayList<Request>();
    
    public void runRequest(Request request) {
            requests.add(request);
            if (!isPaused) {
                //没有正在请求的request的话
                request.begin();
            } else {
                //挂起
                pendingRequests.add(request);
            }
        }
    

    先看下 request.begin();

    @Override
        public void begin() {
            startTime = LogTime.getLogTime();
            if (model == null) {
                onException(null);
                return;
            }
    
            status = Status.WAITING_FOR_SIZE;
            //是否设定 .override(300,300)
            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
                onSizeReady(overrideWidth, overrideHeight);
            } else {
              //获取size,最终还是会回调onSizeReady;如果获取size失败,则说明view还没绘制完,调用ViewTree监听
                target.getSize(this);
            }
    
            if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
              //获取占位图
                target.onLoadStarted(getPlaceholderDrawable());
            }
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logV("finished run method in " + LogTime.getElapsedMillis(startTime));
            }
        }
    

    3.Loadprovider

    由上面几步,到了request.begin方法,里面最重要的是最终会到onsizeReady方法,看下这个方法是如何实现的:
    1).通过LoadProvider去获取ModelLoader和ResourceTranscoder(原始数据解码的对象)
    2).根据ModelLoader去获取DataFetcher(将Data原始数据转换成我们能用的不同图片类型)
    3).最后将这些参数传到engine,去做实际的图片加载

    4.engine.load()

    Engine类负责开启图片加载,同时还可以管理正在使用和处于缓存状态的图片
    内存缓存:(1).LruCache:最近所使用的对象的强引用存于LinkedHashMap中,并且把最近最少使用的对象在缓存池达到预设值前从内存中移除;(2).弱引用。

    加载图片缓存有如下几个步骤:
    (1).从内存缓存中读取我们加载的图片loadFromCache(),如果存在直接回调onResourceReady()
    (2).如果获取失败,再调用loadFromActiveResources(),表明在缓存中没有我们再看下正在使用的图片中是否存在(Map<Key, WeakReference<EngineResource<?>>> activeResources 中获取);如果存在,再回调
    (3).都没有再启Runnable从磁盘或者网络中获取

    (1).如果命中缓存,将缓存从memerycache中移除,然后放到activeResources 中,返回engineResource中++acquired
    (2).如果缓存失效,则尝试从activeResources 获取
    ps:engineResource中acquired为0时会从activeResources 清除,然后放到memerycache中

    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();
          //加载图片的唯一标识,例如如果是网络图片,可能是url地址
            final String id = fetcher.getId();
          //Glide缓存的key
            EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                    loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                    transcoder, loadProvider.getSourceEncoder());
            //从缓存中获取图片;内部通过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;
            }
            
            //以EngineResource弱引用为value,也就是一个hashmap
            EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
            if (active != null) {
                //通过handler发送一条消息,然后执行逻辑切回主线程
                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);
        }
    

    Glide相关问题

    1.Bitmap
    (1).为什么Bitmap会导致OOM
    a.使用ListView,GridView:没有合理的处理缓存,大量加载Bitmap(可以使用三级缓存,设置Listview的滑动监听事件)。
    b.图片分辨率高,消耗的内存大,图片Bitmap所占用的内存=图片长度图片宽度一个像素占用的字节数;
    c.VM值上线,在每个rom都会设置每个应用的堆内存上限值;dalvik.vm.heapgrowlimit

    (2).Bitmap的4种优化策略
    a.对图片质量进行压缩
    bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputstream);//100 代表不压缩
    b.图片按比例进行压缩
    BitmapFactory.Options.inJustDecodeBounds = true(只获取宽高而不分配内存)
    BitmapFactory.Options.inSampleSize(缩放比)
    c.关于bitmap的recycle方法(争议)
    d.捕获异常 OutofMemoryError

    2.三级缓存/LruCache算法
    三级缓存:内存-本地-网络;
    内存缓存:LRU缓存算法,当我们缓存满的时候会优先淘汰那些最近最不经常使用的缓存对象
    内部用LinkedHashMap

    相关文章

      网友评论

          本文标题:Glide源码笔记

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