之前查看页面启动耗时时候,发现一件事,这个页面在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控件都用到了这个缓存。
这里只是我的一点小理解,如有错误,希望可以指出。