Glide介绍
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。
Glide的使用
本文是从4.10.0开始介绍的
- 最小SDK版本需要使用API 14(或者更高版本)
- Complie SDK Version需要使用API 27(或者更高版本)
- Glide使用的SupportLibrary 版本是27,如果需要不同的SupportLibrary版本可以用exclude将Glide的SupportLibrary从依赖中去掉,具体的在集成时说明
Glide导包
implementation('com.github.bumptech.glide:glide:4.10.0')
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
//如果使用了在Kotlin中使用了Glide注解,需要引入kapt依赖代替annotationProcessor依赖
//kapt 'com.github.bumptech.glide:compiler:4.10.0'
//若使用了不是27的SupportLibrary版本,使用以下代码
implementation('com.github.bumptech.glide:glide:4.10.0') {
exclude group: "com.android.support"
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
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。
- load(String url) - 这里我们所使用的一个字符串形式的网络图片的 URL,后面会讲解 load() 的更多使用方式
- into(ImageView imageView) - 你需要显示图片的目标 ImageView
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)
.thumbnail( 0.2f )
.placeholder(R.drawable.place_image)//图片加载出来前,显示的图片
.error(R.drawable.error_image)//图片加载失败后,显示的图片
.into(imageView);
- placeholder(int resId) 图片加载出来前显示的预显示图片
- error(int resId) 加载失败后显示的预存图片
上面这两个参数也支持Drawable 参数 - thumbnail 是做略图的属性,上面的例子中,如果是0.2f,则将会显示原始图片的20%的大小,如果原图是 1000x1000 的尺寸,那么缩略图将会是 200x200 的尺寸
缩略图还有另外一种
private void loadImageThumbnailRequest(){
// setup Glide request without the into() method
DrawableRequestBuilder<String> thumbnailRequest = Glide.with( context ).load( url );
// pass the request as a a parameter to the thumbnail request
Glide.with( context )
.load( url )
.thumbnail( thumbnailRequest )
.into( imageView );
}
与上面第一种方式不同的是,这里的第一个缩略图请求是完全独立于第二个原始请求的。该缩略图可以是不同的资源图片,同时也可以对缩略图做不同的转换,等等...
图片的大小调整
可以调用override来调整图片的大小
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200) // resizes the image to these dimensions (in pixel). does not respect aspect ratio
.into(imageViewResize);
transformation(变换)
Glide提供了transformation得功能,在获取到请求的图片之后,能对图片进行一些处理,例如:裁剪、模糊等;而transformation的强大在于可以自定义,这样一来transformation不仅能处理bitmap,同样可以用于处理GIF动画,还有自定义资源类型。
Glide在API上提供的了5个相关的方法可以直接使用
circleCrop()//加载原形图片
centerCrop()//方法是将图片按比例缩放到足矣填充
centerInside()//保留图片原本的长宽比,如果图片长和宽(X和Y)均小于ImageView的长和宽,则不进行放大,直接将图片在ImageView中居中展示;否则对图片的长和宽(X和Y)进行等比例缩小,直到图片至少一边与ImageView重合,并且图片完全显示在ImageView里面,最终将图片在ImageView中居中展示。
fitCenter()//保留图片原本的长宽比,对图片的长和宽(X和Y)进行等比例缩放,直到图片至少一边与ImageView重合,并且图片完全显示在ImageView里面,最终将图片在ImageView中居中展示
optionalFitCenter()//
使用方法如下
GlideApp.with(this)
.load(url)
.centerCrop()
.into(imageView);
可以直接调用以上方法,如果没有直接可以调用的方法,需要通过RequestOptions来设置
RequestOptions options = new RequestOptions().centerCrop();
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
//静态方法
Glide.with(this)
.load(url)
.apply(RequestOptions.centerCropTransform())
.into(imageView);
针对ImageView可能自身也会设置scaleType的情况,Glide在部分情况会自动应用 FitCenter或CenterCrop,如果 scaleType 是 CENTER_CROP , Glide 将会自动应用 CenterCrop 变换。如果 scaleType 为 FIT_CENTER 或 CENTER_INSIDE ,Glide会自动使用 FitCenter 变换
图片的缓存处理
为了更快的加载图片,我们肯定希望可以直接拿到图片,而不是进行网络请求,所以我们需要缓存。Glide 通过使用默认的内存和磁盘缓存来避免不必要的网络请求,之后我们再详细的去看它的实现。
内存缓存是 Glide 默认帮我们做了的,除非你不需要,可以调用 skipMemoryCache(true) 告诉 Glide 跳过内存缓存。这样 Glide 就不会把这张图片放到内存缓存中,该方法只影响内存缓存。
此外也可以调用用diskCacheStrategy方法自定义缓存策略
- DiskCacheStrategy.NONE 什么都不缓存
- DiskCacheStrategy.SOURCE 只缓存全尺寸图
- DiskCacheStrategy.RESULT 只缓存最终的加载图
- DiskCacheStrategy.ALL 缓存所有版本图(默认行为)
Glide.with(context)
.load(url)
.skipMemoryCache(true)
.diskCacheStrategy( DiskCacheStrategy.NONE )
.into(imageView);
图片的优先级
同一时间加载多个图片,App 将难以避免这种情况。如果这个时候我们希望用户的体验更好,往往会选择先加载对于用户更加重要的图片。Glide 可以调用 .priority() 方法配合 Priority 枚举来设置图片加载的优先级。
- Priority.LOW
- Priority.NORMAL
- Priority.HIGH
- Priority.IMMEDIAT
Glide.with( context )
.load( highPriorityImageUrl )
.priority (Priority.HIGH )
.into( imageView );
图片的动画效果
无论你是否使用占位图,对于UI来说,图片的改变是相当大的一个动作。一个简单的方法可以让这个变化更平滑,更让人眼接受,这就是使用crossfade动画。
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
.crossFade(300)
.into(imageViewFade);
crossFade()方法可以携带参数,参数代表着图片的平滑替换时间,默认是300毫秒
如果你想直接跳过这个动画则直接调用.dontAnimate()方法
在图片中
Glide gif和视频的播放
许多图片加载库能处理好图片的加载与显示,但很多并不支持Gif。如果你的app需要支持Gif,Glide的简单会让体验更惊艳。如果你想要播放Gif,你只要使用之前处理图片的类似的方法,
Glide
.with(context)
.load(gifUrl)
.asGif()
.error( R.drawable.full_cake)
.into( imageViewGif );
在上面的代码中还有一个asGif的方法,这个方法是为了防止出入的gif图片如果不是gif只是一个普通的图片,会将它通过asGif方法装换成gif,防止上面的方法调用后报错。如果你的app需要显示一组网络URL,可能包括普通的图片或者Gif。在一些情况下,你可能并不在意是否要播放完整的Gif。如果你只是想要显示Gif的第一帧,当URl指向的的确是Gif,你可以调用asBitmap()将其作为常规图片显示。
此外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
private SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(500,500) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> animation) {
mImageView.setImageBitmap(resource);
}
};
private void loadImageSimpleTarget() {
Glide.with( thi s)
.load( mUrl )
.asBitmap()
.into( mSimpleTarget );
}
首先创建了一个 SimpleTarget 的对象并且实现了 onResourceReady() 方法,看方法名能知道是图片加载完之后会调用该方法,参数就有我们需要的 Bitmap 。而使用 SimpleTarget 的对象的时候就像使用 ImageView 一样,作为参数传给 into() 方法就行了,Glide 会内部去处理并返回结果给任何一个对象。这里我们为了防止加载 Gif 、 Video 或者一些位置资源时与 mSimpleTarget 冲突,所以我们调用了 asBitmap() 方法,使其只能返回 Bitmap 对象。
当我们使用 Custom View 时,Glide 并不支持加载图片到自定义 view 中的,使用 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);
}
在 target 的 onResourceReady 回调方法中使用自定义 view 自己的方法去设置图片,可以看到在创建 ViewTarget 的时候传入了 CustomView 的对象。
下面是通知栏Target 与 AppWidgetTarget的使用
NotificationTarget target2=new NotificationTarget(context,
remoteview,
R.id.remoteview_notification_icon,
notification,
NOTIFICATION_ID);
Glide
.with( context.getApplicationContext() )
.load( url)
.asBitmap()
.into( notificationTarget );
public class MYAppWidgetProvider extends AppWidgetProvider {
private AppWidgetTarget appWidgetTarget;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews remoteviews= new RemoteViews(context.getPackageName(), R.layout.my_remoteView);
appWidgetTarget = new AppWidgetTarget( context, remoteviews, R.id.custom_view_image, appWidgetIds );
Glide
.with( context.getApplicationContext() ) // safer!
.load( GlideExampleActivity.eatFoodyImages[3] )
.asBitmap()
.into( appWidgetTarget );
pushWidgetUpdate(context, remoteviews);
}
public static void pushWidgetUpdate(Context context, RemoteViews remoteviews) {
ComponentName myWidget = new ComponentName(context, FSAppWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, rv);
}
}
异常处理
Glide不提供直接获取常规请求的日志,但是你可以在请求出错时抓取异常的日志。例如,如果图片不存在,Glide会(静静地)抛出一个异常,并显示出你.erroer()里指定的图片。如果你明确想要知道异常,创建一个listener,然后传递给Glide的.listener()方法。
private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
// todo log exception
// important to return false so the error placeholder can be placed
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
};
Glide
.with( context )
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.listener( requestListener )
.error( R.drawable.cupcake )
.into( imageViewPlaceholder );
以上是Glide的基本使用
Glide源码解析
从上面的一大串代码中我们可以发现,glide使用的是建造者模式
我们先看看with方法
with方法有很多的构造函数,可以传入context、Activity等
Glide->with
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
Glide->getRetriever
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");//非空判断
return Glide.get(context).getRequestManagerRetriever();//获取 RequestManagerRetriever 对象
}
Glide->get
/**
* Get the singleton.
*
* @return the singleton
*/
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
//这句是通过反射获取com.bumptech.glide.GeneratedAppGlideModuleImpl并实例化
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {//单例模式
checkAndInitializeGlide(context, annotationGeneratedModule);//从名字上可以看出是初始化glide
}
}
}
return glide;
}
Glide->initializeGlide
@GuardedBy("Glide.class")
private static void initializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
Glide->initializeGlide
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
...
Glide glide = builder.build(applicationContext);
...
}
GlideBuilder->build
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();//创建一个默认的线程池,用来执行耗时任务
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();//创建一个默认的硬盘缓存线程池
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();//创建一个执行动画效果的线程池
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();//创建内存缓存控制器,并设置其大小
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();//创建网络监听器
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();//创建bitmap对象池
if (size > 0) { // 使用缓存
bitmapPool = new LruBitmapPool(size);
} else {// 不使用缓存
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);//在这里创建了requestManagerRetriever
最后创建一个请求管理类,将请求工厂对象传入进去,并且会实例化一个主线程的handler对象,用于主-子线程的切换
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps,
hardwareBitmapFdLimit,
minHardwareDimension);
}
}
RequestManagerRetriever
public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
this.factory = factory != null ? factory : DEFAULT_FACTORY;
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}
Glide->getRequestManagerRetriever
@NonNull
public RequestManagerRetriever getRequestManagerRetriever() { //在这里我们看到直接返回了requestManagerRetriever对象
return requestManagerRetriever;
}
通过上面的方法,我们可以看到Glide已经初始化完成了
现在已经获取到RequestManagerRetriever对象,接下来我们回到刚才的方法中,它调用了RequestManagerRetriever的get方法
从上图可以看到,RequestManagerRetriever中有很多的get方法
RequestManagerRetriever->get
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {//如果不是application的context就一直迭代这个方法
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
// Only unwrap a ContextWrapper if the baseContext has a non-null application context.
// Context#createPackageContext may return a Context without an Application instance,
// in which case a ContextWrapper may be used to attach one.
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
//一直迭代这个方法,获取到application的context,最后进入到这个方法
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, null /*parentHint*/);
}
}
public RequestManager get(Fragment fragment) {
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment);
}
}
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, null /*parentHint*/);
}
}
public RequestManager get(View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Activity activity = findActivity(view.getContext());
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
他们会判断Context是Activity的还是Fragment的调用对应的方法,如果传入的FragmentActivity或者Fragment,会调用supportFragmentGet,如果传入的是Activity则调用fragmentGet,如果传入的是view,则根据view中的context来判断
他们的共同点是,如果isOnBackgroundThread是true,则会调用Application的方法
RequestManagerRetriever->getApplicationManager
@NonNull
private RequestManager getApplicationManager(@NonNull 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.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
//在这里获取RequestManager 对象
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
@Deprecated
@NonNull
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
从上面代码上看,传入Activity 或者Fragment的context的最后都调用了getSupportRequestManagerFragment
@SuppressWarnings("deprecation")
@NonNull
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
SupportRequestManagerFragment的构造函数创建了一个ActivityFragmentLifecycle对象用来管理生命周期,在onStart方法中调用ActivityFragmentLifecycle的onStart方法,同理其他生命周期方法也是。
public class SupportRequestManagerFragment extends Fragment {
private static final String TAG = "SupportRMFragment";
private final ActivityFragmentLifecycle lifecycle;
private final RequestManagerTreeNode requestManagerTreeNode =
new SupportFragmentRequestManagerTreeNode();
private final Set<SupportRequestManagerFragment> childRequestManagerFragments = new HashSet<>();
@Nullable private SupportRequestManagerFragment rootRequestManagerFragment;
@Nullable private RequestManager requestManager;
@Nullable private Fragment parentFragmentHint;
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
@VisibleForTesting
@SuppressLint("ValidFragment")
public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
/**
* Sets the current {@link com.bumptech.glide.RequestManager}.
*
* @param requestManager The manager to put.
*/
public void setRequestManager(@Nullable RequestManager requestManager) {
this.requestManager = requestManager;
}
...
@Override
public void onDetach() {
super.onDetach();
parentFragmentHint = null;
unregisterFragmentWithRoot();
}
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
接下来我们看下RequestManager
RequestManager
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
private static final RequestOptions DECODE_TYPE_BITMAP = decodeTypeOf(Bitmap.class).lock();
private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();
private static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
diskCacheStrategyOf(DiskCacheStrategy.DATA).priority(Priority.LOW).skipMemoryCache(true);
protected final Glide glide;
protected final Context context;
@SuppressWarnings("WeakerAccess")
@Synthetic
final Lifecycle lifecycle;
@GuardedBy("this")
private final RequestTracker requestTracker;
@GuardedBy("this")
private final RequestManagerTreeNode treeNode;
@GuardedBy("this")
private final TargetTracker targetTracker = new TargetTracker();
private final Runnable addSelfToLifecycle =
new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
};
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final ConnectivityMonitor connectivityMonitor;
// Adding default listeners should be much less common than starting new requests. We want
// some way of making sure that requests don't mutate our listeners without creating a new copy of
// the list each time a request is started.
private final CopyOnWriteArrayList<RequestListener<Object>> defaultRequestListeners;
@GuardedBy("this")
private RequestOptions requestOptions;
private boolean pauseAllRequestsOnTrimMemoryModerate;
public RequestManager(
@NonNull Glide glide,
@NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode treeNode,
@NonNull Context context) {
this(
glide,
lifecycle,
treeNode,
new RequestTracker(),
glide.getConnectivityMonitorFactory(),
context);
}
// Our usage is safe here.
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
connectivityMonitor =
factory.build(
context.getApplicationContext(),
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()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
defaultRequestListeners =
new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners());
setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());
glide.registerRequestManager(this);
}
...
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
...
在RequestManager实现了LifecycleListener接口,并且调用lifecycle.addListener(this)添加监听器。接口生命周期函数onStart()启动请求,onStop暂停请求,在ActivtyFragmentLifecycle中如果添加了监听器,在onStart调用监听器的onStart方法,也就是调用RequsetManager中的onStart,同理onPause也是。
class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners =
Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;
/**
* Adds the given listener to the list of listeners to be notified on each lifecycle event.
*
* <p>The latest lifecycle event will be called on the given listener synchronously in this
* method. If the activity or fragment is stopped, {@link LifecycleListener#onStop()}} will be
* called, and same for onStart and onDestroy.
*
* <p>Note - {@link com.bumptech.glide.manager.LifecycleListener}s that are added more than once
* will have their lifecycle methods called more than once. It is the caller's responsibility to
* avoid adding listeners multiple times.
*/
@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
@Override
public void removeListener(@NonNull LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
}
经过上面的分析发现整个过程就理顺了,先创建一个Fragment对象并在构造函数中创建Lifecycle对象
,在创建RequestManager的时候会把Lifecycle对象传递进去。RequestManger则在构造函数处为接收的Lifecycle添加监听器,而RequestManager自身实现了LifecycleListener,在实现的接口方法onStop和onPause分别启动和暂停请求。
Lifecycle对象在添加监听器后,会在生命周期方法onStart和onPauser分别调用监听器对应的方法。
因此当Fragment会在生命周期方法中调用Lifecycle对应的生命周期方法,最终也就是在调用RequestManger中的生命周期方法,因此实现了请求和生命周期的绑定,从而可以根据生命周期来启动和暂停请求。
总结一下,with()方法主要初始化Glide和创建了RequestManger绑定SupportRequestManagerFragment的生命周期函数,最后返回RequestManager。
load方法
我们看到load方法有很多重载方法,load方法都实现自ModelTypes,经过with方法,我们知道with方法返回的是requestManager对象,因此load方法在RequestManager方法中查找
RequestManager implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
...
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Drawable)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(String)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Uri)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(File)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Integer)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@SuppressWarnings("deprecation")
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(URL)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@SuppressWarnings("deprecation")
@CheckResult
@Override
@Deprecated
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(byte[])}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
/**
* A helper method equivalent to calling {@link #asDrawable()} and then {@link
* RequestBuilder#load(Object)} with the given model.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
...
我们可以发现无论哪个load方法都是调用了asDrawable()方法
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
public RequestBuilder<File> asFile() {
return as(File.class).apply(skipMemoryCacheOf(true));
}
public RequestBuilder<GifDrawable> asGif() {
return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
}
public RequestBuilder<Bitmap> asBitmap() {
return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
}
我们可以看到,调用AS方法后里面实际上是初始化一个RequestBuilder对象
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
...
@SuppressLint("CheckResult")
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
...
构造了一个RequestBuilder实例,传入的参数赋值到RequestBuilder的成员变量,这里成员transcodeClass也就是刚才的Drawable.class,记住了,初始化了一个requestOptions,这2个成员记住了,后面会有用的,接着回到之前的一段代码,查看load方法
RequestBuilder->load
@NonNull
@CheckResult
@Override
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
return loadGeneric(drawable)
.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap)
.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
return loadGeneric(uri);
}
public RequestBuilder<TranscodeType> load(@Nullable File file) {
return loadGeneric(file);
}
public RequestBuilder<TranscodeType> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return loadGeneric(resourceId).apply(signatureOf(ApplicationVersionSignature.obtain(context)));
}
public RequestBuilder<TranscodeType> load(@Nullable URL url) {
return loadGeneric(url);
}
public RequestBuilder<TranscodeType> load(@Nullable byte[] model) {
RequestBuilder<TranscodeType> result = loadGeneric(model);
if (!result.requestOptions.isDiskCacheStrategySet()) {
result = result.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
if (!result.requestOptions.isSkipMemoryCacheSet()) {
result = result.apply(skipMemoryCacheOf(true /*skipMemoryCache*/));
}
return result;
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;//这个model就是url String 的实例
//数据来源是否已经设置的标志位 isModelSet 设置为true,意味着我们在调用 Glide.with(context).load(url) 之后数据来源已经设置成功了。
isModelSet = true;
return this;
}
到这里其实load方法就已经完了,还有一些其他的方法
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}
public RequestBuilder<TranscodeType> transition(
@NonNull TransitionOptions<?, ? super TranscodeType> transitionOptions) {
this.transitionOptions = Preconditions.checkNotNull(transitionOptions);
isDefaultTransitionOptionsSet = false;
return this;
}
public RequestBuilder<TranscodeType> listener(
@Nullable RequestListener<TranscodeType> requestListener) {
this.requestListeners = null;
return addListener(requestListener);
}
public RequestBuilder<TranscodeType> addListener(
@Nullable RequestListener<TranscodeType> requestListener) {
if (requestListener != null) {
if (this.requestListeners == null) {
this.requestListeners = new ArrayList<>();
}
this.requestListeners.add(requestListener);
}
return this;
}
public RequestBuilder<TranscodeType> error(@Nullable RequestBuilder<TranscodeType> errorBuilder) {
this.errorBuilder = errorBuilder;
return this;
}
info(ImageView view)方法
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {//这里获取imageView的ScaleType属性,glide会将ScaleType转化成requestOptions
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
RequestBuilder->info
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}