一、概念
Context 是应用程序和系统之间的桥梁,用于获取全局消息、访问系统资源、调用应用程序级的操作。一般直接调用 Context 的方法或者调用接口时传入Context。
Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境。一个Android程序可以理解为一部电影,四大组件(Activity、Service、BroadcastReceiver、ContentProvider)就好比戏里的四个主角,它们是剧组(系统)一开始定好的,主角并不是大街上随便拉个人(new 一个对象)都能演的。有了演员当然也得有摄像机拍摄,它们必须通过镜头(Context)才能将戏传给观众,这也就正对应说四大组件必须工作在Context环境下。那么Button、TextView等等控件就相当于群演,显然没那么重要随便一个路人甲都能演(可以new一个对象),但是它们也必须在面对镜头(工作在Context环境下),所以Button mButtom = new Button(context) 是可以的。
ContextImpl | 是 Context 接口的真正实现者,我们调用的各种 Context 方法均来自于该类的实现。然后赋值给ContextWrapper。 |
ContextWrapper | 是一个装饰类,构造中需要传入一个真正的 Context 引用,提供了 attachBaseContext() 用于指定真正的 Context 实现对象。所以虽然 Activity、Service、Application 都继承自 ContextWrapper 但初始化过程中都会创建 ContextImpl 对象由它实现 Context 接口中的方法。 |
ContextThemeWrapper | 内部包含了与主题 Theme 相关的接口,就是在 AndroidManifest 中通过 android:theme 指定的主题,只有 Activity 才需要主题。 |
Application | 特点是生命周期长,在整个应用程序运行的期间他都会存在。同时我们可以自定义Application,并在onCreate()里面做一些全局的初始化操作。在不涉及UI以及启动Activity操作时使用 |
Activity | 是一个拥有主题的 Context 对象。Activity常用于与UI有关的操作,如添加window等。常规使用可以直接用 activity.this。 |
Service | 也可以和Activity一样直接使用 service.this 来使用 Context。 |
Broadcast ContextProvider | 所持有的 Context 是外部传入的。 |
二、选择
一般 Context 造成的内存泄漏,几乎都是当 Context 销毁的时候却因为被引用导致销毁失败。
出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个 Activity 的启动必须要建立在另一个 Activity 的基础之上,也就是以此形成返回栈,所以在非 Activity 环境下启动 Activity 需要 Intent 指定 FLAG_ACTIVITY_NEW_TASK 这个 Flag。而Dialog则必须在一个 Activity 上面弹出(除非是System Alert类型的Dialog),因此在这种场景下只能使用 Activity 类型的Context 否则报错。
在 Application 和 Service 中去 Layout Inflate 也是合法的,但会使用系统默认的主题样式,如果自定义了某些样式可能不会被使用,因此不推荐。
- 当 Application 的 Context 能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
- 凡是跟 UI 相关的都应该使用 Activity 作为 Context 来处理。
- 不要让生命周期长于 Activity 的对象持有到 Activity 的引用。
- 尽量不要在 Activity 中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
Application | Activity | Service | |
弹出Dialog | No | Yes | No |
启动Activity | 不推荐 | Yes | 不推荐 |
启动Service | Yes | Yes | Yes |
发送和注册Broadcast | Yes | Yes | Yes |
加载Layout | 不推荐 | Yes | 不推荐 |
加载Resource | Yes | Yes | Yes |
三、获取
View.getContext( ) | 返回当前 View 的 Context 对象,通常是当前正在展示的 Activity 对象。 |
Activity.getApplicationContext( ) | 获取当前 Activity 所在进程的 Context 对象,通常使用 Context 对象时优先考虑这个全局的进程Context。 |
ContextWrapper.getBaseContext( ) | 用来获取一个 ContextWrapper 进行装饰之前的 Context,实际开发中使用的不多,也不建议使用。 |
Activity.this | 返回当前 Activity 实例,跟 UI 相关的都应该使用 Activity 作为 Context 避免内存泄漏。 |
四、面试相关
4.1 一个应用程序有几个Context?
应用程序中 Context 的子类是 Activity、Service、Application(注意 Broadcast 和 ContextProvider 持有的 Context 都是外部传入的因此不算),所以 Comtext 数量 = Activity数量 + Service数量 + 1。
4.2 getApplication() 和 getApplicationContext() 区别?
获得的对象相同,作用域不同(语义的区别,getApplication() 只在 Activity 和 Service 中有,在别的地方获取例如 BroadcastReceiver 中就要使用 getApplicationContext() 了)。