登录

去注册

登录

注册

去登录

注册

每日一问 View中的getContext一定返回的是Activity对象吗?

xiaoyang   2019-06-23   收藏

如果不是,那什么场景下不是。

31

不一定是。

那么,在什么场景下不是呢:

除了自己手动传不是Activity的Context进去之外,还有一种情况,就是:

当使用AppCompatActivity 时。

我们都知道,在这个Activity里的原生控件(如TextView, ImageView等等),当在LayoutInflater中把xml解析成View的时候,最终会经过AppCompatViewInflater的createView方法: 把这些原生控件都变成 AppCompatXXX 一类的! 比如TextView的话,就会变成AppCompatTextView, ImageView会变成AppCompatImageView 。
当然了,这些AppCompat开头的,都是继承于被转换的那个对象的。

那重点就在这些AppCompat开头的控件了,随便打开一个他们源码,比如AppCompatImageView,
打开之后会看到: 当它们调用父类的构造方法时,调用了TintContextWrapper的wrap方法,并把Context传进去了!
看这个方法的名字, wrap很明显就是包装的意思嘛,点进去wrap方法看,还会看到首先调用了shouldWrap方法: 检查一下这个context应不应该被包装。
如果方法返回true, 会创建一个TintContextWrapper对象(把Context传进去),然后返回,那么,这时候,当我们调用这个View的getContext方法,自然就不是Activity了,而是它传进去的TintContextWrapper。

那么,究竟什么情况下,shouldWrap方法会返回true呢(Context会被包装), 点开看下源码:

  • 如果它已经被包装过了,那么就不需要继续包装,即返回false了。
  • 如果没有被包装过,并且Build.VERSION.SDK_INT < 21(也就是5.0之前的版本),就会返回true。

得出结论:

当运行在5.0系统版本以下的手机,并且Activity是继承自AppCompatActivity的,那么View的getConext方法,返回的就不是Activity而是TintContextWrapper.


回复
lxykad : @陈小缘 

讲的真好,总结的真好

2019-06-24 回复
DaveBoy : @陈小缘 

小缘请正面接受鸿洋的夸奖

2019-06-21 回复
4

先做个大胆的猜想, View.getContext() 不可能一定返回 Activity 对象,不然干啥不用 View.getActivity() 方法来代替。

至于什么情况下不是,我们必须得去看看源码是怎么回事。


回复
nanchen2251 : @nanchen2251 

好吧,当我检查后,发现 @陈小缘啦啦啦 已经说完了。 事实就是他(她)说的那样。

2019-06-19 回复
4

不一定都是Activity.
Activity中setContentView时一定是Activity;
通过 new View、View.inflate、LayoutInflater.inflate 这几种方式添加View,我们传参时传的是什么context, View中的就是什么Context.

Activity中setContentView时为什么一定是Activity
setContentView 时,其加载过程为 Activity.setContentView -> PhoneWindow.setContentView -> LayoutInflater.inflate -> LayoutInflater.createViewFromTag -> LayoutInflater.onCreateView -> LayoutInflater.createView
最终在createView方法中来实例化xml文件中的View,这里可以看到传参的context是LayoutInflater的context,而LayoutInflater是在PhoneWindow中创建的,其context是PhoneWindow的context, PhoneWindow的context是Activity.
所以,以setContentView方式来加载View,View的context一定是Activity.

另外三种方式为什么就按传的内容呢
new View就毋庸置疑了,很明显是直接传的context。另外两种也都是通过LayoutInflater加载的,View的context就是LayoutInflater的context,也就是我们创建LayoutInflater时的传参了。

回复
ningsk : @hexiaosa 

补充一点,我们有时候会遇到将context强转为Activity的情况我们应该怎么处理呢? 事实上当运行在5.0系统版本以下的手机,并且Activity是继承自AppCompatActivi  ...查看更多

2019-07-06 回复
1

除了 TintContextWrapper,还有的情况直接写了一篇文章,感兴趣的可以看看,

每日一问:View.getContext() 一定会返回 Activity 对象么?


链接是:https://mp.weixin.qq.com/s/5Xj6O9wV_EnynpdnB5mJ5A


回复
1

补充一点,我们有时候会遇到将context强转为Activity的情况我们应该怎么处理呢?
事实上当运行在5.0系统版本以下的手机,并且Activity是继承自AppCompatActivity的,那么View的getConext方法,返回的就不是Activity而是`TintContextWrapper`. 但是你说的View的底层容器其实都是activity是对的,因为TintContextWrapper的`getBaseContext` 确实还是activity。 而题目问的是view.getContext一定是activity吗,而不是底层容器最后是不是activity。
那么我们就可以用以下代码实现了。。
```
if (context instanceof ContextWrapper)
{ if(((ContextWrapper) context).getBaseContext() instanceof Activity) }
```
这样我们就可以拿到activity了

回复
1

按我理解

在activity通过setView方式,getConext是activity

在fragment通过LayoutInflater inflater方式setView也是activity
如果是悬浮窗、通过new View(appliaction)方式就不是activity

回复
SilenceV : @709847739@qq.com 

LayoutInflater的也可以换的...

2019-06-23 回复
0

    答案是: 不一定是。

    关键是取决于你传入的Context是什么,无论是xml还是代码声明的View 最终都会走View 的构造方法创建,通过源码很明确的可以知道,你构造方法传入的是谁,你的getContext 就是谁。

    对于View的创建区分两种形式,先说简单的直接new,这种就比较明显了,你new 的谁就是谁,无论是Application or Activity or ContextWrapper 类都是可以的; 另一种就是xml的形式,这个底层使用的LayoutInflater 去加载解析的,对于构建View来说,View的context 取决于LayoutInflater内的context(此处细节看LayoutInflater的实现代码,太多就不贴了),对于LayoutInflater来说其初始化的方法使用LayoutInflater.from(context) 的形式获得(此处细节看下Context的getSystemServiceName的实现),你如果传入Activity 则你的LayoutInflater内的context就是Activity (Activity 重写了getSystemServiceName的方法,会将自己的Context 传入),如果你传入的是Application的,则会使用Application的,默认Activity内的布局都是传入的Activity的所以你这个时候生成的View 的getContext 都是Activity 的。 

    上面已经清楚的说明View的context 取决于你传的什么,具体细节不清晰的可以再看下回复然后结合源码去看(Activity、ContextImpl、LayoutInflater、SystemServiceRegistry、PhoneLayoutInflater)。


回复
0

肯定不一定是吧,那比如添加到window中的,和桌面控件和通知的View都是么?

回复
lt 李小白 : @lt 李小白 

尤其是桌面控件的 \手动抠鼻

2019-06-21 回复
0

看完陈小缘的回答,自己再结合代码看了一下,发现几个问题。首先,TintContextWrapper是一个包装类,它的父类是ContextWrapper,而这个看完陈小缘的回答,自己再结合代码看了一下,发现几个问题。首先,TintContextWrapper是一个包装类,它的父类是ContextWrapper,而这个类又是Context的子类,这个类的作用就是在于在有新功能时不改变顶层Context,TintContextWrapper就是一个很好的例子。在TintContextWrapper出现之前,在View中出现的都是ContextWrapper,那么按照回答中的理论,getContext也获取的不是Context了,其实并不然,因为ContextWrapper中有Context的成员变量,在View中获取Context的时候都会直接调用getBaseContext方法,而这个方法得到的就是构造方法中设定的Context。


我觉得我们还是应该从源头去寻找答案,问题中是问View中getContext,既然如此应该直接去这里查看,getContext中直接返回了mContext的成员变量,而这个变量是在构造方法中赋值的,由此我们可以寻找,什么时候会把Context赋值?就是说,什么时候会生成一个View?而这些View出现在哪里?显然在Activity,Dialog或者PopupWindow中,而这些View的底层容器其实都是Activity,我认为View中的getContext一定返回的是Activity对象。

回复
0

问出这种问题大概率时不一定,然后看小缘同志的答案啦啦啦  

回复

删除留言

确认删除留言,会导致相关评论丢失?

取消 确定