Nobody can go back and start a new beginning, but anyone can start today and make a new ending.
Context 在我们的 Android 开发中,真是无处不在:加载资源、启动一个新的 Activity、获取系统服务和创建 View 操作等。那么问题来了,Context 究竟是什么,具体怎么使用,又有哪些注意事项呢,作为新年第一篇博客,这次来探索 Android 中的 Context。
简介 Context
Context,是一个应用程序环境中全局信息的接口。它是一个抽象类,实现由 Android 系统提供。它允许访问特定于应用程序的资源和类,以及对应程序级操作如启动活动、广播和接收意图等的上调。
以上来自官网,总结来说,分为以下三点:
i. Context 描述的是一个应用程序环境的信息,即上下文;
ii. Context 类是一个抽象类,Android 为该抽象类提供了具体的实现类 (ContextImpl);
iii. 通过 Context 能获取应用程序的资源和类,也包括一些应用级别的操作,如启动一个 Activity、发送广播和接收 Intent 信息等。
上面的描述还是有些抽象,不妨这么形象地阐述下,也许不太恰当:
Android 应用程序如同一场篮球比赛,传球、抢篮板和得分等(类比于启动一个 Activity、发送广播等一切发生在应用程序里的行为),都得发生在 Context 这块场地上,没有 Context 这块场地,精彩的比赛就无法进行。
探索 Context
Context 的类型
由前文的简单介绍,我们已经知道,虽然 Android 应用程序是由 Java 语言编写的,但是 Android 程序不像 Java 程序那样,创建一个类,写个 main() 方法就能运行了,还要配套有一个完整的工程环境。这样,Activity、Service 和 BroadcastReceiver 等系统组件才能正常工作。Context 可以说是 Android 程序中维持各组件正常运行的一个核心功能类。
简单的结构图如下:
Context 有两个子类,分别为 ContextWrapper 和 ContextImpl。
Context 部分源码:
1 | public abstract class Context { |
ContextWrapper 为 Context 的包装类,构造函数包含了一个真正的 Context 引用,即 ContextImpl 对象。部分源码:
1 | public class ContextWrapper extends Context { |
ContextImpl 为 Context 的实现类,其大部分功能都是直接调用其属性 mPackageInfo 去完成,部分源码:
1 | /** |
而 ContextWrapper 又有三个子类,ContextThemeWrapper、Service 和 Application。该类内部包含主 (Theme) 相关的接口,是 android:theme 属性指定的。Activity 需要主题,Service 不需要,故 Activity 继承该 ContextThemeWrapper 类。部分源码:
1 | public class ContextThemeWrapper extends ContextWrapper { |
总结一下,Context 共有三种类型,分为 Application、Activity 和 Service,其具体 Context 的功能由 ContextImpl 实现。绝大多数场景下,Activity、Service 和 Application 三种类型的 Context 可以通用。不过,也有些例外情况,比如启动一个 Activity 或 弹出一个 Dialog。Android 中,不允许 Activity 或 Dialog 凭空出现,一个 Activity 的启动须建立在另一个 Activity 的基础上,以此形成返回栈。对于 Dialog,必须在 Activity 上弹出 (系统 Alert 类型的 Dialog 除外),这种情况下,必须使用 Activity 类型的 Context。
Context 的数量
Context 中共有 Application、Activity 和 Service 三种类型,故一个应用程序中 Context 数量的计算公式如下:
1 | N(Context) = N(Activity) + N(Service) + 1 |
N 代表数量,1 即 Application 的数量,一个 Android 应用程序只有一个 Application。上述公式意义即 Context 的数量为应用程序中多个 Activity 的数量加上多个 Service 的数量,再加上 1。
注意
:常说的四大组件中,BroadcastReceiver 和 ContentProvider 并不是 Context 的子类,它们持有的 Context 都是其他地方传过去的,并不计入 Context 总数。
Context 的作用域
总结如下:
注意到 NO 右上角有数字,其实从能力上来说是 YES 的,关于标明 NO 的解释如下:
数字1:应用可以从这里启动一个 Activity,但要求创建一个新的任务。这样,或许适用于特殊的使用实例,但是会在应用程序里创建非标准的返回栈行为,通常不推荐。
数字2:用在这里是可以的,但会在运行的系统上加载默认的主题,而不是加载应用程序里自定义的主题。
数字3:在 Android 4.2+ 版本上,Receiver 为空时允许,用来获取粘性广播的当前值。
重点看 Activity 和 Application,注意以下几点:
i. 和 UI 相关的方法不建议或不能使用 Application,并且,表格中前三个动作基本不可能出现于 Application 中。
ii. 凡是和 UI 相关的,都应该使用 Activity 作为 Context 来处理;其他一些动作,Service,Activity 和 Application 等实例都可以。
Context 的使用实例
1 | TextView text = new TextView(getContext()); |
Context 的使用注意事项
I.需要 Context 的时候,若是在 Activity 中,大多直接传递个 this;若是在匿名内部类中,由于 this 不能用,需要写 XXActivity.this,有些人会直接使用 getApplicationContext()。实际上,XXActivity 和 getApplicationContext() 返回的肯定不是同一个对象,前者为当前 Activity 的实例,后者是应用程序 Application 的实例。区别很明显,各自的使用场景不同,乱用会带来问题。
II.注意使用 Context 不当引起的内存泄漏。举例如下:
不当的单例模式
1 | public class Singleton { |
这是一个非线程安全的单例模式,mInstance 作为静态对象,其生命周期比普通的对象长。其中包含 Activity,假设 BBActivity 调用 getInstance() 方法获得 mInstance 对象,传入 this,这时,常驻内存的 Singleton 保存了传入的 BBActivity 对象,并且一直持有。即使 Activity 被销毁掉,由于其引用还存在于 Singleton 中,不会被 GC 掉,如此便导致内存泄漏。
View 持有 Activity 的引用
1 | public class MainActivity extends Activity { |
将一个静态的 Drawable 对象当 ImageView,设置这个 Drawable 时,ImageView 保存了 mDrawable 的引用,而 ImageView 传入的 this 是 MainActivity 的 mContext,由于 static 修饰的 mDrawable 是常驻内存的,MainActivity 是它的间接引用,当 MainActivity 被销毁时,无法被 GC 掉,故造成内存泄漏。
III. 使用 Context 的正确姿势:
一般使用 Context 造成的内存泄漏,几乎都是当 Context 销毁时,由于其被引用导致销毁失败。总结得出使用 Context 的正确方法如下:
(1) 由于 Application 的 Context 对象是随着应用程序进程存在的,故在 Application 的 Context 适用的情况下,对于生命周期长的对象,优先使用 Application 的 Context。
(2) 不要让生命周期比 Activity 长的对象持有 Activity 的饮用。
(3) 最好不要在 Activity 中使用非静态内部类,由于非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
至此,浅探 Android 中的 Context 到此结束。
本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。
1 | Email: [email protected] / WeChat: Wolverine623 |
您也可以关注我个人的微信公众号 :码农六哥,第一时间获得博客的更新通知,或后台留言与我交流。
参考文献
1.http://blog.csdn.net/guolin_blog/article/details/47028975
2.http://www.jianshu.com/p/94e0f9ab3f1d
3.http://blog.csdn.net/lmj623565791/article/details/40481055