关于Glide
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。
API
Glide 使用简明的流式语法API,这是一个非常棒的设计,因为它允许你在大部分情况下一行代码搞定需求:
Glide.with(fragment)
.load(url)
.into(imageView);
Generated API
Glide v4 使用 注解处理器 (Annotation Processor) 来生成出一个 API,在 Application 模块中可使用该流式 API 一次性调用到 RequestBuilder
, RequestOptions
和集成库中所有的选项。
Generated API 模式的设计出于以下两个目的:
- 集成库可以为 Generated API 扩展自定义选项。
- 在 Application 模块中可将常用的选项组打包成一个选项在 Generated API 中使用
虽然以上所说的工作均可以通过手动创建 RequestOptions
子类的方式来完成,但想将它用好更具有挑战,并且降低了 API 使用的流畅性。
GlideApp.with(fragment)
.load(myUrl)
.placeholder(R.drawable.placeholder)
.fitCenter()
.into(imageView);
性能
Glide 充分考虑了Android图片加载性能的两个关键方面:
- 图片解码速度
- 解码图片带来的资源压力
为了让用户拥有良好的App使用体验,图片不仅要快速加载,而且还不能因为过多的主线程I/O或频繁的垃圾回收导致页面的闪烁和抖动现象。
Glide使用了多个步骤来确保在Android上加载图片尽可能的快速和平滑:
- 自动、智能地下采样(downsampling)和缓存(caching),以最小化存储开销和解码次数;
- 积极的资源重用,例如字节数组和Bitmap,以最小化昂贵的垃圾回收和堆碎片影响;
- 深度的生命周期集成,以确保仅优先处理活跃的Fragment和Activity的请求,并有利于应用在必要时释放资源以避免在后台时被杀掉。
结构
Glide构造
-
Glide.with()
作用:初始化glide,返回一个RequestManger对象
以传入的是FragmentActivity为例,getRetriever (activity)流程如下:
initializeGlide主要做了以下工作:
- 获取应用中带注解的GlideModule(annotationGeneratedModule),这里要解释一下GlideModule:用户自定义glide配置模块,用来修改默认的glide配置信息。如果这个为空或者可配置menifest里面的标志为true,则获取menifest里面配置的GlideModule模块(manifestModules)。
- 把manifestModules以及annotationGeneratedModule里面的配置信息放到builder里面(applyOptions)替换glide默认组件(registerComponents)
各种初始化信息Glide glide = builder.build(applicationContext);
看一下build的源码,主要做了以下工作: - 创建请求图片线程池sourceExecutor,创建硬盘缓存线程池diskCacheExecutor。动画线程池animationExecutor
- 依据设备的屏幕密度和尺寸设置各种pool的size
- 创建图片线程池LruBitmapPool,缓存所有被释放的bitmap, LruBitmapPool依赖默认的缓存策略和缓存配置。缓存策略在API大于19时,为SizeConfigStrategy,小于为AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config为key,value为bitmap的HashMap
- 创建对象数组缓存池LruArrayPool,默认4M
- 创建LruResourceCache,内存缓存
- new glide里面 new Registry()注册管理任务执行对象的类(Registry),可以简单理解为:Registry是一个工厂,而其中所有注册的对象都是一个工厂员工,当任务分发时,根据当前任务的性质,分发给相应员工进行处理
先看代码
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
可以看到,with方法有很多,但内容基本一致,都是通过 RequestManagerRetriever.get(); 获取一个 RequestManagerRetriever 对象 retriever ,然后通过 retriever.get(context); 获取一个 RequestManager 对象并返回。这些with方法关键的不同在于传入的参数不一致,可以是Context、Activity、Fragment等等。那么为什么要分这么多种呢?其实我们应该都知道:Glide在加载图片的时候会绑定 with(context) 方法中传入的 context 的生命周期,如果传入的是 Activity ,那么在这个 Activity 销毁的时候Glide会停止图片的加载。这样做的好处是显而易见的:避免了消耗多余的资源,也避免了在Activity销毁之后加载图片从而导致的空指针问题。
初始化glide,单例模式
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
防止重复初始化
private static void checkAndInitializeGlide(@NonNull Context context) {
// In the thread running initGlide(), one or more classes may call Glide.get(context).
// Without this check, those calls could trigger infinite recursion.
if (isInitializing) {
throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
isInitializing = true;
initializeGlide(context);
isInitializing = false;
}
加载自定义的配置例如自定义的modeloader,网络加载栈等,注册ComponentCallbacks,在低内存时 memoryCache, bitmapPool ,arrayPool 释放到内存
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set> excludedModuleClasses =
annotationGeneratedModule.getExcludedModuleClasses();
Iterator iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
}
iterator.remove();
}
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
}
}
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
构建Glide,配置数据转换器/解码器/转码器/编码器
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptions defaultRequestOptions,
@NonNull Map, TransitionOptions, ?>> defaultTransitionOptions) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
registry = new Registry();
// Right now we're only using this parser for HEIF images, which are only supported on OMR1+.
// If we need this for other file types, we should consider removing this restriction.
// Note that order here matters. We want to check the ExifInterface parser first for orientation
// and then fall back to DefaultImageHeaderParser for other fields.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
registry.register(new ExifInterfaceImageHeaderParser());
}
registry.register(new DefaultImageHeaderParser());
Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
resources.getDisplayMetrics(), bitmapPool, arrayPool);
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
ResourceDecoder parcelFileDescriptorVideoDecoder =
VideoDecoder.parcel(bitmapPool);
ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
ResourceDrawableDecoder resourceDrawableDecoder =
new ResourceDrawableDecoder(context);
ResourceLoader.StreamFactory resourceLoaderStreamFactory =
new ResourceLoader.StreamFactory(resources);
ResourceLoader.UriFactory resourceLoaderUriFactory =
new ResourceLoader.UriFactory(resources);
ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
new ResourceLoader.FileDescriptorFactory(resources);
ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
new ResourceLoader.AssetFileDescriptorFactory(resources);
BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);
BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();
ContentResolver contentResolver = context.getContentResolver();
registry
.append(ByteBuffer.class, new ByteBufferEncoder())
//省略很多行
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
engine,
logLevel);
}
模型转换器
转换器 | 功能 |
---|---|
ResourceLoader.StreamFactory | 将Android资源ID转换为Uri,在加载成为InputStream |
ResourceLoader.UriFactory | 将资源ID转换为Uri |
ResourceLoader.FileDescriptorFactory | 将资源ID转化为ParcelFileDescriptor |
ResourceLoader.AssetFileDescriptorFactory | 将资源ID转化为AssetFileDescriptor |
UnitModelLoader.Factory | 不做任何转换,返回源数据 |
ByteBufferFileLoader.Factory | 将File转换为ByteBuffer |
FileLoader.StreamFactory | 将File转换为InputStream |
FileLoader.FileDescriptorFactory | 将File转化为ParcelFileDescriptor |
DataUrlLoader.StreamFactory | 将Url转化为InputStream |
StringLoader.StreamFactory | 将String转换为InputStream |
StringLoader.AssetFileDescriptorFactory | 将String转换为AssetFileDescriptor |
HttpUriLoader.Factory | 将http/https Uri转换为InputStream |
UriLoader.StreamFactory | 将Uri转换为InputStream |
UriLoader.FileDescriptorFactory | 将Uri转换为ParcelFileDescriptor |
UriLoader.AssetFileDescriptorFactory | 将Uri转换为AssetFileDescriptor |
UrlUriLoader.StreamFactory | 将将http/https的Uri转换为InputStream |
UrlLoader.StreamFactory | 将Url转换为InputStream |
HttpGlideUrlLoader.Factory | 将HttpGlide转换为InputStream |
解码器
解码器 | 功能 |
---|---|
ByteBufferGifDecoder | 将ByteBuffer解码为GifDrawable |
ByteBufferBitmapDecoder | 将ByteBuffer解码为Bitmap |
ResourceDrawableDecoder | 将资源Uri解码为Drawable |
ResourceBitmapDecoder | 将资源ID解码为Bitmap |
BitmapDrawableDecoder | 将数据解码为BitmapDrawable |
StreamBitmapDecoder | 将InputStreams解码为Bitmap |
StreamGifDecoder | 将InputStream数据转换为BtyeBuffer,再解码为GifDrawable |
GifFrameResourceDecoder | 解码gif帧 |
FileDecoder | 包装File成为FileResource |
UnitDrawableDecoder | 将Drawable包装为DrawableResource |
UnitBitmapDecoder | 包装Bitmap成为BitmapResource |
VideoDecoder | 将本地视频文件解码为Bitmap |
编码器
编码器 | 功能 |
---|---|
ByteBufferEncoder | 将Byte数据缓存为File |
StreamEncoder | InputStream缓存为File |
BitmapEncoder | 将Bitmap数据缓存为File |
BitmapDrawableEncoder | 将BitmapDrawable数据缓存为File |
GifDrawableEncoder | 将GifDrawable数据缓存为File |
Glide的加载流程可以概括为以下流程:
model(数据源)-->data(转换数据)-->decode(解码)-->transformed(缩放)-->transcoded(转码)-->encoded(编码保存到本地)
Glide 生命周期管理
- ComponentCallbacks
public interface ComponentCallbacks2{
//当低内存时回调,
void onTrimMemory(int );
}
当回调onTrimMemory(int level)执行以下代码,
释放 memoryCache bitmapPool arrayPool 一些对象
public void trimMemory(int level) {
// Engine asserts this anyway when removing resources, fail faster and consistently
Util.assertMainThread();
// memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687.
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
- 与activity生命周期绑定,在Activity 添加一个不可见的RequestManagerFragment,监听其生命周期来决定当前加载任务需不需要取消
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;
}
- 如何加载GIF 文件
注意看GifFrameLoader,注意逻辑在这里,用handle不断loadNextFrame;
SourceGenerator-->HttpUrlFetcher-->inputstream (网络获取数据)-> lrudiskcache 保存到本地-->DataCacheGenerator-->ByteBufferFetcher-->ByteBuffer-->byteBufferGifDecoder-->GifDrawableResource-->GifDrawable --> GifFrameLoader (这里面就开始循坏显示图片)