Glide 分析-[Android_YangKe]

作者: Android_YangKe | 来源:发表于2017-12-19 17:43 被阅读231次
    yangke.jpeg

    为什么使用Glide?

    知识点普及:
    我们在页面上看到的任何图片资源经过编解码都会转换为Bitmap(位图)由Matrix(矩阵)进行管理。Bitmap即由一个一个的像素点组成的图像,每个像素点的内存占用比决定了一张图片在运行过程会占用多大的内存。其内部有一个Config(枚举类)默认对应Bitmap的四种类型:

    • ALPHA_8:每个像素点占用1个字节
    • RGB_565:每个像素点占用2个字节
    • ARGB_4444:每个像素点占用2个字节
    • ARGB_8888:每个像素点占用4个字节

    做过图像处理的对Matrix都不陌生,在Android中它的尺寸默认是3*3,主要作用是:

    • Translate:图像平移
    • Rotate:图像旋转
    • Scale:图像伸缩
    • Skew:错切变化

    ok,由于这些知识点不是本章重点,这里我们只需了解Bitmap的内存占用比与什么相关就行,此处不在进行拓展。


    Glide

    笔者为了读者有更好的读后感,本文秉承着段段有总结的方式,希望给大家带来一个良好的阅读体验。

    优点:

    1.Glide符合我们的项目需求,支持Gif、Png、Gpg、Vedio等。

    2.Glide的网络任务与Android生命周期绑定。支持任务取消、重新加载操作,资源更加可控。

    3.针对低内存设备Glide可以动态的调整图片的缓存大小,不需要人为干涉。

    4.内部Bitmap格式默认为RGB_565内存占用较低,当然也可手动调整为RGB_888。

    5.是市面上比较主流的框架,具有稳定、易用、疑难点查阅资料方便等特点。例:with(xxx)函数支持Fragment、Activity、Context等。

    接着我们来看下Glide的介绍:

      /**
       * A singleton to present a simple static interface for building requests with {@link BitmapRequestBuilder} and
       * maintaining an {@link Engine}, {@link BitmapPool}, {@link com.bumptech.glide.load.engine.cache.DiskCache} and
       * {@link MemoryCache}. 
       */
    

    BitmapRequestBuilder:用于构建ImageView(及其子类)需要的资源文件,为ImageView设置占位符、动画等。

    Engine:主要用于管理缓存事物。

    BitmapPool:位图池。主要用途是便于资源的重用。类似的还有线程池,常量池。

    DiskCache:磁盘缓存。主要负责对磁盘的读与写。

    MemoryCache:内存缓存。主要负责内存中对象的添加与删除。

    下面我们在来看一小段代码

      public static Glide get(Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                 // 代码省略...
                    glide = builder.createGlide();
                 // 代码省略...
                }
            }
        }
        return glide;
      }
    

    一般运用单例模式(双重检验锁)的地方都意味着其对象使用频繁,构造函数中有过多的初始化工作,真的是这样吗?我们再来研究下createGlide()函数。

    这里注意下代码中的if1、if2... if7,下文要用。

    Glide createGlide() {
        //if1 if (sourceService == null) { 
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        //if2 if (diskCacheService == null) {diskCacheService = new FifoPriorityThreadPoolExecutor(1);} 
        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
        //if3 if (bitmapPool == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);
            } else {bitmapPool = new BitmapPoolAdapter();}
        }
        //if4 if (memoryCache == null) {memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());}
        //if5 if (diskCacheFactory == null) {diskCacheFactory = new InternalCacheDiskCacheFactory(context);}
        //if6 if (engine == null) {engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);}
        //if7 if (decodeFormat == null) {decodeFormat = DecodeFormat.DEFAULT;}
        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }
    

    if1:构建一个固定大小的线程池。容量为1 或者 Java虚拟机可用的处理器数量,用于资源管理。

    if2:构建一个固定大小的线程池。容量为1,用于磁盘缓存。

    if3:构建一个位图池。且在Android3.0(HONEYCOMB API 11)版本进行了优化,使用了LRU算法(Least Recently Used的缩写,即最近最久未使用)。

    if4:构建一个内存缓存对象。主要用途是对内存中的对象进行辅助操作。

    if5:构建一个磁盘缓存工厂对象。主要用途:构建一个磁盘缓存对象,可指定缓存目录。

    if6:构建一个引擎(engine)对象。主要用途:负责缓存,资源的调配。

    if7:构建图片编码格式对象,默认为PREFER_RGB_565,一个像素点占用2个字节。主要用途:调整图片质量。

    MemorySizeCalculator:构建一个缓存辅助对象。主要作用:内部根据一些给定的常量它可以智能的对缓存大小进行控制。当Glide的Api运行在低内存设备上时,其内部自动调整缓存大小。说直白点就是一个判断,这里不再将代码出,有兴趣的同学可以定位MemorySizeCalculator搜索getMaxSize(xxx)函数。

    总结:Glide在构造函数中做了大量的初始化操作,为了避免此对象在构建上的资源开销使用了单例模式。加载图片资源相对而言比较耗时、占用内存。其内部借助了:内存缓存、磁盘缓存、池(BitmapPool)概念、LRU算法、弱引用、针对低内存设备动态调整缓存大小方式进行优化。


    以上是Glide内部的一些核心机制,下面我们看下如何使用。

    Glide.with(context)
         .load(imageUrl)
         .into(imageView);
    

    代码很简单只需要一个context、图片地址,view控件就可以完成图片的展示,是不是很简单呢?说直白点这就是包装后的女人(效果需要女士勿喷)。下面我们就来通过源码进一步解析。

    Glide.with(context)

    此函数返回一个RequestManager对象,是Glide框架中一个特别重要的Class。我们看下RequestManager介绍:

      /**
       * A class for managing and starting requests for Glide. Can use activity, fragment and connectivity lifecycle events to
       * intelligently stop, start, and restart requests. Retrieve either by instantiating a new object, or to take advantage
       * built in Activity and Fragment lifecycle handling, use the static Glide.load methods with your Fragment or Activity.
       */
    

    大致意思是:RequestManager管理着Glide的请求,负责与Activity,Fragment的生命周期进行绑定。

    生命周期绑定好处:当Activity,Fragment调用onStart、onStop函数时,RequestManager可以很聪明的开始、取消我们的网络任务,这也是我们调用Glide.with(xxx)函数时尽量传递Activity或者Fragment的原因之一。

    下面我们看几个Class:
    Lifecycle:一个用于监听Activity、Fragment生命周期事件的接口。

    RequestManagerTreeNode:我们可以通过此对象获取RequestManager对象,可以理解为一个辅助类。

    RequestTracker:我们可以通过此类对我们的请求任务进行跟踪。例:图片加载成功、失败、完成、重新加载等。

    ConnectivityMonitor:用于监听网络的连接状态。

    ConnectivityMonitorFactory:一个工厂类内部只有一个build函数用于构建ConnectivityMonitor对象。

    大致了解了RequestManager的重要性,我们再看下它的构造函数主要做了什么操作。

    RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
    this.context = context.getApplicationContext(); //this.xxx = xxx 省略部分代码...
    this.glide = Glide.get(context);
    this.optionsApplier = new OptionsApplier();
    ConnectivityMonitor connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker));
    // 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 { lifecycle.addListener(this); }
        lifecycle.addListener(connectivityMonitor);
    }
    

    其中this.xxx = xxx负责将外界传递过来的对象交给RequestManager进行管理。lifecycle.addListener(xxx)负责与Activity、Fragment的生命周期进行绑定、网络监听。众所周知四大组件的生命周期都是运行在主线程中的,Lifecycle对象与Activity、Fragment的生命周期进行了绑定监听所以lifecycle.addListener(xxx)函数运行在主线程中。

    知识点延伸:public void run() { }为什么运行在主线?原理:Looper.getMainLooper()可以获取主线程的Looper对象,通过post的形式将消息传送到主线程的消息队列(Handler机制)。

    通过分析我们了解到RequestManager构造函数中主要操作是对应用生命周期进行绑定,我们继续跟进。

    @Override public void onStart() {resumeRequests();}
    @Override public void onStop() {pauseRequests();}
    @Override public void onDestroy() {requestTracker.clearRequests();}
    /** Starts any not yet completed or failed requests.*/
    public void resumeRequests() {
        isPaused = false;
        for (Request request : Util.getSnapshot(requests)) {
            if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) {
                request.begin();
            }
        }
        pendingRequests.clear();
    }// 省略部分代码...
    

    这里我们以onStart函数为模板进行解析,其他如:onStop、onDestroy原理类似,不再进行讲解。

    resumeRequests():主要负责载入未完成或者失败的请求,这里我们只需重点关注pendingRequests。pendingRequests用于存放未完成或失败的网络任务,便于后续恢复请求(其实就是一个ArrayList)。例:我们的应用从后台切换到前台,重新加载未完成的图片。

    总结:这部分相对来说比较简单,请求任务进度由RequestTracker(请求跟踪器)进行管理,其内部请求队列借助ArrayList维护。当应用的onStart函数被调用时pendingRequests清空任务请求、onDestroy函数被调用时pendingRequests清空任务请求、onStop函数被调用时添加任务请求。

    注意:当onStop函数调用时,RequestTracker负责将网络任务暂停,同时将未完成网络任务加入pendingRequests待下次重新开始任务。而onDestroy函数则是释放所有系统资源。

    通过本篇文章相信你对Glide的整体轮廓有了一个大致了解。结束~

    喜欢有帮助的话: 双击、评论、转发,动一动你的小手让更多的人知道!关注 帅比-杨

    相关文章

      网友评论

        本文标题:Glide 分析-[Android_YangKe]

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