前言
关于Glide的优缺点和使用,这里不在阐述,具体请看GitHub上官方介绍。这里主要结合别人的文章和自己看源码的一些见解,分享一下自己的理解。Glide庞大的图片库做的封装性极好支持的格式也很多,开发者的代码设计严格遵守了软件设计的六大原则,一开始看的时候感觉结构好麻烦,好多接口,好多回调,后来细想了一下这些都是软件设计的六大原则约束,带来的好处就是低耦合,易维护,等等很多优点,我会在分析的时候讲一下里边设计的设计模式。
整个Glide的流程图
如下是一张大概的流程图
Glide时序图.png
发现的问题
自定义AppGlideModule中applyOptions方法设置默认图片格式PREFER_RGB_565无效,查看源码会发现在glide在解码图片的时候,会根据解析图片头得到的图片格式设置解码格式:Png格式图片设置为PREFER_ARGB_8888,jpg格式的图片设置为PREFER_RGB_565。
-验证方法
@GlideModule
public class CustomGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
//设置格式为565
builder.setDefaultRequestOptions(RequestOptions.formatOf(DecodeFormat.PREFER_RGB_565));
}
}
在MainActivity中通过如下方式打印一下图片的格式
Glide.with(this).asBitmap().addListener(new RequestListener<Bitmap>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
Bitmap.Config config = resource.getConfig();
Log.i("jawe", "onResourceReady: config="+config);
return false;
}
}).load("http://d.lanrentuku.com/down/png/1904/international_food/fried_rice.png").into(imageView);
这里打印出来的config就是ARGB_8888
-查看源码
具体在Downsample.java的calculateConfig方法内
private void calculateConfig(
....){
....
...
boolean hasAlpha = false;
try {
hasAlpha = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool).hasAlpha();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(
TAG,
"Cannot determine whether the image has alpha or not from header"
+ ", format "
+ format,
e);
}
}
//这里根据是否有透明度设置解码格式
optionsWithScaling.inPreferredConfig =
hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
if (optionsWithScaling.inPreferredConfig == Config.RGB_565) {
optionsWithScaling.inDither = true;
}
}
主流程分析
一般的使用如下:
Glide.with(this).load("http://d.lanrentuku.com/down/png/1904/international_food/fried_rice.png") .apply(new RequestOptions().error(R.drawable.ic_launcher_background).placeholder (R.mipmap.ic_launcher).override(500, 500)).into(iv);
越是看起来很简单的代码,背后的封装性很复杂,下面我们就一步步分析一下
Glide.with做的事情
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
//1.getRetriever(activity)得到RequestManager检索器
//2.通过检索器得到一个RequestManager
return getRetriever(activity).get(activity);
}
@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.
return Glide.get(context).getRequestManagerRetriever();
}
其中Glide.get(context)得到的是Glide单例对象,然后getRequestManagerRetriever();得到检索器
private static volatile Glide glide;
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
这里采用的双重校验加锁(DCL)的方式实现单例模式,其中glide使用volatile修饰加入内存屏障,禁止指令重排序,使得其他线程的修改能被直接读取最新值,即高并发时候线程的可见性。
Glide在V4中推荐使用注解的方式配置Glide选项,Glide在创建Glide对象的时候会根据编译期生成的RequestManagerRetriever.RequestManagerFactory设置Glide然后调用Glide glide = builder.build(applicationContext);创建Glide对象。
private static void initializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
...
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
...
Glide glide = builder.build(applicationContext);
...
Glide.glide = glide;
这里边其中GlideBuilder内部实现了很多默认的Glide配置,我们一起看看
/** A builder class for setting default structural classes for Glide to use. */
@SuppressWarnings("PMD.ImmutableField")
public final class GlideBuilder {
private final Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions = new ArrayMap<>();
private Engine engine;//加载引擎
private BitmapPool bitmapPool;//bitmap复用池
private ArrayPool arrayPool;//array复用池
private MemoryCache memoryCache;//内存缓存
private GlideExecutor sourceExecutor;//source线程池
private GlideExecutor diskCacheExecutor;//磁盘缓存线程池
private DiskCache.Factory diskCacheFactory;//磁盘缓存工厂
private MemorySizeCalculator memorySizeCalculator;//缓存大小计算器
private ConnectivityMonitorFactory connectivityMonitorFactory;
private int logLevel = Log.INFO;
private RequestOptionsFactory defaultRequestOptionsFactory =
new RequestOptionsFactory() {
@NonNull
@Override
public RequestOptions build() {
return new RequestOptions();
}
};
@Nullable private RequestManagerFactory requestManagerFactory;//requestManager工厂
private GlideExecutor animationExecutor;
...
}
通过注释可以很清楚的知道这个类的作用就是用来构建Glide,这里也可以看出使用了构建者模式(builder),将复杂对象的表示和它的实现分离。
我们看一下build方法
Glide build(@NonNull Context context) {
//GlideExecutor创建线程池,这里我把GlideExecutor理解是一个工具类
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
...
...
//根据设备内存大小计算最优的内存,复用池,磁盘缓存大小等
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
......
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);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps,
hardwareBitmapFdLimit,
minHardwareDimension);
}
这里体现了设计模式种的单一职责模式,builder里边主要做的事情:
1.根据不同的任务特性结合设备的cpu等特征设置不同的线程池
2.根据设备的内存和磁盘大小设置最优的内存和磁盘大小,以及构建复用池
3.构建加载引擎(Engine)
4.构建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 RequestOptionsFactory defaultRequestOptionsFactory,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
boolean isLoggingRequestOriginsEnabled,
boolean isImageDecoderEnabledForBitmaps,
int hardwareBitmapFdLimit,
int minHardwareDimension) {
...
registry
/** 编码器 磁盘缓存使用的*/
.append(ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps 注册解码器 使用byteBufferBitmapDecoder从ByteBuffer 解码成bitmap */
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
.append(
Registry.BUCKET_BITMAP,
ParcelFileDescriptor.class,
Bitmap.class,
parcelFileDescriptorVideoDecoder)
.append(
Registry.BUCKET_BITMAP,
AssetFileDescriptor.class,
Bitmap.class,
VideoDecoder.asset(bitmapPool))
.append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.<Bitmap>getInstance())
.append(Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
/**注册 bitmap的编码器 用于磁盘缓存*/
.append(Bitmap.class, bitmapEncoder)
/* BitmapDrawables */
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ByteBuffer.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
InputStream.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ParcelFileDescriptor.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
/* GIFs */
.append(
Registry.BUCKET_GIF,
InputStream.class,
GifDrawable.class,
new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
.append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.append(GifDrawable.class, new GifDrawableEncoder())
/* GIF Frames */
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(
GifDecoder.class, GifDecoder.class, UnitModelLoader.Factory.<GifDecoder>getInstance())
.append(
Registry.BUCKET_BITMAP,
GifDecoder.class,
Bitmap.class,
new GifFrameResourceDecoder(bitmapPool))
/* Drawables */
.append(Uri.class, Drawable.class, resourceDrawableDecoder)
.append(
Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitmapPool))
/* Files 文件解码器 */
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
/* Models 网络 */
.register(new InputStreamRewinder.Factory(arrayPool))
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
.append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
.append(Integer.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
.append(Integer.class, Uri.class, resourceLoaderUriFactory)
.append(int.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)
.append(Integer.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)
.append(int.class, Uri.class, resourceLoaderUriFactory)
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(
String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(
Uri.class,
ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(contentResolver))
.append(
Uri.class,
AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
.append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
.append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
.append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
/* Transcoders 转码器 */
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
.register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
.register(
Drawable.class,
byte[].class,
new DrawableBytesTranscoder(
bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
...
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
构造方法内部核心就是把所需要的编码器,解码器,加载模型,转码器都注册到注册表中,以供后边加载图片 缓存图片的时候使用。这里解释一下Glide中的组件以及作用:
1.ModelLoader, 将自定义的 Model(Url,File, Uri,任意的 POJO )加载成 Data(InputStreams, FileDescriptors)。
2.ResourceDecoder, 将 Data 类型(InputStreams, FileDescriptors)进行解码成新的 Resources(Drawables, Bitmaps)
3.Encoder, 用于向 Glide 的磁盘缓存写 Data (InputStreams, FileDesciptors)。
4.ResourceTranscoder,用于在不同的资源类型之间做转换,例如,从 BitmapResource 转换为 DrawableResource 。
5.ResourceEncoder,用于向 Glide 的磁盘缓存写 Resources(BitmapResource, DrawableResource)。
RequestManagerRetriever#get(activity)
构建RequestManger对象
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
这里是在主线程中执行的,所以执行else条件
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(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;
}
这里可以看出RequestManager 绑定一个SupportRequestManagerFragment
@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;
}
当前的页面绑定一个隐藏无界面的Fragment,来感知组件的生命周期,这样做的目的是避免内存泄漏,如果activity页面中使用Glide加载一张图片,activity被用户关掉了,但是Glide单例中的ViewTarget还在引用ImageView对象,而ImageView对象又引用activity对象,那么这个activity对象就不会被回收掉,也就是内存泄漏了。这里可以看出一个fragmentManager只有一个fragment。
注意这里有一个疑问,为什么要用handler发送消息的形式移除fm,这里看起来没有线程切换,完全可以直接移除。关于这个问题我问了我的同事,同事的解释是正确的,因为commitAllowingStateLoss 这个事务是异步执行的,为了保证一个fragmentManager只有一个绑定的SupportRequestManagerFragment,所以添加一个集合控制逻辑,当第一次提交事务 异步执行的时候,如果用户接着第二次get的时候,findFragmentByTag是null(前边的fragment还没有挂载到activity上)这时候从集合中查到了,就返回去。但是如果不从集合中移出去就会内存泄漏,为了保证是在挂载成功后,从集合中移掉fm,所以使用handler发送消息,利用handler的消息机制,消息队列排队取出消息处理的,所以移除的动作是在commit后执行的。
RequestManager
- 介绍:RequestManager是Glide中管理和启动Request的一个类,可以感知组件生命周期,不同的周期采用不同的管理策略。
- 分析:前边的分析看到RequestManager是通过工厂构建产生的,注解生成默认的工厂是new方式创建对象
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);//1
}
lifecycle.addListener(connectivityMonitor);
defaultRequestListeners =
new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners());
setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());
glide.registerRequestManager(this);
}
这里最重要的就是注释1处,把requestManger当前对象添加到lifecycle的监听接口中,这个lifecycle就是前边在构建SupportRequestManagerFragment对象的时候构建出来的ActivityFragmentLifecycle,而ActivityFragmentLifecycle 就是一个观察者,观察Fragment的生命周期,然后通知所有的订阅者(LifecycleListener),订阅者其中就有RequestManager,看一下ActivityFragmentLifecycle
class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners =
Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;
...
@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
...
}
其中addListener内部就像一个粘性事件一样,添加订阅的时候,能够订阅到订阅前的信息。
内存管理
Android是一个内存很敏感的应用,当系统内存不足的时候,就会杀死一些进程回收内存。为了让应用存活更久,Glide做了内存优化,其中Glide 实现了ComponentCallbacks2接口并且在onTrimMemory(int level)中根据不同的级别做出相应的内存管理。
public void trimMemory(int level) {
// Engine asserts this anyway when removing resources, fail faster and consistently
Util.assertMainThread();
// Request managers need to be trimmed before the caches and pools, in order for the latter to
// have the most benefit.
for (RequestManager manager : managers) {
manager.onTrimMemory(level);
}
// 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);
}
总结
Glide.with(activity)采用的外观设计模式,创建Request Manger对象 能够根据组件生命周期的不同阶段,来暂停,启动,停止请求,请求的处理流程requestTrack中管理的。Glide.with的流程图如下:
Glide.with.png
网友评论