Glide 使用大全及源码解析

目录

前言

  • 图片加载是Android开发中最最基础的功能,同时图片加载OOM也一直困扰着很多开发者,因此为了降低开发周期和难度,我们经常会选用一些图片加载的开源库。

  • 主流的有,Picasso,Glide,Fresco等等,选择一款好的图片加载裤就成了我们的首要问题。

  • 接下来我们对比一下主流的三款 Picasso,Glide,Fresco框架的优缺点。

和其他库对比

对比项 Picasso Glide Fresco
发布时间 2013年5月 2014年9月 2015年5月
是否支持gif false true true
是否支持webP(新的图片格式,加载速度快) true true true
视频缩略图 false true true
大小 100k 500 KB 2~3M
加载速度
Disk(磁盘缓存)+Men Cache(分布式缓存) true true true
使用难易度 low mediun difficult
开发者 Square主导 Google主导 Facebook主导

Glide 使用

导入

在AndroidStudio上添加依赖非常简单

dependencies {  
    compile 'com.github.bumptech.glide:glide:3.7.0'  
    compile 'com.android.support:support-v4:23.2.1'  
}  

使用

基本方法

Glide的一个完整的请求至少需要三个参数,代码如下:


String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/7858995348613407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
    .load(url)
    .into(imageView);

注意: with(Context context) - 需要上下文,这里还可以使用 Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 的对象。将 Activity/Fragment 对象作为参数的好处是,图片的加载会和 Activity/Fragment 的生命周期保持一致,例如:onPaused 时暂停加载,onResume 时又会自动重新加载。所以在传参的时候建议使用 Activity/Fragment 对象,而不是 Context。

占位图设置

偶尔出现图片加载慢或者加载不出来的情况是难以避免的,所以为了 UI 能好看一些,我们会使用占位图。Glide 也为我们提供这种方法 placeHolder() 和 error()


String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/7858995348613407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
    .load(url)
    .placeholder(R.drawable.place_image)//图片加载出来前,显示的图片
    .error(R.drawable.error_image)//图片加载失败后,显示的图片
    .into(imageView);

注:这里需要注意一点,placeholder() 和 error() 的参数都是只支持 int 和 Drawable 类型的参数,这种设计应该是考虑到使用本地图片比网络图片更加合适做占位图。

动画设置

动画效果可以让图片加载变得更加的平滑,crossFade() 方法强制开启 Glide 默认的图片淡出淡入动画,当前版本3.7.0是默认开启的。crossFade() 还有一个重载方法 crossFade(int duration)。可以控制动画的持续时间,单位ms。动画默认的持续时间是300ms。既然可以添加动画,那肯定就可以设置没有任何淡出淡入效果,调用 dontAnimate()

Glide.with(context)
    .load(url)
    .crossFade()//或者使用 dontAnimate() 关闭动画
    .placeholder(R.drawable.place_image)
    .error(R.drawable.error_image)
    .into(imageView);

注意:Glide 是可以自定义动画效果的,这个在下面做讲解

图片大小与裁剪

在项目开发过程中,指定图片显示大小长长可能用到,毕竟从服务器获取的图片不一定都是符合设计图的标准的。我们在这里就可以使用 override(width,height) 方法,在图片显示到 ImageView 之前,重新改变图片大小。

Glide.with(context)
    .load(url)
    .override(width,height)//这里的单位是px
    .into(imageView);

注意: 在设置图片到 ImageView 的时候,为了避免图片被挤压失真,ImageView 本身提供了 ScaleType 属性,这个属性可以控制图片显示时的方式,具体的属性使用还是去搜索吧!Glide 也提供了两个类似的方法 CenterCrop() 和 FitCenter(),CenterCrop() 方法是将图片按比例缩放到足矣填充 ImageView 的尺寸,但是图片可能会显示不完整;而 FitCenter() 则是图片缩放到小于等于 ImageView 的尺寸,这样图片是显示完整了,但是 ImageView 就可能不会填满了。

图片的缓存处理

为了更快的加载图片,我们肯定希望可以直接拿到图片,而不是进行网络请求,所以我们需要缓存。Glide 通过使用默认的内存和磁盘缓存来避免不必要的网络请求

内存缓存

内存缓存是 Glide 默认帮我们做了的,除非你不需要,可以调用 skipMemoryCache(true) 告诉 Glide 跳过内存缓存。这样 Glide 就不会把这张图片放到内存缓存中,该方法只影响内存缓存。

磁盘缓存

磁盘缓存也是默认开启的,当然也是可以关闭的,不过关闭的方式略微有点不一样。

Glide.with(context)
    .load(url)
    .skipMemoryCache(true)
    .diskCacheStrategy( DiskCacheStrategy.NONE )
    .into(imageView);

自定义磁盘缓存行为

使用 DiskCacheStrategy 可以为 Glide 配置磁盘缓存行为。Glide 的磁盘缓存比较复杂,这也是在图片加载可以比 Picasso 的原因(之一)。Picasso 只缓存了全尺寸的图片,而 Glide 的不同之处在于,Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。

DiskCacheStrategy 的枚举意义:

DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认行为)
这只是举个例子而已

Glide.with(context)
    .load(url)
    .diskCacheStrategy( DiskCacheStrategy.SOURCE )
    .into(imageView);

图片请求的优先级

图片请求的优先级
同一时间加载多个图片,App 将难以避免这种情况。如果这个时候我们希望用户的体验更好,往往会选择先加载对于用户更加重要的图片。Glide 可以调用 .priority() 方法配合 Priority 枚举来设置图片加载的优先级。

//设置 HIGH 优先级
Glide.with( context )
    .load( highPriorityImageUrl )
    .priority (Priority.HIGH )
    .into( imageView );
//设置 LOW 优先级
Glide.with( context )
    .load( lowPriorityImageUrl )
    .priority( Priority.LOW )
    .into( imageView );

Priority.LOW
Priority.NORMAL
Priority.HIGH
Priority.IMMEDIAT

这里有一点需要注意,优先级并不是完全严格遵守的。Glide 将会用他们作为一个准则,尽可能的处理这些请求,但是不能保证所有的图片都会按照所有要求的顺序加载。

显示 Gif 和 Video

显示 GIf 对于 Glide 来说一个比较特别的功能(至少 Picasso 暂时还不行)而且使用起来非常简单

String gifUrl = "http://i2.mhimg.com/M00/0E/AE/CgAAilTPWJ2Aa_EIACcMxiZi5xE299.gif";
Glide.with( context )
    .load( gifUrl )
    .placeholder( R.drawable.default.error( R.drawable.error )
    .into( imageView );

这段代码还有点问题,如果加载的不是一张 gif 图的话,是没有办法显示的。

Glide.with( context )
    .load( gifUrl )
    .asGif()
    .error( R.drawable.error )
    .into( imageView );

做以上修改,如果图片类型不是 Gif 图的话就会当作 load 失败来处理,因此 error() 会被回调。即使这个url的图片是好的,也是不会显示的。当然,如果你想显示 Gif 但只是向现实静态的图片你就可以这么做

Glide.with( context )
    .load( gifUrl )
    .asBitmap()
    .error( R.drawable.error )
    .into( imageView );

仅仅是显示 Gif 的第一帧图像,这样就可以保证图片的正常显示了。

还有一个神奇的功能,Glide 还能显示视频!但是只能够显示手机本地的视频,要是想实现网络上的视频的话,还是用其他方法吧!

String filePath = "/storrage/emulated/0/Pictures/video.mp4";
Glide.with( context )
    .load( Uri.fromFile( new File( filePath ) ) )
    .into( imageView );
   ``` 




## 进阶使用

### Target

到现在为止,我们所涉及到的代码都是直接加载图片到 ImageView 中。Glide 隐藏做了所有的网络请求和后台的线程处理,图片准备好之后切回到 UI 线程刷新 ImageView。也就是说 ImageView 在我们代码的链式结构中成为了最后一步,但是如果我们需要获取到 Bitmap 本身
的话我们就需要用到 Target 了。Target 其实就是整个图片的加载的生命周期,所以我们就可以通过它在图片加载完成之后获取到 Bitmap。


>其实对于 Target 可以简单的理解为回调,本身就是一个 interface,Glide本身也为我们提供了很多 Target

#### SimpleTarget

private SimpleTarget mSimpleTarget = new SimpleTarget() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation


首先创建了一个 SimpleTarget 的对象并且实现了 onResourceReady() 方法,看方法名能知道是图片加载完之后会调用该方法,参数就有我们需要的 Bitmap 。而使用 SimpleTarget 的对象的时候就像使用 ImageView 一样,作为参数传给 into() 方法就行了,Glide 会内部去处理并返回结果给任何一个对象。这里我们为了防止加载 Gif 、 Video 或者一些位置资源时与 mSimpleTarget 冲突,所以我们调用了 asBitmap() 方法,使其只能返回 Bitmap 对象。

这里就有个问题了,如果我需要改变图片的大小怎么办?这点小问题 Glide 还是有考虑到的,加入原尺寸 1000x1000 的图片,我们显示的时候只需要是 500x500 的尺寸来节省时间和内存,你可以在 SimpleTarget 的回调声明中指定图片的大小。

private SimpleTarget mSimpleTarget = new SimpleTarget(500,500) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation


从代码中可以看到 SimpleTarget 的对象的声明没有使用匿名对象,而是单独的声明了一个变量,这里是故意这么做的,如果使用匿名内部类的方式创建 SimpleTarget 的对象,这样会增大该对象在 Glide 完成图片请求之前就被回收的可能性。

还记得前面说过 with() 方法传入 Activity 或者 Fragment 时 Glide 的图片加载会与他们的生命周期关联起来,但是如果我们使用 Target 的话,这个 Target 就有可能独立于他们的生命周期以外,这时候我们就需要使用 context.getApplicationContext() 的上下文了,这样只有在应用完全停止时 Glide 才会杀死这个图片请求。代码如下

Glide.with(mContext.getApplicationContext())
.load(mUrl)
.asBitmap()
.into(target);

#### ViewTarget

当我们使用 Custom View 时,Glide 并不支持加载图片到自定义 view 中的,使用 ViewTarget 更容易实现。

public class CustomView extends FrameLayout {
private ImageView mImageView;

public CustomView(Context context) {
    super(context);
}

public CustomView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    mImageView = new ImageView(getContext());
    addView(mImageView , LayoutParams.MATCH_PARENT , LayoutParams.MATCH_PARENT);
}

public void setImage(Drawable drawable){
    mImageView.setImageDrawable(drawable);
}

}

上面这个例子就没有办法直接使用 .into() ,如果我们使用 ViewTarget 实现呢!

public void loadImageTarget(Context context){
CustomView mCustomView = (CustomView) findViewById(R.id.custom_view);

ViewTarget viewTarget = new ViewTarget<CustomView,GlideDrawable>( mCustomView ) {
    @Override
    public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
        this.view.setImage(resource);
    }
};

Glide.with(context)
        .load(mUrl)
        .into(viewTarget);

}


#### Transformations

图片显示之前我们可能还需要对图片进行处理操作,比如:图片切圆角,灰阶处理等等;这些需求我们通过 Transformations 操作 bitmap 来实现,我们可以修改图片的任意属性:尺寸,范围,颜色,像素位置等等。

接下来就要讲讲怎么样来实现自己的 Transformation 

- 圆角照片 GlideRoundTransform

/**
* 图片圆角
*/
public class GlideRoundTransform extends BitmapTransformation {

private static float radius = 0f;

public GlideRoundTransform(Context context) {
    this(context, 4);
}

public GlideRoundTransform(Context context, int dp) {
    super(context);
    this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
}

@Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
    return roundCrop(pool, toTransform);
}

private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
    if (source == null) return null;

    Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
    if (result == null) {
        result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(result);
    Paint paint = new Paint();
    paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
    paint.setAntiAlias(true);
    RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
    canvas.drawRoundRect(rectF, radius, radius, paint);
    return result;
}

// public

@Override
public String getId() {
    return getClass().getName() + Math.round(radius);
}
加载圆角图片

Glide.with(mContext)
.load(item.getImage())
.transform(new GlideRoundTransform(mContext,5)) 5是圆角半径
.into((ImageView) helper.getView(R.id.iv));


- GlideCircleTransform 圆形图片

public class GlideCircleTransform extends BitmapTransformation {
public GlideCircleTransform(Context context) {
super(context);
}

@Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
    return circleCrop(pool, toTransform);
}

private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
    if (source == null) return null;

    int size = Math.min(source.getWidth(), source.getHeight());
    int x = (source.getWidth() - size) / 2;
    int y = (source.getHeight() - size) / 2;

    // TODO this could be acquired from the pool too
    Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);

    Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
    if (result == null) {
        result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(result);
    Paint paint = new Paint();
    paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
    paint.setAntiAlias(true);
    float r = size / 2f;
    canvas.drawCircle(r, r, r, paint);
    return result;
}

@Override public String getId() {
    return getClass().getName();
}

}


加载圆形图片
会自动根据imageview大小填充

Glide.with(mContext).
load(item.getHeadpicUrl())
.transform(new GlideCircleTransform
(mContext)).into((ImageView) helper.getView(R.id.iv_head));




Animate 

从图像到图像的平滑过渡是非常重要,Glide 中有一个标准动画去柔软的在你的 UI 中改变,但是我们现在希望设置自己的动画。

这是个 XML 动画缩放动画,图片刚开始小的,然后逐渐增大到原尺寸。我们现在要应用到 Glide 加载图片中去,调用 .animate() 方法传入 XML 动画的 id 即可。

Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(this , 20))
.animate( R.anim.zoom_in )
.into(mImageView);


这种加载方式用在常规的 ImageView 上是没有问题的,但如果使用的 Target 是一些自定义的时候就没法好好的实现了。这时候我们就可以通过传入实现了 ViewPropertyAnimation.Animator 接口的类对象来实现

ViewPropertyAnimation.Animator animator = new ViewPropertyAnimation.Animator() {
@Override
public void animate(View view) {
view.setAlpha( 0f );

    ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
    fadeAnim.setDuration( 2500 );
    fadeAnim.start();
}

};


然后,我们只需要在 Glide 请求中设置这个动画对象就ok

Glide.with(context)
.load(mUrl)
.animate( animator )
.into(viewTarget);


#### 增加 Glide 的图片质量

在 Android 中有两个主要的方法对图片进行解码:ARGB_8888 和 RGB_565 。前者为每个像素使用4个字节,后者每个像素仅使用2个字节。ARGB_8888 的有时就是图像质量更高以及能储存一个 alpha 通道。 Picasso 使用的就是 ARGB_8888 , Glide 默认使用低质量的 RGB_565 ,但是现在你就可以使用 Glide module 来改变图片解码规则。就象这样

- 定义moudle
 ```
public class QualityModule implements GlideModule{
    @Override
    public void applyOptions(Context context , GlideBuilder builder){
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
    }

    @Override
    public void registerComponents(Context context , Glide glide){
        // nothing to do here
    }
}

  • AndroidManifest.xml 中使用 meta 声明
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mrtrying.demoglide">

    <application>

        <meta-data
            android:name="com.mrtrying.demoglide.module.ExampleModule"
            android:value="GlideModule" />

        ...

    </application>

    ...

</manifest>

到这里我们就完成了Module 的声明,Glide 将会在工作是使用我们所定义的 Module 去做加载行为

Glide 源码分析

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

到底是怎么通过这一句话将图片显示上去的

with()

with()方法是Glide类中的一组静态方法,它有好几个方法重载,我们来看一下Glide类中所有with()方法的方法重载:

public class Glide {

    ...

    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);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static RequestManager with(android.app.Fragment fragment) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(fragment);
    }

    public static RequestManager with(Fragment fragment) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(fragment);
    }
}

可以看到,with()方法的重载种类非常多,既可以传入Activity,也可以传入Fragment或者是Context。每一个with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,没什么需要解释的。然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。

而RequestManagerRetriever的实例get()方法中的逻辑是什么样的呢?我们一起来看一看:

public class RequestManagerRetriever implements Handler.Callback {

    private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();

    private volatile RequestManager applicationManager;

    ...

    /**
     * Retrieves and returns the RequestManagerRetriever singleton.
     */
    public static RequestManagerRetriever get() {
        return INSTANCE;
    }

    private RequestManager getApplicationManager(Context context) {
        // Either an application context or we're on a background thread.
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    // Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
                    // However, in this case since the manager attached to the application will not receive lifecycle
                    // events, we must force the manager to start resumed using ApplicationLifecycle.
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }
        return applicationManager;
    }

    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);
    }

    public RequestManager get(FragmentActivity activity) {
        if (Util.isOnBackgroundThread()) {
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            FragmentManager fm = activity.getSupportFragmentManager();
            return supportFragmentGet(activity, fm);
        }
    }

    public RequestManager get(Fragment fragment) {
        if (fragment.getActivity() == null) {
            throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
        }
        if (Util.isOnBackgroundThread()) {
            return get(fragment.getActivity().getApplicationContext());
        } else {
            FragmentManager fm = fragment.getChildFragmentManager();
            return supportFragmentGet(fragment.getActivity(), fm);
        }
    }

    @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);
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private static void assertNotDestroyed(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
            throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public RequestManager get(android.app.Fragment fragment) {
        if (fragment.getActivity() == null) {
            throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
        }
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return get(fragment.getActivity().getApplicationContext());
        } else {
            android.app.FragmentManager fm = fragment.getChildFragmentManager();
            return fragmentGet(fragment.getActivity(), fm);
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
            current = pendingRequestManagerFragments.get(fm);
            if (current == null) {
                current = new RequestManagerFragment();
                pendingRequestManagerFragments.put(fm, current);
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
            }
        }
        return current;
    }

    @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;
    }

    SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
        SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
            current = pendingSupportRequestManagerFragments.get(fm);
            if (current == null) {
                current = new SupportRequestManagerFragment();
                pendingSupportRequestManagerFragments.put(fm, current);
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
            }
        }
        return current;
    }

    RequestManager supportFragmentGet(Context context, FragmentManager fm) {
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

    ...
}

上述代码虽然看上去逻辑有点复杂,但是将它们梳理清楚后还是很简单的。RequestManagerRetriever类中看似有很多个get()方法的重载,什么Context参数,Activity参数,Fragment参数等等,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数。

我们先来看传入Application参数的情况。如果在Glide.with()方法中传入的是一个Application对象,那么这里就会调用带有Context参数的get()方法重载,然后会在第44行调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。

接下来我们看传入非Application参数的情况。不管你在Glide.with()方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。具体添加的逻辑是在上述代码的第117行和第141行,分别对应的app包和v4包下的两种Fragment的情况。那么这里为什么要添加一个隐藏的Fragment呢?因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。

这里额外再提一句,从第48行代码可以看出,如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。不过其实这就属于是在分析代码的细节了,本篇文章我们将会把目光主要放在Glide的主线工作流程上面,后面不会过多去分析这些细节方面的内容。

总体来说,第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已,然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。不过复杂的逻辑还在后面等着我们呢,接下来我们开始分析第二步,load()方法。

load()

由于with()方法返回的是一个RequestManager对象,那么很容易就能想到,load()方法是在RequestManager类当中的,所以说我们首先要看的就是RequestManager这个类。不过在上一篇文章中我们学过,Glide是支持图片URL字符串、图片本地路径等等加载形式的,因此RequestManager中也有很多个load()方法的重载。但是这里我们不可能把每个load()方法的重载都看一遍,因此我们就只选其中一个加载图片URL字符串的load()方法来进行研究吧。

RequestManager类的简化代码如下所示:

public class RequestManager implements LifecycleListener {

    ...

    /**
     * Returns a request builder to load the given {@link String}.
     * signature.
     *
     * @see #fromString()
     * @see #load(Object)
     *
     * @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
     */
    public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }

    /**
     * Returns a request builder that loads data from {@link String}s using an empty signature.
     *
     * <p>
     *     Note - this method caches data using only the given String as the cache key. If the data is a Uri outside of
     *     your control, or you otherwise expect the data represented by the given String to change without the String
     *     identifier changing, Consider using
     *     {@link GenericRequestBuilder#signature(Key)} to mixin a signature
     *     you create that identifies the data currently at the given String that will invalidate the cache if that data
     *     changes. Alternatively, using {@link DiskCacheStrategy#NONE} and/or
     *     {@link DrawableRequestBuilder#skipMemoryCache(boolean)} may be appropriate.
     * </p>
     *
     * @see #from(Class)
     * @see #load(String)
     */
    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));
    }

    ...

}

RequestManager类的代码是非常多的,但是经过我这样简化之后,看上去就比较清爽了。在我们只探究加载图片URL字符串这一个load()方法的情况下,那么比较重要的方法就只剩下上述代码中的这三个方法。

那么我们先来看load()方法,这个方法中的逻辑是非常简单的,只有一行代码,就是先调用了fromString()方法,再调用load()方法,然后把传入的图片URL地址传进去。而fromString()方法也极为简单,就是调用了loadGeneric()方法,并且指定参数为String.class,因为load()方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在loadGeneric()方法中进行的了。

其实loadGeneric()方法也没几行代码,这里分别调用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象。ModelLoader对象是用于加载图片的,而我们给load()方法传入不同类型的参数,这里也会得到不同的ModelLoader对象。不过buildStreamModelLoader()方法内部的逻辑还是蛮复杂的,这里就不展开介绍了,要不然篇幅实在收不住,感兴趣的话你可以自己研究。由于我们刚才传入的参数是String.class,因此最终得到的是StreamStringLoader对象,它是实现了ModelLoader接口的。

最后我们可以看到,loadGeneric()方法是要返回一个DrawableTypeRequest对象的,因此在loadGeneric()方法的最后又去new了一个DrawableTypeRequest对象,然后把刚才获得的ModelLoader对象,还有一大堆杂七杂八的东西都传了进去。具体每个参数的含义和作用就不解释了,我们只看主线流程。

那么这个DrawableTypeRequest的作用是什么呢?我们来看下它的源码,如下所示:

public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
    private final ModelLoader<ModelType, InputStream> streamModelLoader;
    private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
    private final RequestManager.OptionsApplier optionsApplier;

    private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
            ModelLoader<A, InputStream> streamModelLoader,
            ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
            Class<R> transcodedClass,
            ResourceTranscoder<Z, R> transcoder) {
        if (streamModelLoader == null && fileDescriptorModelLoader == null) {
            return null;
        }

        if (transcoder == null) {
            transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
        }
        DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
                resourceClass);
        ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
                fileDescriptorModelLoader);
        return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
    }

    DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
            ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
            RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
        super(context, modelClass,
                buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
                        GlideDrawable.class, null),
                glide, requestTracker, lifecycle);
        this.streamModelLoader = streamModelLoader;
        this.fileDescriptorModelLoader = fileDescriptorModelLoader;
        this.optionsApplier = optionsApplier;
    }

    /**
     * Attempts to always load the resource as a {@link android.graphics.Bitmap}, even if it could actually be animated.
     *
     * @return A new request builder for loading a {@link android.graphics.Bitmap}
     */
    public BitmapTypeRequest<ModelType> asBitmap() {
        return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
                fileDescriptorModelLoader, optionsApplier));
    }

    /**
     * Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
     * <p>
     *     If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
     *     represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
     *     just an {@link DrawableTypeRequest} is sufficient because it will determine whether or
     *     not the given data represents an animated GIF and return the appropriate animated or not animated
     *     {@link android.graphics.drawable.Drawable} automatically.
     * </p>
     *
     * @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
     */
    public GifTypeRequest<ModelType> asGif() {
        return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
    }

    ...
}

这个类中的代码本身就不多,我只是稍微做了一点简化。可以看到,最主要的就是它提供了asBitmap()和asGif()这两个方法。这两个方法我们在上一篇文章当中都是学过的,分别是用于强制指定加载静态图片和动态图片。而从源码中可以看出,它们分别又创建了一个BitmapTypeRequest和GifTypeRequest,如果没有进行强制指定的话,那默认就是使用DrawableTypeRequest。

好的,那么我们再回到RequestManager的load()方法中。刚才已经分析过了,fromString()方法会返回一个DrawableTypeRequest对象,接下来会调用这个对象的load()方法,把图片的URL地址传进去。但是我们刚才看到了,DrawableTypeRequest中并没有load()方法,那么很容易就能猜想到,load()方法是在父类当中的。

DrawableTypeRequest的父类是DrawableRequestBuilder,我们来看下这个类的源码:

public class DrawableRequestBuilder<ModelType>
        extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
        implements BitmapOptions, DrawableOptions {

    DrawableRequestBuilder(Context context, Class<ModelType> modelClass,
            LoadProvider<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> loadProvider, Glide glide,
            RequestTracker requestTracker, Lifecycle lifecycle) {
        super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
        // Default to animating.
        crossFade();
    }

    public DrawableRequestBuilder<ModelType> thumbnail(
            DrawableRequestBuilder<?> thumbnailRequest) {
        super.thumbnail(thumbnailRequest);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> thumbnail(
            GenericRequestBuilder<?, ?, ?, GlideDrawable> thumbnailRequest) {
        super.thumbnail(thumbnailRequest);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
        super.thumbnail(sizeMultiplier);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
        super.sizeMultiplier(sizeMultiplier);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> decoder(ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
        super.decoder(decoder);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
        super.cacheDecoder(cacheDecoder);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder<GifBitmapWrapper> encoder) {
        super.encoder(encoder);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> priority(Priority priority) {
        super.priority(priority);
        return this;
    }

    public DrawableRequestBuilder<ModelType> transform(BitmapTransformation... transformations) {
        return bitmapTransform(transformations);
    }

    public DrawableRequestBuilder<ModelType> centerCrop() {
        return transform(glide.getDrawableCenterCrop());
    }

    public DrawableRequestBuilder<ModelType> fitCenter() {
        return transform(glide.getDrawableFitCenter());
    }

    public DrawableRequestBuilder<ModelType> bitmapTransform(Transformation<Bitmap>... bitmapTransformations) {
        GifBitmapWrapperTransformation[] transformations =
                new GifBitmapWrapperTransformation[bitmapTransformations.length];
        for (int i = 0; i < bitmapTransformations.length; i++) {
            transformations[i] = new GifBitmapWrapperTransformation(glide.getBitmapPool(), bitmapTransformations[i]);
        }
        return transform(transformations);
    }

    @Override
    public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) {
        super.transform(transformation);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> transcoder(
            ResourceTranscoder<GifBitmapWrapper, GlideDrawable> transcoder) {
        super.transcoder(transcoder);
        return this;
    }

    public final DrawableRequestBuilder<ModelType> crossFade() {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
        return this;
    }

    public DrawableRequestBuilder<ModelType> crossFade(int duration) {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(duration));
        return this;
    }

    public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
                duration));
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> dontAnimate() {
        super.dontAnimate();
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> animate(ViewPropertyAnimation.Animator animator) {
        super.animate(animator);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> animate(int animationId) {
        super.animate(animationId);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> placeholder(int resourceId) {
        super.placeholder(resourceId);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> placeholder(Drawable drawable) {
        super.placeholder(drawable);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> fallback(Drawable drawable) {
        super.fallback(drawable);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> fallback(int resourceId) {
        super.fallback(resourceId);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> error(int resourceId) {
        super.error(resourceId);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> error(Drawable drawable) {
        super.error(drawable);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> listener(
            RequestListener<? super ModelType, GlideDrawable> requestListener) {
        super.listener(requestListener);
        return this;
    }
    @Override
    public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
        super.diskCacheStrategy(strategy);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
        super.skipMemoryCache(skip);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> override(int width, int height) {
        super.override(width, height);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> sourceEncoder(Encoder<ImageVideoWrapper> sourceEncoder) {
        super.sourceEncoder(sourceEncoder);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> dontTransform() {
        super.dontTransform();
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> signature(Key signature) {
        super.signature(signature);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> load(ModelType model) {
        super.load(model);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> clone() {
        return (DrawableRequestBuilder<ModelType>) super.clone();
    }

    @Override
    public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }

    @Override
    void applyFitCenter() {
        fitCenter();
    }

    @Override
    void applyCenterCrop() {
        centerCrop();
    }
}

into()

如果说前面两步都是在准备开胃小菜的话,那么现在终于要进入主菜了,因为into()方法也是整个Glide图片加载流程中逻辑最复杂的地方。

不过从刚才的代码来看,into()方法中并没有任何逻辑,只有一句super.into(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。

DrawableRequestBuilder的父类是GenericRequestBuilder,我们来看一下GenericRequestBuilder类中的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));
}

可以看到,这里在onResourceReady()方法使用Handler发出了一条MSG_COMPLETE消息,那么在MainThreadCallback的handleMessage()方法中就会收到这条消息。从这里开始,所有的逻辑又回到主线程当中进行了,因为很快就需要更新UI了。

然后在第72行调用了handleResultOnMainThread()方法,这个方法中又通过一个循环,调用了所有ResourceCallback的onResourceReady()方法。那么这个ResourceCallback是什么呢?答案在addCallback()方法当中,它会向cbs集合中去添加ResourceCallback。那么这个addCallback()方法又是哪里调用的呢?其实调用的地方我们早就已经看过了,只不过之前没有注意,现在重新来看一下Engine的load()方法,如下所示:

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {

    ...    

    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) {

        ...

        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);
    }

    ...
}

这次把目光放在第18行上面,看到了吗?就是在这里调用的EngineJob的addCallback()方法来注册的一个ResourceCallback。那么接下来的问题就是,Engine.load()方法的ResourceCallback参数又是谁传过来的呢?这就需要回到GenericRequest的onSizeReady()方法当中了,我们看到ResourceCallback是load()方法的最后一个参数,那么在onSizeReady()方法中调用load()方法时传入的最后一个参数是什么?代码如下所示:

public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
        ResourceCallback {

    ...

    @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));
        }
    }

    ...
}

请将目光锁定在第29行的最后一个参数,this。没错,就是this。GenericRequest本身就实现了ResourceCallback的接口,因此EngineJob的回调最终其实就是回调到了GenericRequest的onResourceReady()方法当中了,代码如下所示:


public void onResourceReady(Resource<?> resource) {
    if (resource == null) {
        onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
                + " inside, but instead got null."));
        return;
    }
    Object received = resource.get();
    if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
        releaseResource(resource);
        onException(new Exception("Expected to receive an object of " + transcodeClass
                + " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
                + " inside Resource{" + resource + "}."
                + (received != null ? "" : " "
                    + "To indicate failure return a null Resource object, "
                    + "rather than a Resource object containing null data.")
        ));
        return;
    }
    if (!canSetResource()) {
        releaseResource(resource);
        // We can't set the status to complete before asking canSetResource().
        status = Status.COMPLETE;
        return;
    }
    onResourceReady(resource, (R) received);
}

private void onResourceReady(Resource<?> resource, R result) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;
    if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
            isFirstResource)) {
        GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
        target.onResourceReady(result, animation);
    }
    notifyLoadSuccess();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
                + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
    }
}

这里有两个onResourceReady()方法,首先在第一个onResourceReady()方法当中,调用resource.get()方法获取到了封装的图片对象,也就是GlideBitmapDrawable对象,或者是GifDrawable对象。然后将这个值传入到了第二个onResourceReady()方法当中,并在第36行调用了target.onResourceReady()方法。

那么这个target又是什么呢?这个又需要向上翻很久了,在第三步into()方法的一开始,我们就分析了在into()方法的最后一行,调用了glide.buildImageViewTarget()方法来构建出一个Target,而这个Target就是一个GlideDrawableImageViewTarget对象。

那么我们去看GlideDrawableImageViewTarget的源码就可以了,如下所示:

public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
    private static final float SQUARE_RATIO_MARGIN = 0.05f;
    private int maxLoopCount;
    private GlideDrawable resource;

    public GlideDrawableImageViewTarget(ImageView view) {
        this(view, GlideDrawable.LOOP_FOREVER);
    }

    public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) {
        super(view);
        this.maxLoopCount = maxLoopCount;
    }

    @Override
    public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
        if (!resource.isAnimated()) {
            float viewRatio = view.getWidth() / (float) view.getHeight();
            float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
            if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
                    && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
                resource = new SquaringDrawable(resource, view.getWidth());
            }
        }
        super.onResourceReady(resource, animation);
        this.resource = resource;
        resource.setLoopCount(maxLoopCount);
        resource.start();
    }

    @Override
    protected void setResource(GlideDrawable resource) {
        view.setImageDrawable(resource);
    }

    @Override
    public void onStart() {
        if (resource != null) {
            resource.start();
        }
    }

    @Override
    public void onStop() {
        if (resource != null) {
            resource.stop();
        }
    }
}

在GlideDrawableImageViewTarget的onResourceReady()方法中做了一些逻辑处理,包括如果是GIF图片的话,就调用resource.start()方法开始播放图片,但是好像并没有看到哪里有将GlideDrawable显示到ImageView上的逻辑。

确实没有,不过父类里面有,这里在第25行调用了super.onResourceReady()方法,GlideDrawableImageViewTarget的父类是ImageViewTarget,我们来看下它的代码吧:


public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {

    ...

    @Override
    public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
        if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
            setResource(resource);
        }
    }

    protected abstract void setResource(Z resource);

}

可以看到,在ImageViewTarget的onResourceReady()方法当中调用了setResource()方法,而ImageViewTarget的setResource()方法是一个抽象方法,具体的实现还是在子类那边实现的。

那子类的setResource()方法是怎么实现的呢?回头再来看一下GlideDrawableImageViewTarget的setResource()方法,没错,调用的view.setImageDrawable()方法,而这个view就是ImageView。代码执行到这里,图片终于也就显示出来了。

那么,我们对Glide执行流程的源码分析,到这里也终于结束了。

总结

真是好长的一篇文章,这也可能是我目前所写过的最长的一篇文章了。感谢阅读。

猜你喜欢

转载自blog.csdn.net/qq_24045059/article/details/81042685