何时调用活动上下文或应用程序上下文?

Posted

技术标签:

【中文标题】何时调用活动上下文或应用程序上下文?【英文标题】:When to call activity context OR application context? 【发布时间】:2011-11-10 01:18:56 【问题描述】:

关于这两种情况的帖子已经有很多了。但我仍然没有完全正确

据我所知: 每个都是其类的一个实例,这意味着一些程序员建议您尽可能频繁地使用this.getApplicationContext(),以免“泄漏”任何内存。这是因为另一个this(获取Activity 实例上下文)指向一个Activity,每次用户倾斜手机或离开应用程序等时都会销毁该Activity。显然垃圾收集器(GC)没有'不捕捉,因此使用了太多的内存..

但是任何人都可以提出一些非常好的编码示例,其中使用this 是正确的(获取当前Activity 实例的上下文)并且应用程序上下文将无用/错误?

【问题讨论】:

【参考方案1】:

getApplicationContext() 几乎总是错误的。 Ms. Hackborn(以及其他人)非常明确地表示您在您知道为什么您正在使用getApplicationContext() 并且仅在您需要时使用getApplicationContext() 使用getApplicationContext()

坦率地说,“一些程序员”使用getApplicationContext()(或getBaseContext(),在较小程度上)是因为他们的Java 经验有限。他们实现了一个内部类(例如,OnClickListener 用于Button 中的Activity)并且需要Context。他们没有使用MyActivity.this 来获取外部类'this,而是使用getApplicationContext()getBaseContext() 来获取Context 对象。

在您知道您需要Context 的情况下使用Context 可能比您拥有的任何其他可能的Context 寿命更长.场景包括:

如果您需要与 Context 绑定的东西,它本身将具有全局范围,请使用 getApplicationContext()。我使用getApplicationContext(),例如,在WakefulIntentService 中,用于服务的静态WakeLock。因为WakeLock 是静态的,我需要Context 才能在PowerManager 创建它,所以使用getApplicationContext() 是最安全的。

当您从Activity 绑定到Service 时使用getApplicationContext(),如果您希望通过Activity 实例在Activity 实例之间传递ServiceConnection(即绑定的句柄)@987654359 @。 android 通过这些ServiceConnections 在内部跟踪绑定,并保存对创建绑定的Contexts 的引用。如果从Activity 绑定,那么新的Activity 实例将引用ServiceConnection,而ServiceConnection 隐式引用了旧的Activity,并且旧的Activity 不能被垃圾回收。

一些开发人员将Application 的自定义子类用于他们自己的全局数据,他们通过getApplicationContext() 检索这些数据。这当然是可能的。我更喜欢静态数据成员,如果没有其他原因,您只能拥有 一个 自定义 Application 对象。我使用自定义 Application 对象构建了一个应用程序,发现它很痛苦。 Ms. Hackborn also agrees with this position.

以下是在任何地方都使用getApplicationContext() 的原因:

这不是一个完整的Context,支持Activity 所做的一切。你尝试用这个Context做的各种事情都会失败,mostly related to the GUI。

如果来自getApplicationContext()Context 保留了由您对其调用而创建的、您没有清理的东西,它可能会造成内存泄漏。使用Activity,如果它持有某物,一旦Activity 被垃圾收集,其他所有东西也会被清除。 Application 对象在您的进程的生命周期内保持不变。

【讨论】:

@Norfeldt:仅供参考,您评论中的链接链接回此答案。 谢谢你..这是链接:***.com/questions/5796611/… 它描述了我害怕使用它得到的内存泄漏 @djaqeel:你引用的后半部分几乎是真的。最好将其表述为“不要将 Activity 上下文提供给比 Activity 寿命更长的东西,例如静态数据成员”。但是,当您确切地知道在特定情况下需要它时,您仍然只使用getApplicationContext()。膨胀布局?使用活动。绑定到服务,您需要该绑定以在配置更改后继续存在吗?使用getApplicationContext(),因此绑定不会绑定到Activity 实例。 @CommonsWare:为什么 getApplicationContext() 几乎总是错误的?我如何在android-developers.blogspot.de/2009/01/… 中看到,为了避免与上下文相关的内存泄漏,我们应该使用上下文应用程序而不是上下文活动 @Sever:我在回答中提到了这一点。 Dave Smith 也有一篇很棒的博客文章,涵盖了上下文:doubleencore.com/2013/06/context 他的摘要段落:“在大多数情况下,使用您正在工作的封闭组件中直接提供给您的 Context。您可以放心地持有对它的引用因为该引用不会超出该组件的生命周期。一旦您需要从位于 Activity 或 Service 之外的对象保存对 Context 的引用,即使是暂时的,也可以将该引用切换到应用程序上下文。 "【参考方案2】:

我认为 SDK 网站上有很多内容没有得到很好的记录,这就是其中之一。我要提出的主张是,似乎最好默认使用应用程序上下文并且仅在您真正需要时使用活动上下文。我见过的唯一需要活动上下文的地方是进度对话框。 SBERG412 声称您必须为 toast 消息使用活动上下文,但 Android 文档清楚地显示了正在使用的应用程序上下文。由于这个 Google 示例,我一直使用应用程序上下文来祝酒。如果这样做是错误的,那么 Google 就在这里丢球了。

还有更多需要思考和回顾的内容:

对于 toast 消息,Google 开发指南使用应用程序上下文并明确说明使用它: Toast Notifications

在开发指南的对话框部分,您会看到 AlertDialog.Builder 使用应用程序上下文,然后进度条使用活动上下文。谷歌没有解释这一点。 Dialogs

似乎使用应用程序上下文的一个很好的理由是当您想要处理配置更改(例如方向更改),并且您想要保留需要上下文(例如视图)的对象时。如果你看这里:Run Time Changes 使用活动上下文需要注意,这可能会造成泄漏。这可以通过具有要保留的视图的应用程序上下文来避免(至少这是我的理解)。在我正在编写的应用程序中,我打算使用应用程序上下文,因为我试图在方向更改时保留一些视图和其他内容,并且我仍然希望在方向更改时销毁和重新创建活动。因此,我必须使用应用程序上下文来避免内存泄漏(请参阅Avoiding memory Leaks)。对我来说,使用应用程序上下文而不是活动上下文似乎有很多充分的理由,而且对我来说,你似乎会比活动上下文更频繁地使用它。这就是我读过的许多 Android 书籍似乎都是这样做的,这也是我看到的大部分 Google 示例的所作所为。

Google 文档确实让人觉得在大多数情况下使用应用程序上下文非常好,而且实际上比在他们的示例中使用活动上下文更频繁(至少在我见过的示例中)。如果使用应用程序上下文真的是这样一个问题,那么谷歌确实需要更加重视这一点。他们需要说清楚,他们需要重做一些例子。我不会将这完全归咎于缺乏经验的开发人员,因为权威(Google)确实让使用应用程序上下文看起来不是问题。

【讨论】:

我完全同意。 CommonsWare 的回答让我有点意外。我很高兴找到了这个问题,因为谷歌文档表明使用 getApplicationContext 可能非常危险。【参考方案3】:

我将此表用作何时使用不同类型的上下文的指导,例如 应用程序上下文(即:getApplicationContext())和 活动上下文,以及 BroadcastReceiver 上下文:

所有优点都去原作者here了解更多信息。

【讨论】:

【参考方案4】:

使用哪个上下文?

有两种类型的上下文:

    应用程序上下文与应用程序相关联,并且在应用程序的整个生命周期中始终保持不变——它不会改变。因此,如果您使用 Toast,您可以使用应用程序上下文甚至活动上下文(两者),因为 Toast 可以从应用程序中的任何位置显示,并且不附加到特定窗口。但是有很多例外,一个例外是当您需要使用或传递活动上下文时。

    Activity 上下文 与 Activity 相关联,如果 Activity 被销毁,则可能会被销毁 - 单个应用程序可能有多个 Activity(很可能)。有时您绝对需要活动上下文句柄。例如,如果您启动一个新活动,您需要在其 Intent 中使用活动上下文,以便新启动的活动在活动堆栈方面连接到当前活动。但是,您也可以使用应用程序的上下文来启动新的活动,但是您需要设置标志 Intent.FLAG_ACTIVITY_NEW_TASK 以将其视为新任务。

让我们考虑一些情况:

MainActivity.this指的是MainActivity上下文,它扩展了Activity类,但基类(activity)也扩展了Context类,所以可以用来提供Activity上下文。

getBaseContext() 提供活动上下文。

getApplication() 提供应用程序上下文。

getApplicationContext() 还提供应用程序上下文。

更多信息请查看link。

【讨论】:

需要在应用程序中显示 AlertDialog 的情况如何?显示结果的异步进程。 一个例子可能是:用户点击下载,这会触发downloadmanager的下载请求,当收到完成的信号时,它应该显示一个对话框,例如“你想用这个下载做什么?”。我的(hack)解决方案将最新的Activity 保存在static Application 类中,并在下载完成时请求当前的Activity。但是,我怀疑这是正确的实施方式。 TL;DR 如何在应用中的任意位置显示 AlertDialog? @KGCybeX 如果您想在下载完成时在应用程序中的任何位置显示任何内容,您应该在您的活动上手动注册一个广播接收器,该接收器侦听您的下载服务将广播的特定消息并执行您的任何操作收到消息后想要,或直接将您的活动附加到该服务。【参考方案5】:

我想知道为什么不对它支持的每个操作使用应用程序上下文。最后,它降低了内存泄漏和缺少对 getContext() 或 getActivity() 的空值检查的机会(当使用注入的应用程序上下文或通过应用程序的静态方法获取时)。声明,例如 Ms. Hackborn 仅在需要时使用应用程序上下文的声明,如果没有解释原因,对我来说似乎没有说服力。但似乎我找到了一个不言而喻的原因:

发现某些 Android 版本/设备组合存在不遵循这些规则的问题。例如,如果我有一个通过 Context 传递的 BroadcastReceiver 并将该 Context 转换为 Application Context,然后尝试在 Application Context 上调用 registerReceiver() ,那么在很多情况下它都可以正常工作,但也有很多我得到的实例由于 ReceiverCallNotAllowedException 而导致的崩溃。这些崩溃发生在从 API 15 到 22 的各种 Android 版本上。 https://possiblemobile.com/2013/06/context/#comment-2443283153

因为不能保证下表中描述为 Application Context 支持的所有操作都可以在所有 Android 设备上运行!

【讨论】:

【参考方案6】:

在显示 Toast 消息或内置 Dialog 消息时应该使用 Activity 上下文与应用程序上下文的两个很好的例子,因为使用应用程序上下文会导致异常:

ProgressDialog.show(this, ....);

Toast t = Toast.makeText(this,....);

这两个都需要来自 Activity 上下文的信息,而应用程序上下文中没有提供。

【讨论】:

嗯.. 您测试了哪个 Android 操作系统版本?我在 4.4.4 上测试过,效果很好。另外,正如@Andi Jay 所提到的,官方的 Android 开发人员文档在他们的示例代码中使用了应用程序上下文。 developer.android.com/guide/topics/ui/notifiers/… @中文名字,是的,它可能会工作,但在那个应用程序的未来的某个时候,它也会崩溃。发生在我身上好几次了。 当我在 Toast 中使用 Activity 上下文时,它会泄漏内存!【参考方案7】:

应用程序上下文仅在您的应用程序存活之前存在,它不依赖于活动生命周期,而是上下文保持对象长期存在 .如果您临时使用的对象,则使用 Application Context 并且 Activity Context 与 Application Context 完全相反。

【讨论】:

以上是关于何时调用活动上下文或应用程序上下文?的主要内容,如果未能解决你的问题,请参考以下文章

ktor 服务器 - 何时移动到另一个协程上下文

上下文或活动之外的 getString

何时在 Swift 中使用全局变量

应用程序中的全局活动上下文

基于任务的应用程序何时切换上下文?

何时类型不是类型?错误:'是一种类型,在给定的上下文中无效'