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

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()方法,再会。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容