Android开发

原来windowBackground的drawable被缓存了

本文主要是介绍原来windowBackground的drawable被缓存了,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

之前查看页面启动耗时时候,发现一件事,这个页面在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控件都用到了这个缓存。

这里只是我的一点小理解,如有错误,希望可以指出。


这篇关于原来windowBackground的drawable被缓存了的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!