Android获取Drawable对象方式为getResources().getDrawable(@DrawableResId int res), 只可能返回***Drawable、BitmapDrawable对象, 当然id不存在会抛异常。
public abstract class Drawable {
...
}
在/framework/base/graphics/java/android/graphics目录下有很多Drawable派生类。
下面讲解下返回对象是如何产生的, 核心逻辑在ResourcesImpl.java
private final DrawableCache mDrawableCache = new DrawableCache(); //缓存的是ConstantState抽象类的对象,而不是Drawable对象; 弱引用,不影响GC
private final DrawableCache mColorDrawableCache = new DrawableCache(); //弱引用,不影响GC
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
throws NotFoundException {
final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;
//getDrawableForDensity(id, 0, theme);density为0, 所以useCache默认为true,
//app取自己的drawable时mPreloading为false, 即下面的if条件为真
if (!mPreloading && useCache) {
final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
if (cachedDrawable != null) {
cachedDrawable.setChangingConfigurations(value.changingConfigurations);
return cachedDrawable;
}
}
不要被上面的caches.getInstance误导, 它创建了新的实例(newDrawble方法). 但是复用了ConstantState, 如果是BitmapDrawable的话就复用了Bitmap对象。
public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
final Drawable.ConstantState entry = get(key, theme);
if (entry != null) {
return entry.newDrawable(resources, theme);
}
return null;
}
public Drawable newDrawable() {
return new BitmapDrawable(this, null);
}
做个练习:
BitmapDrawable firstDrawable = (BitmapDrawable) getResources()
.getDrawable(R.drawable.school_picture);
BitmapDrawable secondDrawable = (BitmapDrawable) getResources()
.getDrawable(R.drawable.school_picture);
2个drawable实例指向的是同一个Bitmap对象(其实是Bitmap
State), 在操作其中一个drawble的时候, 另一个实例也会受影响。
如果想2个drawable互不影响, 即使用不同的ConstantState实例。 可以调用BitmapDrawable.java的mutate方法:
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mBitmapState = new BitmapState(mBitmapState);
mMutated = true;
}
return this;
}
即使用新的ConstantState实例替换mBitmapState参数值。
ResouresesImpl.java是通过文件后缀判断Drawable类型的, 如果后缀是xml则返回ColorDrawable对象, 否则返回BitmapDrawable对象。
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
int id, int density) {
...
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
AssetInputStream ais = (AssetInputStream) is;
dr = decodeImageDrawable(ais, wrapper, value); //返回BitmapDrawable
}
private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
@NonNull Resources wrapper, @NonNull TypedValue value) {
ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
wrapper, value);
try {
return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
});
} catch (IOException ioe) {
// This is okay. This may be something that ImageDecoder does not
// support, like SVG.
return null;
}
}
public static Drawable decodeDrawable(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
Bitmap bitmap = decodeBitmap(src, listener);
**return new BitmapDrawable(src.getResources(), bitmap);**
}
后缀是xml时,执行了Drawable.createFromXmlForDensity方法, 最终会执行到DrawableInflator.java的inflateFromTag方法, 返回上面graphics文件夹下面的类。。
private Drawable inflateFromTag(@NonNull String name) {
switch (name) {
case "selector":
return new StateListDrawable();
case "animated-selector":
return new AnimatedStateListDrawable();
case "level-list":
return new LevelListDrawable();
case "layer-list":
return new LayerDrawable();
case "transition":
return new TransitionDrawable();
case "ripple":
return new RippleDrawable();
case "adaptive-icon":
return new AdaptiveIconDrawable();
case "color":
return new ColorDrawable();
case "shape":
return new GradientDrawable();
case "vector":
return new VectorDrawable();
case "animated-vector":
return new AnimatedVectorDrawable();
case "scale":
return new ScaleDrawable();
case "clip":
return new ClipDrawable();
case "rotate":
return new RotateDrawable();
case "animated-rotate":
return new AnimatedRotateDrawable();
case "animation-list":
return new AnimationDrawable();
case "inset":
return new InsetDrawable();
case "bitmap":
return new BitmapDrawable();
case "nine-patch":
return new NinePatchDrawable();
case "animated-image":
return new AnimatedImageDrawable();
default:
return null;
}
}
小结:
1、 getResources().getDrawable只可能返回Drawable(根据xml里的tag生成不同实例)、BitmapDrawable或者抛异常;
2、 在ResourcesImpl.java里缓存了ConstantState弱引用, 对应BitmapDrawable对象来说缓存了Bitmap引用。
3、通过调用drawable的mutate方法可以创建新的ConstantState对象。
4、ResourcesImpl.java通过文件名后缀返回不同的drawable对象, 后缀是xml时根据文件里的tag名称返回Drawable对象,其它情况返回BitmapDrawable对象;
5、 在Activity、View退出时不需要解绑Drawable, 即ImageView的setImageDrawable(null)。 因为ResourcesImpl缓存的是Bitmap弱引用,可以被GC回收;