1.Glide
Glide是Google主导的图片加载开源库。它稳定、速度快、可自适应图片尺寸、支持众多格式、支持加载不同来源的图片、内存和磁盘缓存的优化等。
Glide优势:
①多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
②生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)。Glide可以感知调用页面的生命周期
③高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
④高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
2.Glide的使用
①Glide的一个完整的请求至少需要三个参数:
Glide.with(context).load(url).into(imageView);
with(Context context) :上下文,这里还可以使用Activity、Fragment的对象。将Activity或Fragment对象作为参数的好处是,图片的加载会和Activity/Fragment的生命周期保持一致,例如:onPaused 时暂停加载,onResume 时又会自动重新加载。所以在传参的时候建议使用 Activity/Fragment 对象,而不是Context。
load(String url) - 这里使用的是一个图片的URL
into(ImageView imageView) - 需要显示图片的目标 ImageView
②占位图设置
Glide.with(context).load(url)
.placeholder(R.drawable.place_image)
.error(R.drawable.error_image)
.fallback(R.drawable.fallback_image)
.into(imageView);
placeholder :指定加载前显示的图片资源。
error:指定加载失败显示的图片资源。
fallback:指定传递加载资源为null的时候,显示的图片资源。
注:这里需要注意一点,placeholder() 和 error() 的参数都是只支持 int 和 Drawable 类型的参数,这种设计应该是考虑到使用本地图片比网络图片更加合适做占位图。
③裁剪缩放
在项目开发过程中,指定图片显示大小可能用到,毕竟从服务器获取的图片不一定都是符合设计图的标准的。在这里就可以使用 override(width,height)方法,在图片显示到ImageView之前,重新改变图片大小。
Glide.with(context).load(url)
.override(width,height)//这里的单位是px
.into(imageView);
ImageView本身提供了ScaleType属性,这个属性可以控制图片显示时的方式。
Glide也提供了两个类似的方法CenterCrop()和 FitCenter()。
CenterCrop()方法是将图片按比例缩放到足以填充 ImageView的尺寸,但是图片可能会显示不完整;
FitCenter()则是图片缩放到小于等于ImageView的尺寸,这样图片是显示完整了,但是ImageView就可能不会填满了。
注:其实Glide的CenterCrop()和FitCenter()这两个方法分别对应ImageView的ScaleType属性中的CENTER_CROP和FIT_CENTER命名基本一致。
④缩略图
Glide的缩略图功能和占位图略有不同,占位图必须使用资源文件才行,而缩略图是动态的占位图可以从网络中加载。缩略图会在事件请求加载完成或者处理完之后才显示。在原始图片到达之后,缩略图不会取代原始图片,只会被抹除。
Glide 为缩略图提供了2种不同的加载方式,比较简单的方式是调用thumbnail()方法,参数是 float 类型,作为其倍数大小。例如,传入0.2f作为参数,Glide将会显示原始图片的20%的大小,如果原图是1000x1000的尺寸,那么缩略图将会是 200x200的尺寸。为缩略图明显比原图小得多,所以我们需要确保ImageView的ScaleType设置的正确。
Glide.with(context).load(url).thumbnail(0.2f ).into(imageView);
注:应用于请求的设置也将应用于缩略图。
使用thumbnail()方法来设置是简单粗暴的,但是如果缩略图需要通过网络加载相同的全尺寸图片,就不会很快的显示了。所以Glide提供了另一种防止去加载缩略图。
private void loadImageThumbnailRequest(){
DrawableRequestBuilder<String> thumbnailRequest = Glide.with( context ).load( url );
Glide.with( context ).load( url )
.thumbnail( thumbnailRequest )
.into( imageView );
}
与第一种方式不同的是,这里的第一个缩略图请求是完全独立于第二个原始请求的。该缩略图可以是不同的资源图片,同时也可以对缩略图做不同的转换,等等…
⑤载入动画
Glide在显示图片的时候,为了让显示效果不那么突兀,会以一种更柔和的方式去显示,就会在加载的时候给一个动画效果,它可以使用 crossFade()方法进行配置,如果不特殊处理,默认它是开启的,并且本身默认动画的时长是 300ms。
crossFade()的效果是默认开启的,所以如果不需要这样的一个动画效果,可以使用dontAnimate() 来禁用动画效果。
Glide.with(context).load(url)
.dontAnimate()
.placeholder(R.drawable.place_image)
.error(R.drawable.error_image)
.into(imageView);
⑥缓存处理
为了更快的加载图片,肯定希望可以直接拿到图片,而不是进行网络请求,所以需要缓存。Glide 通过使用默认的内存和磁盘缓存来避免不必要的网络请求。
Glide.with(context).load(url)
.skipMemoryCache(false)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
内存缓存是默认开启的,.skipMemoryCache(false)可以关闭。
磁盘缓存是默认开启,.diskCacheStrategy( DiskCacheStrategy strategy )可以关闭。
DiskCacheStrategy 的枚举意义:
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认)
⑦优先级
对于同一个页面,如果需要在多个地方都加载线上图片,必然会存在一个优先级的问题。例如:正常来说,背景图是比其他图片优先级更高的图片。
Glide 是可以在加载中,对当前加载的图片,调整加载的优先级的。需要使用.priority() 方法,它可以接受一个 Priority 的枚举类型,包含四种值:LOW(低)、HIGH(高)、NORMAL(普通)、IMMEDIATE(立即)。
⑧支持 Gif & 视频
Glide 支持 Gif
Glide.with(context).load(gifUrl)
.asGif()
.error( R.drawable.error )
.into( imageView );
.asGif(): 校验它是否是一个 Gif ,如果不是,则认为是一次错误的加载。这个时候就可以使用 asGif() 来进行校验,如果当前加载的图片不是一个正确的 Gif 格式,则会去显示 error() 配置的图片。
有时候可能只为显示一张图片,可以强制显示Gif 图片的第一帧,用asBitmap()方法替代asGif()即可。
Glide还能显示视频(只能显示手机本地的视频)。但它和Gif显示的效果不一样的一点在于,它不会去播放视频文件,而只是将视频文件的第一帧做为一个图片去显示出来。
String filePath = "/storrage/emulated/0/Pictures/video.mp4";
Glide.with( context )
.load( Uri.fromFile( new File( filePath ) ) )
.into( imageView );
⑨加载监听
Glide 加载的图片的结果可以进行监听,使用 listener() 方法设置一个监听器,它接收一个 RequestListener 的接口。
RequestListener requestListener = new RequestListener() {
@Override
public boolean onLoadFailed(GlideException e, Object model, Target target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Object resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
return false;
}
};
Glide.with(context).load(url)
.listener(requestListener)
.into(imageView);
如果需要监听图片加载错误的原因,可以在 onLoadFailed() 中做处理。
需要注意的是,这两个方法的返回值,最好都是 false。因为如果返回 true ,将表示你已经处理了这次的事件,而 Glide 将不会再做额外的处理。例如,如果 onLoadFailed() 返回了 true 的话,在图片加载失败之后,error() 中设置的图片,并不会被显示,因为 Glide 认为开发者已经在外部对这个错误进行了处理。
⑩变换加载的图片
对于使用Glide加载的图片,如果想要在其显示之前,对其进行一些变换操作,例如,改变颜色、虚化、圆角子类的,都需要用到transfrom()方法,它主要用于支持在图片显示之前,自定义的变换效果。
变换有两个方法:
transfrom():它可以添加一个通用的变换效果。
bitmapTransfrom():限制了变换的类型,只能设置 Bitmap 的变换。
3.Glide源码解析
以Glide.with(activity)方法为切入点开始分析源码:
public static RequestManager with( FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
with()方法返回的是RequestManager实例,获取RequestManager分两步:
①第一步getRetriever(activity):先获取到RequestManagerRetriever实例。
//1. 单例模式创建Glide实例
//2. 获取RequestManagerRetriever实例
private static RequestManagerRetriever getRetriever(Context context) {
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(); //分两步分析
}
1)使用单例模式获取Glide实例
public static Glide get(Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules( context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
2)继续追踪调用链:checkAndInitializeGlide() -> initializeGlide()
private static void initializeGlide(Context context, GlideBuilder builder, GeneratedAppGlideModule annotationGeneratedModule) {
……
Glide glide = builder.build(applicationContext); //建造者模式创建Glide实例
……
Glide.glide = glide;
}
3)最终调用GlideBuilder.build()方法构建Glide实例
Glide build(Context context) {
//此处省略初始化默认配置信息代码
……
//创建实例并赋值给Glide成员变量requestManagerRetriever 。这里传入的requestManagerFactory用来创建RequestManager的工厂
RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory, experiments);
return new Glide(context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel, defaultRequestOptionsFactory, defaultTransitionOptions, defaultRequestListeners, experiments);
}
获取RequestManagerRetriever小结:
RequestManagerRetriever的创建是在Glide的初始化过程中完成的;
RequestManagerRetriever构造方法内传入requestManagerFactory,用来创建RequestManager。
②第二步 get(activity)创建并返回RequestManager实例
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, null, isActivityVisible(activity));
}
}
1)如果当前线程不是主线程,直接转到get(activity.getApplicationContext())中执行;否则继续调用supportFragmentGet()方法。
private RequestManager supportFragmentGet( Context context, FragmentManager fm, Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
//工厂模式创建requestManager
requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager( requestManager); //requestManager存储到隐藏的Fragment
}
return requestManager;
}
2)RequestManagerFactory初始化过程
RequestManagerRetriever.java
//默认RequestManagerFactory
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
@Override
public RequestManager build(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode requestManagerTreeNode, Context context) {
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
}
};
public RequestManagerRetriever( RequestManagerFactory factory, GlideExperiments experiments) {
this.factory = factory != null ? factory : DEFAULT_FACTORY;
handler = new Handler( Looper.getMainLooper(), this );
frameWaiter = buildFrameWaiter( experiments);
}
默认使用DEFAULT_FACTORY来创建RequestManager。
3)RequestManager构造方法
RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) {
……
if (Util.isOnBackgroundThread()) {
Util.postOnUiThread(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
}
private final Runnable addSelfToLifecycle = new Runnable() {
@Override
public void run() {
lifecycle.addListener( RequestManager.this);
}
};
把当前RequestManager添加到lifecycle,lifecycle是ActivityFragmentLifecycle类型,在SupportRequestManagerFragment构造方法中创建。
重点类总结:
①RequestManager implements LifecycleListener
操作和管理Glide加载图片的请求;
实现LifecycleListener接口,根据Activity、Fragment生命周期变化和网络状态来控制图片加载流程;
由RequestManagerRetriever统一创建和管理,创建时使用工厂模式;
②RequestManagerRetriever
作用是创建和复用RequestManager:使用RequestManagerRetriever.RequestManagerFactory工厂创建RequestManager,复用SupportRequestManagerFragment 中已有的RequestManager;
在Glide初始化时创建;
③SupportRequestManagerFragment 无视图的Fragment
安全存储RequestManager,由RequestManagerRetriever负责绑定;
添加到当前Activity,利用SupportRequestManagerFragment的生命周期变化来控制图片的加载流程;
4.RequestManager如何感知组件的生命周期
Glide感知生命周期的方法是添加隐藏的SupportRequestManagerFragment,继续查看getSupportRequestManagerFragment源码:
RequestManagerRetriever.java:
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>(); //缓存已经添加的隐藏SupportRequestManagerFragment
private SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm, Fragment parentHint) {
SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current=(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint( parentHint);
pendingSupportRequestManagerFragmen ts.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPP ORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
首先检查是否已经添加了隐藏的SupportRequestManagerFragment,使用HashMap缓存SupportRequestManagerFragment 。然后查找FragmentManager中是否有可以复用的SupportRequestManagerFragment。如果都没有找到,创建一个新的SupportRequestManagerFragment,并且添加到pendings列表&&提交到FragmentManager。
现在隐藏的SupportRequestManagerFragment已经添加成功,继续分析SupportRequestManagerFragment.java源码:
private final ActivityFragmentLifecycle lifecycle;
//构造方法
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
public SupportRequestManagerFragment( ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
……
@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();
}
新建一个ActivityFragmentLifecycle实例赋值给SupportRequestManagerFragment的lifecycle成员变量,查看ActivityFragmentLifecycle类:
class ActivityFragmentLifecycle implements Lifecycle {
//储存LifecycleListener的列表
private final Set<LifecycleListener> lifecycleListeners= Collections.newSetFromMap( new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;
//向lifecycleListeners列表添加LifecycleListener
@Override
public void addListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
//添加完成后,根据状态执行对应的回调方法
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
@Override
public void removeListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
//遍历执行onStart
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
//与onStart类似
void onStop() {
……
}
void onDestroy() {
……
}
}
使用观察者模式,在组件生命周期发生变化的时候回调相应的操作方法。这一切都是框架完成的,在RequestManager构造函数中把自身加入到ActivityFragmentLifecycle的成员变量lifecycleListeners中。
RequestManager.java:
RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) {
……
if (Util.isOnBackgroundThread()) {
Util.postOnUiThread(addSelfToLifecycle); //切换到主线程执行
} else {
lifecycle.addListener(this); //添加到ActivityFragmentLifecycle的成员变量lifecycleListeners
}
lifecycle.addListener(connectivityMonitor);
}
private final Runnable addSelfToLifecycle = new Runnable() {
@Override
public void run() {
lifecycle.addListener( RequestManager.this); //添加到ActivityFragmentLifecycle成员变量lifecycleListeners
}
};
监听组件生命周期流程汇总:
①创建无视图SupportRequestManagerFragment 绑定到当前Activity;
SupportRequestManagerFragment持有RequestManager实例,RequestManager实例由RequestManagerRetriever创建并传递给SupportRequestManagerFragment ;
②RequestManager持有ActivityFragmentLifecycle引用,在SupportRequestManagerFragment 构造方法中产生并传递给RequestManager;
RequestManager在构造函数中把自己加入到ActivityFragmentLifecycle.lifecycleListeners列表
③生命周期同步过程,以Activity调用onStart()为例:
1)Activity调用onStart()
2)SupportRequestManagerFragment调用onStart()
@Override
public void onStart() {
super.onStart();
lifecycle.onStart(); //==ActivityFragmentLifecycle.onStart()
}
3)ActivityFragmentLifecycle#onStart()
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart(); //==RequestManager.onStart()
}
}
4)RequestManager#onStart()
@Override
public synchronized void onStart() {
resumeRequests(); //启动图片加载请求
targetTracker.onStart(); //添加到TargetTracker,统一管理targets响应组件生命周期
}
图片加载时感知组件生命周期全过程:
5.Glide如何管理图片加载请求
with()方法返回的是一个RequestManager对象,说明load()方法是在RequestManager类当中的,所以首先要看的就是RequestManager这个类。
load()方法支持多种形式的图片来源,以RequestManager类中的 load(String string) 方法为例分析。
@Override
public RequestBuilder<Drawable> load(String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as( Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
为了使逻辑更清晰,把上面三个方法合并成一行代码来分析:
RequestManager#load(String string)
//相当于
new RequestBuilder<>(glide, RequestManager.this, Drawable.class, context).load(string);
load(String string)最终被分为两步执行:
①以Drawable.class为参数创建RequestBuilder实例。
RequestBuilder构造函数:
protected RequestBuilder(Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass; //重要变量赋值为Drawable.class,构建ImageViewTarget的时候使用
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners( requestManager.getDefaultRequestListeners()); //初始化默认请求监听
apply(requestManager.getDefaultReque stOptions()); //应用默认配置信息
}
②调用RequestBuilder#load(String string)
public RequestBuilder<TranscodeType> load(String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(Object model) {
if (isAutoCloneEnabled()) {
return clone().loadGeneric(model);
}
this.model = model; //String变量赋值给model
isModelSet = true;
return selfOrThrowIfLocked();
}
RequestBuilder重要成员变量:
transcodeClas = Drawable.class, 构建ImageViewTarget的时候使用;
model = 图片来源为String地址,构建Request的时候使用;
RequestManager#load(String string)分析到现在,似乎都是一些准备工作,真正的图片加载还没有出现。继续查看RequestBuilder#into(ImageView view)方法:
#RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(ImageView view) {
Util.assertMainThread(); //判断是否在主线程执行
Preconditions.checkNotNull(view);
//把ImageView的scaleType写进requestOptions
……
return into(
//注释1:构建新的ImageViewTarget
glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions, Executors.mainThreadExecutor()); //主线程池,切回到主线程显示图片
}
private <Y extends Target<TranscodeType>> Y into(Y target, RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//注释2:构建新的加载请求Request
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest(); //获取目标View上已经存在的旧请求
if (request.isEquivalentTo(previous) //两次的请求相同 && !(跳过内存缓存 && 旧请求已完成)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull( previous).isRunning()) { //如果旧请求未开始
previous.begin(); //启动旧请求
}
return target;
}
//取消Glide为target准备的所有加载请求,并释放已经加载的资源(例如Bitmap),以便它们可以被重用。
requestManager.clear(target);
//利用View.setTag把request请求绑定到target指向的View
target.setRequest(request);
//注释3
requestManager.track(target, request);
return target;
}
private boolean isSkipMemoryCacheWithCom pletePreviousRequest(
BaseRequestOptions<?> options, Request previous) {
return !options.isMemoryCacheable() && previous.isComplete();
}