本系列为小说《逆袭西二旗》的技术讲解,用于详细说明剧情里涉及的开发细节。

什么是 Context,Context 有哪些类型
Context 代表应用程序的环境或状态,提供对应用专属资源和类的访问。它充当应用与 Android 系统之间的桥梁,让组件能够访问资源、数据库、系统服务等。在执行诸如启动 Activity、读取 assets 文件或加载布局等任务时,Context 至关重要。
Android 中有多种类型的 Context。
面试问题
在 Android 应用中,为何使用正确的 Context 类型至关重要?
持有 Activit Context 的长生命周期引用又可能带来哪些风险?
Application Context
Application Context 与整个应用程序的生命周期绑定。当你需要一个全局、长生命周期且独立于当前 Activity 或 Fragment 的 Context 时,就应使用它。可通过调用 getApplicationContext() 获取。
适用场景:
- 访问全局资源,如
SharedPreferences或数据库; - 注册需贯穿整个应用生命周期的广播接收器;
- 初始化在整个应用生命周期内持续存在的库或组件。
Activity Context
Activity Context(即 Activity 中的 this)与 Activity 的生命周期绑定,用于访问资源、启动其他 Activity,或加载该 Activity 特有的布局。
适用场景:
- 创建或修改 UI 组件;
- 启动另一个
Activity; - 访问限定于当前
Activity的资源或主题。
Service Context
Service Context 与 Service 的生命周期绑定,主要用于后台任务,例如执行网络请求或播放音乐。它可访问 Service 所需的系统级服务。
Broadcast Context
Broadcast Context 在 BroadcastReceiver 被调用时提供。它的生命周期很短,通常仅用于响应特定广播事件。切勿在此上下文中执行耗时操作。
Context 的常见使用场景
- 访问资源 :通过
getString()、getDrawable()等方法,Context可用于获取字符串、图片、尺寸等资源。 - 加载布局 :使用
Context通过LayoutInflater将 XML 布局文件转换为View对象。 - 启动 Activity 和 Service :调用
startActivity()或startService()时必须传入Context。 - 访问系统服务 :通过
getSystemService(),Context能获取如ClipboardManager、ConnectivityManager等系统级服务。 - 数据库与
SharedPreferences操作 :Context用于访问 SQLite 数据库或SharedPreferences等持久化存储机制。
总结
Context 是 Android 中的核心组件,使应用能够与系统资源进行交互。Android 提供了多种 Context 类型,如 Application Context、Activity Context 和 Service Context,各自适用于不同场景。正确使用 Context 不仅能高效管理资源,还能避免内存泄漏和崩溃。因此,务必根据实际需求选择合适的 Context,并避免不必要的长期持有。
进阶:使用 Context 时要注意什么
Context 是 Android 中一种便捷的机制,但使用不当会引发严重问题,比如内存泄漏、崩溃,或者资源处理效率低下。为避免这些陷阱,务必了解其细微差别以及有效使用 Context 的最佳实践。
最常见的问题之一,是在 Activity 或 Fragment 等 Context 的生命周期之外,仍然保留对它们的引用。这会导致内存泄漏,因为垃圾回收器无法回收该 Context 及其相关资源所占用的内存。
例如,下面的示例代码可能会导致内存泄漏:
Kotlin
object Singleton {
var context: Context? = null // If this retains the Activity context, causing a memory leak
}
相反,对于需要 Context 的长生命周期对象,应使用 Application Context:
Kotlin
object Singleton {
lateinit var applicationContext: Context
}
因此,使用合适类型的 Context 至关重要。不同类型的 Context 用途各异,用错类型可能导致意外行为:
- 执行与界面(UI)相关的任务,如加载布局或显示对话框时,使用
Activity Context。 - 进行与 UI 生命周期无关的操作,如初始化库时,使用
Application Context。
下面的示例展示了误用 Application Context 的情况:
Kotlin
val dialog = AlertDialog.Builder(context.getApplicationContext()) // Incorrect
应使用 Activity Context 以确保正确的主题设置:
Kotlin
val dialog = AlertDialog.Builder(activityContext) // Correct
另一个关键考量点是,当关联组件(如 Activity 或 Fragment)被销毁后,要避免使用 Context。访问与已销毁组件绑定的 Context,可能会导致程序崩溃或出现未定义行为,因为与该 Context 关联的资源可能已不复存在。
下面的示例展示了对 Context 的误用,其中自定义视图的创建方式不当:
Kotlin
activity.finish() // The activity is destroyed
val text = TextView(activity) // but the TextView retains a reference
如果在 activity 销毁后依然使用 text,是非常危险的行为(例如 activity 销毁后,才请求到数据,然后更新 text 的显示)。
避免在后台线程使用 Context。
Context 是为主线程设计的,尤其适用于访问资源或与用户界面(UI)交互。在后台线程使用它可能会导致意外崩溃或线程问题。在与 UI 相关的 Context 资源交互前,需切换回主线程:
Kotlin
viewModelScope.launch {
val data = fetchData()
withContext(Dispatchers.Main) {
Toast.makeText(context, "Data fetched", Toast.LENGTH_SHORT).show()
}
}
总之。
要有效使用 Context,需谨慎考量,规避常见陷阱。不应在 Activity 或 Fragment 的生命周期结束后仍保留其 Context,否则可能导致内存泄漏。针对特定任务,务必选择正确类型的 Context,避免在后台线程,或关联组件销毁后使用它。此外,使用匿名内部类和回调时要小心,它们可能会不经意间持有对 Context 的引用。正确管理 Context 能确保高效利用资源,有助于防止内存泄漏或应用程序崩溃。
进阶:ContextWrapper
ContextWrapper 是 Android 中的一个基类,它具备包装 Context 对象的能力,将调用委托给被包装的 Context。它充当中间层,用于修改或扩展原始 Context 的行为。通过使用 ContextWrapper,你可以在不改变底层 Context 的情况下定制功能。
当你需要增强或覆盖现有 Context 的特定行为时,就会用到 ContextWrapper。它允许你拦截对原始 Context 的调用,并提供额外功能或定制化行为。
以 Activity 的继承关系为例:Activity -> ContextThemeWrapper -> ContextWrapper。
而 ContextWrapper 源码如下:
Kotlin
public class ContextWrapper extends Context {
@UnsupportedAppUsage
Context mBase;
...
}
使用场景:
- 自定义
Context:当你出于特定目的需要创建自定义Context时,例如应用不同的主题,或以特定方式处理资源。 - 动态资源处理 :包装
Context以动态提供或修改诸如字符串、尺寸或样式等资源。 - 依赖注入 :像 Dagger 和 Hilt 这样的库会创建
ContextWrapper,以便将自定义Context附加到组件上进行依赖注入。
下面的代码展示了如何使用 ContextWrapper 来应用自定义主题:
Kotlin
class CustomThemeContextWrapper(base: Context) : ContextWrapper(base) {
override fun getTheme(): Resources.Theme {
val theme = super.getTheme()
theme.applyStyle(R.style.CustomTheme, true) // Apply a custom theme
return theme
}
}
你可以在 Activity 中使用这个自定义包装器:
Kotlin
class MyActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(CustomThemeContextWrapper(newBase))
}
}
在此示例中,CustomThemeContextWrapper 为 Activity 应用了特定主题,覆盖了基础上下文的默认行为。
这样做有很多好处:
- 可复用性:你能将自定义逻辑封装在包装器类中,并在多个组件间复用。
- 封装性 :增强或修改行为的同时,无需更改原始
Context的实现。 - 兼容性 :与现有
Context对象无缝协作,保持向后兼容性。
总之。
ContextWrapper 是 Android 中用于定制 Context 行为的灵活且可复用的工具。它使开发者能够拦截并修改对原始 Context 的调用,而无需直接更改它,这使其成为构建模块化且适应性强的应用程序必不可少的类。
进阶:Activity 中的 baseContext 是什么
在 Activity 中,this 和 baseContext 都能用于访问 Context,但它们用途不同,代表着 Android 上下文层级结构中的不同层次。清楚何时使用它们,对避免代码混淆或潜在问题至关重要。
在 Activity 中,关键字 this 指的是 Activity 类的当前实例。由于 Activity 是 ContextWrapper 的子类(因而间接是 Context 的子类),this 提供了对 Activity 特定上下文的访问,包括诸如生命周期管理以及与用户界面(UI)交互等额外功能。
当在 Activity 中使用 this 时,它通常指向 Activity 的当前上下文,允许你调用特定于该 Activity 的方法。例如,如果你需要启动另一个 Activity 或显示与这个特定 Activity 相关联的对话框,就可以使用 this。
Kotlin
val intent = Intent(this, AnotherActivity::class.java)
startActivity(intent)
val dialog = AlertDialog.Builder(this)
.setTitle("Example")
.setMessage("This dialog is tied to this Activity instance.")
.show()
baseContext 代表 Activity 所基于的基础上下文。它是 ContextWrapper 类的一部分,而 Activity 继承自 ContextWrapper。baseContext 通常是 ContextImpl 实例,为 Context 方法提供核心实现。
baseContext 一般通过 getBaseContext() 方法来访问。你很少直接使用它,但在处理自定义 ContextWrapper 实现,或者需要引用被包装上下文背后的原始上下文时,它就会派上用场。
Kotlin
// 和直接通过 this 返回的相比,此处的 baseContext 不携带当前 Activity 的主题信息
val systemService = baseContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
总结一下他们之间的区别:
- 作用域 :
this代表当前的Activity实例及其生命周期,而baseContext指的是Activity构建所基于的更低层次的上下文。 - 用法 :
this通常用于与Activity生命周期或用户界面(UI)相关的操作,例如启动Activity或显示对话框。baseContext一般在与Context的核心实现进行交互时使用,特别是在自定义ContextWrapper的场景中。 - 层级关系 :
baseContext是Activity的基础上下文。访问baseContext会绕过Activity作为ContextWrapper所提供的额外功能。
总之。
在 Activity 中,this 指向当前的 Activity 实例,提供了具有生命周期和特定于用户界面(UI)功能的高级上下文。而 baseContext 代表 Activity 所基于的基础上下文,常用于诸如自定义 ContextWrapper 实现等高级场景。虽然在 Android 开发中 this 使用最为普遍,但理解 baseContext 有助于调试以及创建模块化、可复用的组件。
番外
本文讲述的技术细节完全是基于面试下的,如果你想对 Context 有个更加完整的认识,请看下面两篇文章: