之前查看页面启动耗时时候,发现一件事,这个页面在style中设置了windowBackground,首次启动该页面,发现有个getDrawable方法耗时上百毫秒,第二次进入却只有几十毫秒,退出该页面,手动gc后又是耗时上百毫秒,以此决定看看AppCompatActivity的onCreate流程。
AppCompatActivity:
@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}复制代码
@NonNullpublic AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
return mDelegate;
}复制代码
getDelegate()获取的是AppCompateDelegateImpl类,installViewFactory()就是给LayoutInflater设置Factory2方法,也就是把一系列控件改为AppCompat控件
AppCompateDelegateImpl:
@Override
public void onCreate(Bundle savedInstanceState) {
mBaseContextAttached = true;
applyDayNight(false);
ensureWindow();
//省略。。
mCreated = true;
}复制代码
主要是ensureWidow方法进去,来到下面这个方法:
AppCompateDelegateImpl:
private void attachToWindow(@NonNull Window window) {
//省略。。。
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
mContext, null, sWindowBackgroundStyleable);
final Drawable winBg = a.getDrawableIfKnown(0);
if (winBg != null) {
// Now set the background drawable
window.setBackgroundDrawable(winBg);
a.recycle();
mWindow = window;
}复制代码
这里去获取windowBackground的TypedArray, 然后a.getDrawableIfKnown(0)获取drawable(如果有),获取和缓存应该都在这方法里:
TintTypedArray:
public Drawable getDrawableIfKnown(int index) {
if (mWrapped.hasValue(index)) {
final int resourceId = mWrapped.getResourceId(index, 0);
if (resourceId != 0) {
return AppCompatDrawableManager.get().getDrawable(mContext, resourceId, true);
return null;
}复制代码
AppCompatDrawableManager:
synchronized Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
boolean failIfNotKnown) {
return mResourceManager.getDrawable(context, resId, failIfNotKnown);
}复制代码
ResourceManagerInternal:
//这就是存放缓存的map
private final WeakHashMap<Context, LongSparseArray<WeakReference<ConstantState>>>
mDrawableCaches = new WeakHashMap<>(0);
synchronized Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
boolean failIfNotKnown) {
checkVectorDrawableSetup(context);
//这里解析vector,animated-vector,animated-selector
Drawable drawable = loadDrawableFromDelegates(context, resId);
if (drawable == null) {
主要看这个方法
drawable = createDrawableIfNeeded(context, resId);
if (drawable == null) {
drawable = ContextCompat.getDrawable(context, resId);
if (drawable != null) {
// Tint it if needed
drawable = tintDrawable(context, resId, failIfNotKnown, drawable);
if (drawable != null) {
// See if we need to 'fix' the drawable
DrawableUtils.fixDrawable(drawable);
return drawable;
}复制代码
ResourceManagerInternal:
private Drawable createDrawableIfNeeded(@NonNull Context context,
@DrawableRes final int resId) {
//省略。。。
final long key = createCacheKey(tv);
//这里获取到对应的drawable
Drawable dr = getCachedDrawable(context, key);
if (dr != null) {
// If we got a cached drawable, return it
return dr;
// Else we need to try and create one...
dr = (this.mHooks == null) ? null
: this.mHooks.createDrawableFor(this, context, resId);
if (dr != null) {
dr.setChangingConfigurations(tv.changingConfigurations);
//把drawable添加进缓存
addDrawableToCache(context, key, dr);
return dr;
}复制代码
ResourceManagerInternal:
private synchronized Drawable getCachedDrawable(@NonNull final Context context,
final long key) {
final LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context);
if (cache == null) {
return null;
final WeakReference<ConstantState> wr = cache.get(key);
if (wr != null) {
// We have the key, and the secret
ConstantState entry = wr.get();
if (entry != null) {
return entry.newDrawable(context.getResources());
} else {
// Our entry has been purged
cache.delete(key);
return null;
}复制代码
上述代码就是从缓存获取到drawable过程了,存的时候同理,这里主要存的是ConstantState对象,这个类接触不多,借用网上的解释就是:每个 Drawable 类对象类都关联有一个 ConstantState 类对象,这是为了保存 Drawable 类对象的一些恒定不变的数据,如果从同一个 res 中创建的 Drawable 类对象,为了节约内存,它们会共享同一个 ConstantState 类对象。
到这里drawable的缓存就结束了,由于弱引用,gc可能就回收掉了,但是这个缓存应该不止用到window背景,所以我有找了下,发现基本所有AppCompat控件都用到了这个缓存。
这里只是我的一点小理解,如有错误,希望可以指出。