美文网首页Android开发经验谈Android开发
〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(一)

〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(一)

作者: 两行哥 | 来源:发表于2018-04-23 13:26 被阅读124次

    Glide的简介就不多说了,只要记住这是Google推荐AndroidDev使用的图片开源框架就好了。直接进入正题,看看Glide的设计到底有何精妙之处,为何获得了Google的青睐?

    一个最简单的Glide图片加载逻辑举例:

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

    我们的分析会以Glide3.8.0版本为例,基于此行代码展开,分别分析with()、load()、into()三个方法内部的逻辑。

    一、基础概念

    Glide中涉及几个概念,为便于理解,先粗略解释:

    1.Model

    指的是对图片源的封装。在AndroidDev中,比较常见的就是网络图片地址、本地文件或资源ID。比如,String url = "https://www.baidu.com/img/bd_logo1.png",这里的url经过Glide内部的封装后,就可以理解为一个Model。

    2.Data

    指的是对Model处理后的数据源的封装,通常是InputStream。而在Glide中,将Model处理为Data的类便是ModelLoader。比如,通过上文的"https://www.baidu.com/img/bd_logo1.png"而取得的图片输入流,就可以理解为Data。

    3.Resource

    拿到图片的输入流可以直接展示在UI上吗?显然不可以。在第2点中我们提及了Data,即输入流,如果要将其展示就需要对Data进行解码,解码后的数据就是Resource。比如,上文的图片输入流经过解码后成为一个Bitmap对象或Drawable对象,这个对象就可以理解为Resource。而担任解码任务的角色,被成为ResourceDecoder(资源解码器)。

    4.TransformedResource&TranscodedResource

    有时候我们获取到的Resource并不适合展示,而是需要经过处理才能展示,比如需要裁剪变换等(如调用centerCrop()、fitCenter()),那么经过如此变换后的Rescourse称为TransformedResource。
    我们知道Glide是可以展示静态图,也可以展示动态图(动态Gif),而解码后的静态图片和动态图片(例如drawable和gif-drawable)类型是不同的, 为了统一处理逻辑,Glide内部将这两种类型的对象再次封装为统一的GlideBitmapDrawable,这里的GlideBitmapDrawable就可以理解为TranscodedResource。

    5.Target

    这个比较容易理解,即需要在哪个目标上进行展示,比如ImageView。而Glide内部将ImageView再次进行了封装,封装后对象就可以理解为Target。

    综上所述,一个完整的图片处理及展示流程如下:


    图1 Glide流程图

    二、基本用法

    直接看代码和注释:

    Glide.with(context)
            .load(url)
            .placeholder(R.mipmap.ic_launcher)//图片加载前的占位图
            .error(R.mipmap.ic_launcher)//图片加载错误的占位图
            .fitCenter()
            .centerCrop()
            .override(500, 500)//调整图片大小
            .skipMemoryCache(true)//跳过内存缓存
            .crossFade(1000)//渐变显示时间
            .diskCacheStrategy(DiskCacheStrategy.RESULT)//缓存处理后的图像(如尺寸调整、裁剪后的图像)
            .diskCacheStrategy(DiskCacheStrategy.SOURCE)//缓存原尺寸的图像
            .diskCacheStrategy(DiskCacheStrategy.ALL)//缓存所有图像
            .diskCacheStrategy(DiskCacheStrategy.NONE)//跳过磁盘缓存
            .priority(Priority.HIGH)//指定优先级
            .into(imageView);
    

    三、with()源码逻辑

    跟进Glide类中寻找with()方法,发现with()方法有多种重载,包括with(Context context)、with(Activity activity)、with(Fragment fragment)等等。Glide之所以设计如此之多的with()方法重载,其目的在于将图片的加载与传入的Context组件或Fragment的生命周期相关联。比如,当用户关闭当前Activity,那么即使该Activity有正在进行中的图片网络请求,Glide也会随之取消该网络请求。如果传入的是ApplicationContext,那么网络请求会持续进行,直至App进程被销毁。
    这里以with(Context context)为例看一下源码实现,其他重载方法逻辑基本一致:

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

    RequestManager类是Glide中用来处理图片加载请求的管理类。RequestManagerRetriever类是用来创建和管理RequestManager对象的,其get()方法获取了RequestManagerRetriever实例。继续追踪源码,查看RequestManagerRetriever类中get()方法的逻辑。

    RequestManagerRetriever.java
        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);
        }
    

    在get()方法中,对Context的类型进行了区分,有多种重载。我们主要分析一下Application和Activity类型的方法参数,其他类型的参数读者可以自行追踪源码。

    RequestManagerRetriever.java
        private RequestManager getApplicationManager(Context context) {
            if (applicationManager == null) {
                synchronized (this) {
                    if (applicationManager == null) {
                        applicationManager = new RequestManager(context.getApplicationContext(),
                                new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                    }
                }
            }
            return applicationManager;
        }
    

    上述get(Application context)的方法中采取了双重锁单例模式获取了一个RequestManager对象。可以发现,Glide在RequestManager的构造方法中传入了ApplicationLifecycle的实例,其作用是将RequestManager与Application生命周期绑定。

        @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        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);
            }
        }
    

    上述get(Activity context)的方法中,首先进行了线程判断,如果在非主线程的情况下,返回了get(Application context)类型的RequestManager,而在主线程的情况下,通过该Activity context获取到了相关联的FragmentManager。这是Glide设计精妙之处之一,为什么要获取FragmentManger呢?继续追踪源码到fragmentGet(activity,fm)中。

    RequestManagerRetriever.java
        @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
            RequestManagerFragment current = getRequestManagerFragment(fm);
            RequestManager requestManager = current.getRequestManager();
            if (requestManager == null) {
                requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
                current.setRequestManager(requestManager);
            }
            return requestManager;
        }
    

    分析一下fragmentGet()的逻辑。首先,方法第一行getRequestManagerFragment()返回了RequestManagerFragment的实例current。RequestManagerFragment是啥?其实这是Glide封装的Fragment,这里当作Fragment来理解。先来截取部分RequestManagerFragment类的代码:

    RequestManagerFragment.java
    public class RequestManagerFragment extends Fragment {
    
        ......
        private final ActivityFragmentLifecycle lifecycle;
        private RequestManager requestManager;
    
        public RequestManagerFragment() {
            this(new ActivityFragmentLifecycle());
        }
    
        /**
         * Sets the current {@link com.bumptech.glide.RequestManager}.
         *
         * @param requestManager The request manager to use.
         */
        public void setRequestManager(RequestManager requestManager) {
            this.requestManager = requestManager;
        }
    
        ActivityFragmentLifecycle getLifecycle() {
            return lifecycle;
        }
    
        /**
         * Returns the current {@link com.bumptech.glide.RequestManager} or null if none exists.
         */
        public RequestManager getRequestManager() {
            return requestManager;
        }
    
        @Override
        public void onStart() {
            super.onStart();
            lifecycle.onStart();
        }
    
        @Override
        public void onStop() {
            super.onStop();
            lifecycle.onStop();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            lifecycle.onDestroy();
        }
    
    }
    

    对RequestManagerFragment划重点:
    1.含有一个RequestManager类型的成员变量requestManager,通过getRequestManager()返回,通过setRequestManager()赋值。

    即每个RequestManagerFragment对象都与一个RequestManager对象绑定。

    2.含有一个ActivityFragmentLifecycle类型的成员变量lifecycle。通过对lifecycle实现的LifecycleListener接口回调来监听其生命周期,可以发现在RequestManagerFragment类的onStart()、onStop()、onDestroy()方法中对其进行了调用。

    即lifecycle对象实现了对RequestManagerFragment实例的生命周期监听。

    回到fragmentGet()方法中,调用RequestManagerFragment中的getRequestManager()方法返回与其绑定的RequestManager对象(成员变量),如果为null(没有与其绑定的RequestManager对象),则创建一个RequestManager对象,并调用setRequestManager()将其与RequestManagerFragment绑定。至此实现了RequestManagerFragment对象与RequestManager对象的一一绑定。
    现在思考一个问题,Glide如何监听目标Activity或Fragment的生命周期?上文已经说明,根据传入Glide的Context不同,Glide会监听其生命周期,根据生命周期管理图片的网络请求。
    Glide直接监听Activity并不方便。那么在这里,Glide采用了另外的方法:
    1.在Activity或Fragment的上层添再加一个Fragment,也就是上文一直在分析的RequestManagerFragment。这个Fragment并没有覆写createFragmentView(),即这是一个无UI的Fragment,当添加了该Fragment,用户是无法感知到的。
    2.我们知道依附于Activity的Fragment有与被依附的Activity相关联的生命周期,监听Fragment的生命周期也就是获取到了被依附的Activity的生命周期。上文已经划过重点:RequestManagerFragment通过接口回调的方式,在onStart()、onStop()、onDestroy()中调用了ActivityFragmentLifecycle的相应生命周期方法,实现了对其生命周期的监听。
    3.如果传入Glide.with()参数为Fragment,那么处理逻辑同上。大家应该也在Fragment中添加过Fragment吧?Glide同样监听了RequestManagerFragment的生命周期。
    4.每个RequestManagerFragment对象又与RequestManager对象一一绑定。至此,所有疑问解开:

    Glide的每个RequestManager想要监听目标Activity或Fragment生命周期用于执行相关操作:
    ---> 每个RequestManager关联着唯一的RequestManagerFragment
    ---> 该RequestManagerFragment是无UI的,被添加在目标Activity或Fragment上, 而RequestManagerFragment的生命周期又与目标Activity或Fragment相关联
    ---> RequestManagerFragment通过接口回调的方式,在onStart()、onStop()、onDestroy()中调用了ActivityFragmentLifecycle的相应生命周期方法,实现了生命周期的监听
    综上,RequestManager通过监听RequestManagerFragment这个不可见的Fragment的生命周期,间接监听了目标Activity或Fragment。

    这次的Glide源码分析就到这里,主要分析了Glide内部的处理流程以及with()方法中所做的操作,建议自行参阅相关源码的同时阅读本文。下一篇我们将继续分析Glide的load()方法,再会。

    相关文章

      网友评论

        本文标题:〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(一)

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