作用:
虽然本文标题为“浅析Context在常见类的用法”,但阅读者可能有不少是对Context感到疑惑的,故在此附述一下其作用和理解。
在Android中凡是一个带有GUI的组件被调用,必然不是普通Java代码那样直接new
一个对象实例即可,而是需要一个特定的环境下才能被显示出来。当然,一些特殊的组件例如Service
虽然不是GUI形式,但其特殊在于它可以完成很复杂的任务操作(理解为不带GUI的Activity
),当Service被调用然后工作时,其需要一个特定的工作环境才能启动。
一些特殊的组件如Toast和Dialog,虽然不再需要我们额外布局,但当我们调用时它们时,必须有一个特殊的环境,不然可能发生不是我们想要的地方出现,也可能显示错位,当然也可能显示不出来。
上面提到的环境,即为Context
。该词中译包括环境,语义,上下文等。从字面意思来看,上下文,就如同文章里面某一段落一样,某一段落可能是承上启下的作用,没有该段落,下一个段落发生的故事可能出现逻辑上的纰漏。有了完整的上下文,故事便可完整叙述下去且内容逻辑严谨。Android程序也是如此,某个组件缺少了具体的上下文,便会出错(最明显的是可能应用程序直接闪退)。
附加知识:
这里的附加知识仅为提及,对于具体的开发帮助可能不大,但有助于理解Conetxt
1:类图
2:Context数量计算
Context数量 = Activity数量 + Service数量 + 1
从类图可以看到Context的拥有者仅有Activity
,Service
,Application
。一个Android应用仅有一个Application
,但可以拥有多个Activity和Service。公式中的1就是指一个Android应用的一个Application
。
Context在常见类的用法:
初学者可能被this
、XXXActivity.this
、XXXService.this
、v.getContext()
、getActivity()
等搞混淆。
这里先要知晓几个关于Application的Context、Activity的Context、Service的Context的概念。在类图里面都看到了Application、Activity、Service都拥有自己的Context。但每个组件的Context并不是随意使用的。而是有具体的条件。
下面看一个关于三个组件之间Conetxt作用域的表格:
作用域 | Application | Activity | Service |
---|---|---|---|
Dialog | × | √ | × |
启动活动 | : ( | √ | : ( |
LayoutInflater | : ( | √ | : ( |
启动服务 | √ | √ | √ |
启动广播 | √ | √ | √ |
注册广播 | √ | √ | √ |
(√ 代表可以,×代表不可以,: (代表不推荐)
从上表可以看到Activity的作用域最大。那是不是可以认为在应用程序里面全用Activity的Context就行了呢?答案肯定不是的,否则Context也不会分为这几种了。但这里总结了几种使用条件,里面涵盖了几乎所有情况,可供参考:
- 在Activity里面调用控件、启动服务、广播、LayoutInflater创建等:使用某个组件所处的Activity的Context
- 在Service里面调用控件、启动服务、广播等:使用某个组件所处的Service的Context,GUI控件必须使用Service依附类(依附的Activity)的Context
- 在非Activity、非Service、非Application(就是某个普通类,可以为适配器或自定义View等)类中:使用某个组件所处的Activity的Context
上面的看完就需要回到本段标题的内容。this
、XXXActivity.this
、v.getContext()
、getActivity()
这些指的是什么东西?
this
:指代的是当前类的对象,也就是上下文。当在onCreate()
方里面被使用,指的就是当前Acitivity的Context;当在匿名类里面被使用的时候,指的就是匿名类对象而不是匿名类所处的Acitivity的ContextXXXActivity.this
:指代的就是某个具体的Activity的ContextXXXService.this
:指代的就是某个具体的Service的Contextv.getContext()
:指代的就是某个View所处的Activity的Context,常见于适配器等getActivity()
:指代的就是某个被依附类的Context,常见于Fragment等
1:Intent与启动活动
intent常用于启动活动,startActivity()
也需Context。一般情况下前者直接调用Activity的Context,具体指明Context对象,后者直接写。
示例:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
但也有其它情况,例如从Fragment跳转到其它Fragment或Activity。则两者均使用getActivity()
来获得Context。
示例:
Intent intent = new Intent(getActivity(), SecondActivity.class);
getActivity().startActivity(intent);
建议不要直接写this
,很容易出现内存泄漏等问题。具体指明Context对象还有助于理解程序间的调用关系。
2:启动服务
一般调用当前所处活动的上下文,即XXXActivity.this
。但也有例外,例如从一个服务启动另一个服务,那就要写成XXXService.this
。本质上仍然是调用当前类的上下文。
示例,在活动中启动一个服务:
Intent intent = new Intent(MainActivity.this, TestService.class);
startService(intent);
示例,在碎片中启动一个服务:
Intent intent = new Intent(getActivity(), TestService.class);
getActivity().startService(intent);
示例,在服务中启动一个服务:
Intent intent = new Intent(CurrentService.this, TestService.class);
startService(intent);
3:启动广播
和“启动服务”相同
4:Toast
一般调用Activity的Contextt
示例:
Toast.makeText(MainActivity.this, "Text", Toast.LENGTH_SHORT).show();
5:Dialog
和“Toast”相同
6:RecyclerView
有两种方式,一种是使用v.getContext(),另一种是自定义参数传入Context。但二者本质上都是Activiy的Context。
在onBindViewHolder()
方法里面如要使用Glide来进行ImageView图片资源的初始化,就必须得到一个Context,那么这个管理该Context的适配器可写成:
示例:
RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(getActivity(), bean);
或
RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(XXXActivity.this, bean);
上面的情况是只能能使用getActivity()
(或直接指明上下文)。其实在onCreateViewHolder()
里面中写法都是可以的。
7:Fragment
Fragment调用Acitivity的方法必须使用getActivity()。
示例:
getActivity().finish()
8:LayoutInflater
使用要展示活动的上下文。
示例:
LayoutInflater.from(mContext).inflate(R.layout.list_item, null);
在其初始化时传入要展示活动的上下文即可。