Glide Resource体系
Resource是Glide加载各种资源统一抽象化,从而在内部表现为统一的格式。
接口定义:
package com.bumptech.glide.load.engine;
import android.support.annotation.NonNull;
/**
* A resource interface that wraps a particular type so that it can be pooled and reused.
*
* @param <Z> The type of resource wrapped by this class.
*/
public interface Resource<Z> {
/**
* Returns the {@link Class} of the wrapped resource.
*/
@NonNull
Class<Z> getResourceClass();
/**
* Returns an instance of the wrapped resource.
*
* <p> Note - This does not have to be the same instance of the wrapped resource class and in fact
* it is often appropriate to return a new instance for each call. For example,
* {@link android.graphics.drawable.Drawable Drawable}s should only be used by a single
* {@link android.view.View View} at a time so each call to this method for Resources that wrap
* {@link android.graphics.drawable.Drawable Drawable}s should always return a new
* {@link android.graphics.drawable.Drawable Drawable}. </p>
*/
@NonNull
Z get();
/**
* Returns the size in bytes of the wrapped resource to use to determine how much of the memory
* cache this resource uses.
*/
int getSize();
/**
* Cleans up and recycles internal resources.
*
* <p> It is only safe to call this method if there are no current resource consumers and if this
* method has not yet been called. Typically this occurs at one of two times:
* <ul>
* <li>During a resource load when the resource is transformed or transcoded before any consumer
* have ever had access to this resource</li>
* <li>After all consumers have released this resource and it has been evicted from the cache
* </li>
* </ul>
*
* For most users of this class, the only time this method should ever be called is during
* transformations or transcoders, the framework will call this method when all consumers have
* released this resource and it has been evicted from the cache. </p>
*/
void recycle();
}
它的整个体系:
BitmapResource
整个就是对Bitmap的包装。注意2点:
1. bitmap的大小计算
public static int getBitmapByteSize(@NonNull Bitmap bitmap) {
// The return value of getAllocationByteCount silently changes for recycled bitmaps from the
// internal buffer size to row bytes * height. To avoid random inconsistencies in caches, we
// instead assert here.
if (bitmap.isRecycled()) {
throw new IllegalStateException("Cannot obtain size for recycled Bitmap: " + bitmap
+ "[" + bitmap.getWidth() + "x" + bitmap.getHeight() + "] " + bitmap.getConfig());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Workaround for KitKat initial release NPE in Bitmap, fixed in MR1. See issue #148.
try {
return bitmap.getAllocationByteCount();
} catch (@SuppressWarnings("PMD.AvoidCatchingNPE") NullPointerException e) {
// Do nothing.
}
}
return bitmap.getHeight() * bitmap.getRowBytes();
}
2. BitmapPool
为了以后更方便的使用BitmapResource,它在回收的时候其实是放入了BitmapPool池中。
@Override
public void recycle() {
bitmapPool.put(bitmap);
}
Pool池的内容将在后面的文章中分析。
SimpleResource
它的作用是对于那些资源对象,我们不知道大小,并且不能回收或关闭。
FileResource
它是SimpleResource
对象的具体实现者。
DrawableResource
这个是抽象类,代表的是所有Drawable的子对象。
Drawable就是一个可绘制的对象,里面保存的是可以绘制的数据,其可能是一张位图(BitmapDrawable),也可能是一个图形(ShapeDrawable),还可能只是一个颜色(ColorDrawable)等。
Drawable主要的功能就是给canvas上绘制图形,通过方法public void draw(Canvas canvas)
操作。
这里主要的知识点:
public final T get() {
@Nullable ConstantState state = drawable.getConstantState();
if (state == null) {
return drawable;
}
// Drawables contain temporary state related to how they're being displayed
// (alpha, color filter etc), so return a new copy each time.
// If we ever return the original drawable, it's temporary state may be changed
// and subsequent copies may end up with that temporary state. See #276.
return (T) state.newDrawable();
}
什么是ConstantState?
同一个drawable可能会被使用在很多不同的地方,android系统为了优化内存,对drawable抽闲出了共用的状态(即Constant state),这样虽然是在使用不同的Drawable对象,但是这些不同的对象包含了一些共用的属性。
下图展现了view、Drawable和constant state的关系:
上面的优化是对同一个资源创建Drawable才有的优化。
ConstantState带来的问题
ConstantState优化了内存上的消耗,但是会引发修改一个地方,导致所有同一个资源的地方也同时内修改了的问题。
大家可以试下如下的代码:
Drawable star = getResources().getDrawable(R.drawable.test);
star.setAlpha(10);
((ImageView) findViewById(R.id.one)).setImageDrawable(star);
Drawable star2 = getResources().getDrawable(R.drawable.test);
((ImageView) findViewById(R.id.one)).setImageDrawable(star2);
会发现,我只想修改图片1的透明度,但是图片2的透明度也发生了改变,这其实不是我们想要的,而导致这样的问题的是ConstantState造成的。
如何解决呢?
drawable为我们提供了解决mutate()
方法,就是对constant state进行一次拷贝,打破仅有一份constant state的束缚。
mutate()
是这样定义的:
/**
* Make this drawable mutable. This operation cannot be reversed. A mutable
* drawable is guaranteed to not share its state with any other drawable.
* This is especially useful when you need to modify properties of drawables
* loaded from resources. By default, all drawables instances loaded from
* the same resource share a common state; if you modify the state of one
* instance, all the other instances will receive the same modification.
*
* Calling this method on a mutable Drawable will have no effect.
*
* @return This drawable.
* @see ConstantState
* @see #getConstantState()
*/
public @NonNull Drawable mutate() {
return this;
}
ConstantState接口的定义
public static abstract class ConstantState {
/**
* 从ConstantState创建新的Drawable对象
*/
public abstract @NonNull Drawable newDrawable();
/**
* 带上特定资源的,从ConstantState创建新的Drawable对象, 适合有density-dependent属性的具体实现者
*/
public @NonNull Drawable newDrawable(@Nullable Resources res) {
return newDrawable();
}
/**
* 带上特定资源的和主题,从ConstantState创建新的Drawable对象, 适合有theme-dependent属性的具体实现者
*/
public @NonNull Drawable newDrawable(@Nullable Resources res, @Nullable @SuppressWarnings("unused") Theme theme) {
return newDrawable(res);
}
/**
* constant state是否可以有theme
*/
public boolean canApplyTheme() {
return false;
}
public abstract @Config int getChangingConfigurations();
}
一个实现了ConstantState的例子
BitmapDrawable
是Drawable的一个实现者,并且它实现了ConstantState
接口:
final static class BitmapState extends ConstantState {
final Paint mPaint;
// Values loaded during inflation.
int[] mThemeAttrs = null;
Bitmap mBitmap = null;
ColorStateList mTint = null;
Mode mTintMode = DEFAULT_TINT_MODE;
int mGravity = Gravity.FILL;
float mBaseAlpha = 1.0f;
Shader.TileMode mTileModeX = null;
Shader.TileMode mTileModeY = null;
// The density to use when looking up the bitmap in Resources. A value of 0 means use
// the system's density.
int mSrcDensityOverride = 0;
// The density at which to render the bitmap.
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mAutoMirrored = false;
@Config int mChangingConfigurations;
boolean mRebuildShader;
BitmapState(Bitmap bitmap) {
mBitmap = bitmap;
mPaint = new Paint(DEFAULT_PAINT_FLAGS);
}
BitmapState(BitmapState bitmapState) {
mBitmap = bitmapState.mBitmap;
mTint = bitmapState.mTint;
mTintMode = bitmapState.mTintMode;
mThemeAttrs = bitmapState.mThemeAttrs;
mChangingConfigurations = bitmapState.mChangingConfigurations;
mGravity = bitmapState.mGravity;
mTileModeX = bitmapState.mTileModeX;
mTileModeY = bitmapState.mTileModeY;
mSrcDensityOverride = bitmapState.mSrcDensityOverride;
mTargetDensity = bitmapState.mTargetDensity;
mBaseAlpha = bitmapState.mBaseAlpha;
mPaint = new Paint(bitmapState.mPaint);
mRebuildShader = bitmapState.mRebuildShader;
mAutoMirrored = bitmapState.mAutoMirrored;
}
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null || mTint != null && mTint.canApplyTheme();
}
@Override
public Drawable newDrawable() {
return new BitmapDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new BitmapDrawable(this, res);
}
@Override
public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
}
BitmapDrawable
对应实现的mutate()
方法:
/**
* A mutable BitmapDrawable still shares its Bitmap with any other Drawable
* that comes from the same resource.
*
* @return This drawable.
*/
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mBitmapState = new BitmapState(mBitmapState);
mMutated = true;
}
return this;
}
BitmapDrawableResource
GifDrawableResource
专门针对gif的Drawable对象
NonOwnedDrawableResource
不关心Drawable大小,并且对于资源的回收是否有好处也不确定。
LockedResource
相当于给资源加了个锁,只有在锁调用了unlock
之后才能真正执行资源的回收,否则会一直阻塞。
LockedResource
里维护了一个对象池,这个在FactoryPools类专门介绍。
它的使用:
final class LockedResource<Z> implements Resource<Z>,
FactoryPools.Poolable {
private static final Pools.Pool<LockedResource<?>> POOL = FactoryPools.threadSafe(20,
new FactoryPools.Factory<LockedResource<?>>() {
@Override
public LockedResource<?> create() {
return new LockedResource<Object>();
}
});
@SuppressWarnings("unchecked")
@NonNull
static <Z> LockedResource<Z> obtain(Resource<Z> resource) {
LockedResource<Z> result = Preconditions.checkNotNull((LockedResource<Z>) POOL.acquire());
result.init(resource);
return result;
}
synchronized void unlock() {
stateVerifier.throwIfRecycled();
if (!isLocked) {
throw new IllegalStateException("Already unlocked");
}
this.isLocked = false;
if (isRecycled) {
recycle();
}
}
@Override
public synchronized void recycle() {
stateVerifier.throwIfRecycled();
this.isRecycled = true;
if (!isLocked) {
toWrap.recycle();
release();
}
}
}
EngineResource
带引用计数的的资源,具体的使用暂时不清楚。