view的context一定是Activity吗

最近在使用glide加载图片的时候出现“Caused by: java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity”,但明明在使用glide之前已经进行了Activity是否destroy的判断,为什么还会发生这个crash呢?注意到Android版本为4.4.2,对应crash中glide使用的context为view.getContext(),所以是不是因为view.getContext()不是Activity类型,所以没有走到判断逻辑,debug发现此时的view.getContext()为TintContextWrapper类型,这是为什么呢?

源码分析

我的view继承android.support.v7.widget包下的AppCompatImageView,查看AppCompatImageView源码如下:
在这里插入图片描述
可以看到在AppcompatImageView中的super方法中对于原本传入的context进行了封装也就是TintContextWrapper.wrap(context),查看TintContextWrapper.wrap(context)方法源码如下:

public class TintContextWrapper extends ContextWrapper {
    private static final Object CACHE_LOCK = new Object();
    private static ArrayList<WeakReference<TintContextWrapper>> sCache;
    private final Resources mResources;
    private final Theme mTheme;

    public static Context wrap(@NonNull Context context) {
        if (shouldWrap(context)) {
            synchronized(CACHE_LOCK) {
                if (sCache == null) {
                    sCache = new ArrayList();
                } else {
                    int i;
                    WeakReference ref;
                    for(i = sCache.size() - 1; i >= 0; --i) {
                        ref = (WeakReference)sCache.get(i);
                        if (ref == null || ref.get() == null) {
                            sCache.remove(i);
                        }
                    }

                    for(i = sCache.size() - 1; i >= 0; --i) {
                        ref = (WeakReference)sCache.get(i);
                        TintContextWrapper wrapper = ref != null ? (TintContextWrapper)ref.get() : null;
                        // 将context赋值给了baseContext
                        if (wrapper != null && wrapper.getBaseContext() == context) {
                            return wrapper;
                        }
                    }
                }

                TintContextWrapper wrapper = new TintContextWrapper(context);
                sCache.add(new WeakReference(wrapper));
                return wrapper;
            }
        } else {
            return context;
        }
    }
    // 判断是否封装context
    private static boolean shouldWrap(@NonNull Context context) {
        if (!(context instanceof TintContextWrapper) && !(context.getResources() instanceof TintResources) && !(context.getResources() instanceof VectorEnabledTintResources)) {
            return VERSION.SDK_INT < 21 || VectorEnabledTintResources.shouldBeUsed();
        } else {
            return false;
        }
    }
}

可以看出来TintContextWrapper继承ContextWrapper。在wrap方法中根据shouldWrap(context)进行判断。
(1)shouldWrap(context)为false,直接返回外部传入的context,未进行封装。
(2)shouldWrap(context)为true,将外部传入的context赋值给TintContextWrapper的baseContext,封装成TintContextWrapper。
查看
shouldWrap
方法,VERSION.SDK_INT < 21(Android5.0)时会返回true,封装context。

总结:对于view的context,android5.0以下,使用v7包下的view,对应的view的context为TintContextWrapper类型,其余时为activity类型。

view的context获取Activity

从上面得知,view的context不一定是Activity,那我们怎么从view的context得到对应的Activity呢?
方法如下:

private fun getActivityFromView(viewContext: Context): Activity? {
    var context = viewContext
    while (context is ContextWrapper) {
        if (context is Activity) {
            return context
        }
        context = context.baseContext
    }
    return null
}

参考

https://blog.youkuaiyun.com/yyanjun/article/details/79896677
https://www.jianshu.com/p/448c578a252b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值