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


265

关于这两个上下文的内容有很多报道。.但是我仍然不太正确。

到目前为止,据我了解:每个类都是其类的一个实例,这意味着某些程序员建议您this.getApplicationContext()尽可能频繁地使用它,以免“泄漏”出任何内存。这是因为另一个this(获取Activity实例上下文)指向的Activity是每次用户倾斜手机或离开应用程序等时都将销毁的一个。显然,垃圾收集器(GC)无法捕获,因此使用了过多的内存..

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

Answers:


408

getApplicationContext()几乎总是错的。Hackborn女士(等等)已经非常明确,你getApplicationContext()当你知道为什么你使用getApplicationContext(),只有当你需要使用getApplicationContext()

直白地说,“某些程序员” 之所以使用getApplicationContext()(或getBaseContext()程度较小)是因为他们的Java经验有限。他们实现了一个内部类(例如,in中的OnClickListenerfor ),并且需要a 。他们使用或获取对象,而不是使用“ 获取外部类” 。ButtonActivityContextMyActivity.thisthisgetApplicationContext()getBaseContext()Context

getApplicationContext()当你知道你需要Context的东西,可以活得比其他任何可能Context您在您的处置。场景包括:

  • 使用getApplicationContext(),如果你需要的东西绑在Context本身将具有全局作用域。我在中使用getApplicationContext(),例如,WakefulIntentService将静态WakeLock用于服务。既然那WakeLock是静态的,并且我需要ContextPowerManager进行创建,那么使用它是最安全的getApplicationContext()

  • 如果您希望通过绑定实例之间的(即绑定的句柄),则从getApplicationContext()绑定到时使用。Android通过这些内部跟踪绑定,并保留对创建绑定的的引用。如果从进行绑定,则新实例将具有对的引用,而该引用具有对旧的隐式引用,并且无法对旧实例进行垃圾回收。ServiceActivityServiceConnectionActivityonRetainNonConfigurationInstance()ServiceConnectionsContextsActivityActivityServiceConnectionActivityActivity

一些开发人员将自定义子类的Application用作自己的全局数据,然后通过进行检索getApplicationContext()。当然有可能。我更喜欢静态数据成员,如果出于其他原因,您只能拥有一个自定义Application对象。我使用一个自定义Application对象构建了一个应用程序,发现它很痛苦。哈克伯恩女士也同意这一立场

以下是无论您在何处都不使用的原因getApplicationContext()

  • 它不是完整的Context,支持所做的一切Activity。您将尝试执行的各种操作Context将失败,主要与GUI有关

  • 如果Contextfrom getApplicationContext()保留了您不清除的调用所创建的某些内容,则可能导致内存泄漏。使用Activity,如果它保留了某物,则一旦Activity收集到垃圾,其他所有内容也将被冲洗掉。该Application对象将在过程的整个生命周期中保留。


1
非常感谢您的回答。在阅读此答案之前找到的另一个链接也可能会帮助某些人。stackoverflow.com/questions/7298731/…-此链接说明了我对内存泄漏的担忧。
诺费尔德2011年

27
@Norfeldt:仅供参考,您评论中的链接会链接到此答案。
CommonsWare

2
谢谢..这是链接:stackoverflow.com/questions/5796611/…它描述了我害怕通过使用此内存泄漏
Norfeldt 2011年

6
@djaqeel:报价的后半部分几乎是正确的。最好将其表述为“不要将Activity上下文赋予比Activity寿命更长的对象,例如静态数据成员”。但是,您仍然仅getApplicationContext()确切知道为什么在给定情况下需要它时才使用。扩大布局?使用活动。绑定到服务,您需要该绑定来承受配置更改吗?使用getApplicationContext(),因此绑定不绑定到Activity实例。
CommonsWare 2013年

7
@Sever:我在回答中提到了这一点。Dave Smith还有一篇出色的博客文章,内容涉及上下文:doubleencore.com/2013/06/context他的摘要段落:“在大多数情况下,请使用所使用的封闭组件直接提供的上下文。您可以放心地保存只要对该引用的引用不超出该组件的生命周期,只要您需要从活动或服务之外的对象中保存对上下文的引用,即使是暂时的,请切换保存的引用转到应用程序上下文。”
CommonsWare 2013年

48

我认为SDK网站上记录的东西很多,这就是其中之一。我要声明的是,似乎最好默认使用应用程序上下文,而仅在确实需要时才使用活动上下文。我见过的唯一需要活动上下文的地方是进度对话框。SBERG412声称您必须在活动消息中使用活动上下文,但是Android文档清楚地显示了正在使用的应用程序上下文。由于这个Google示例,我一直将应用程序上下文用于烤面包。如果这样做是错误的,那么Google会将球丢在这里。

这里还有更多要考虑和审查的内容:

对于Toast消息,《 Google Dev Guide》使用应用程序上下文,并明确地说要使用它: Toast Notifications

在开发指南的对话框部分中,您将看到AlertDialog.Builder使用应用程序上下文,然后进度栏使用活动上下文。Google对此没有解释。 对话方块

使用应用程序上下文似乎是一个很好的理由,当您想要处理诸如方向更改之类的配置更改,并且想要保留需要诸如Views之类的上下文的对象时。如果您在此处查看:运行时更改 关于使用活动上下文的警告,这会导致泄漏。可以通过在应用程序上下文中保留要保留的视图来避免这种情况(至少是我的理解)。在我正在编写的应用程序中,我打算使用应用程序上下文,因为我试图保留方向更改中的某些视图和其他内容,并且我仍然希望活动能够在方向更改时销毁并重新创建。因此,我必须使用应用程序上下文来避免内存泄漏(请参阅避免内存泄漏)。)。对我来说,似乎有很多充分的理由使用应用程序上下文而不是活动上下文,并且对我来说,您似乎比活动上下文经常使用它。这就是我看过的许多Android书籍所要做的,也是我所看到的许多Google示例所要做的。

Google文档确实使得在大多数情况下使用应用程序上下文看起来非常好,实际上,在它们的示例中(至少是我所看到的示例),与使用活动上下文相比,它的出现频率更高。如果使用应用程序上下文确实是一个问题,那么Google确实需要对此进行更多的强调。他们需要明确说明,并且需要重做一些示例。我不会将这完全归咎于没有经验的开发人员,因为权威机构(Google)确实使使用应用程序上下文看起来不是问题。


5
我完全同意。CommonsWare的回答让我有些惊讶。我很高兴发现了这个问题,因为Google文档表明使用getApplicationContext可能非常危险。
史蒂夫·施瓦茨 Steve Schwarcz)

38

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

在此处输入图片说明

所有优点都可在此处获得原始作者的更多信息。


11

使用哪个上下文?

有两种类型的上下文:

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

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

让我们考虑一些情况:

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

  • getBaseContext() 提供活动上下文。

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

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

有关更多信息,请检查此链接


如果需要在应用程序中显示AlertDialog的情况如何,例如显示结果的异步过程。这样的一个示例可能是:用户单击下载,这会触发对的下载请求downloadmanager,并且在收到完成信号时,它应显示一个对话框,例如“您要对此下载做什么?”。我的(黑客)解决方案将最新的信息保存Activity在一个static Application类中,并Activity在下载完成后请求当前的信息。但是,我怀疑这是正确的实现。TL; DR如何在应用程序中的任何位置显示AlertDialog?
CybeX

@KGCybeX如果要在下载完成后在应用程序中显示任何地方,则应在活动中手动注册一个广播接收器,以侦听下载服务将广播的特定消息,并在接收到消息后做任何您想做的事情,或附加您直接对该服务的活动。
ExiRouS

6

我想知道为什么不对它支持的每个操作使用应用程序上下文。最后,它降低了内存泄漏的机会,并且减少了对getContext()或getActivity()的空检查(使用注入的应用程序上下文或通过Application的静态方法获取时)。像Hackborn女士仅在需要时使用Application Context的声明那样,如果没有解释原因似乎对我没有说服力。但是,似乎我发现了一个失败的原因:

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

因为不能保证下表中描述的由Application Context支持的所有操作都可以在所有Android设备上使用! 在此处输入图片说明


4

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

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

要么

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

这两个都需要来自Activity上下文的信息,而Application上下文中没有提供这些信息。


5
嗯。。您测试了哪个Android OS版本?我已经在4.4.4上测试过,并且效果很好。另外,正如@Andi Jay提到的那样,官方的Android开发人员文档在示例代码中使用了应用程序上下文。developer.android.com/guide/topics/ui/notifiers/...
김 준 호

1
@中文名称,是的,它可能会起作用,但在该应用程序的将来某个时间,它也会崩溃。发生在我身上好几次了。
Ojonugwa Jude Ochalifu

1
当我在Toast中使用Activity上下文时,它会泄漏内存!
杰姆西特·伊斯肯德罗夫

3

应用程序上下文处于活动状态,直到您的应用程序处于活动状态唯一,这是不依赖于活动的生命周期,但是,上下文保持对象长寿。如果您使用的对象是临时的,那时候将使用Application ContextActivity Context完全与Application Context相反。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.