何时调用活动上下文或应用程序上下文?
有很多关于这两个环境是什么的post。但是我仍然没有把它说得很对
据我所知,到目前为止:每个是它的类的一个实例,这意味着一些程序员build议您尽可能经常使用this.getApplicationContext()
,以不“泄漏”任何内存。 这是因为另一个(获取Activity
实例上下文)指向一个Activity
,每当用户倾斜手机或离开应用程序等被销毁。这显然是垃圾收集器(GC)不捕获,因此使用内存太多
但是,任何人都可以拿出一些非常好的编码示例,使用this
(正在获取当前Activity
实例的上下文)是正确的,而应用程序上下文将是无用/错误的?
getApplicationContext()
几乎总是错的。 Hackborn女士 (除其他人)非常明确地表示, 只有在知道为什么使用getApplicationContext()
,只有在需要使用getApplicationContext()
时才使用getApplicationContext()
。
直言不讳,“一些程序员”使用getApplicationContext()
(或getBaseContext()
的程度较小),因为他们的Java经验是有限的。 他们实现了一个内部类(例如,一个OnClickListener
中Button
的OnClickListener
),并且需要一个Context
。 他们使用getApplicationContext()
或getBaseContext()
来获取一个Context
对象,而不是使用MyActivity.this
来获取外部类。
只有当你知道需要一个Context
时候,你才会使用getApplicationContext()
,这个Context
可能比你可以使用的任何可能的Context
更长。 情景包括:
-
使用
getApplicationContext()
如果你需要绑定到一个Context
本身将具有全局作用域的东西。 例如,在WakefulIntentService
,我使用getApplicationContext()
来为服务使用静态WakeLock
。 由于WakeLock
是静态的,我需要一个Context
来获取PowerManager
来创build它,所以使用getApplicationContext()
是最安全的。 -
如果您希望通过
onRetainNonConfigurationInstance()
Activity
实例之间的ServiceConnection
(即绑定的句柄),请在从Activity
绑定到Service
时使用getApplicationContext()
onRetainNonConfigurationInstance()
。 Android通过这些ServiceConnections
在内部跟踪绑定,并持有对创build绑定的Contexts
引用。 如果您从Activity
绑定,则新的Activity
实例将引用ServiceConnection
,该ServiceConnection
具有对旧Activity
的隐式引用,并且旧Activity
不能被垃圾收集。
一些开发人员使用自定义的Application
子类作为自己的全局数据,通过getApplicationContext()
检索它们。 这当然是可能的。 我更喜欢静态数据成员,如果没有其他原因,只能有一个自定义Application
对象。 我使用自定义Application
对象构build了一个应用Application
,发现它很痛苦。 哈克博恩女士也同意这一立场 。
下面是为什么不使用getApplicationContext()
原因:
-
这不是一个完整的
Context
,支持Activity
所做的一切。 您将尝试使用此Context
各种事情将失败, 主要与GUI有关 。 -
如果来自
getApplicationContext()
的Context
持有由您调用创build的东西,而您没有清除,则它可以创build内存泄漏。 通过一个Activity
,如果它坚持某些东西,一旦Activity
被垃圾收集,其他的东西也会被刷新。Application
对象在整个过程中保持不变。
我认为有很多东西在SDK网站上logging的很差,这是其中之一。 我要说的是,看起来好像默认使用应用程序上下文,并且只在真的需要时才使用活动上下文。 我见过的唯一需要活动上下文的地方是进度对话框。 SBERG412声称,你必须使用一个活动上下文作为Toast消息,但Android文档清楚地显示了正在使用的应用程序上下文。 由于这个Google示例,我总是使用应用程序上下文来敬酒。 如果这样做是不对的,那么Google就把球放在这里。
这里有更多的思考和审查:
对于Toast消息,Google开发人员指南使用应用程序上下文并明确说出使用它: Toast Notifications
在开发指南的对话框部分,您会看到AlertDialog.Builder使用应用程序上下文,然后进度栏使用活动上下文。 这不是由Google解释的。 对话框
看起来像使用应用程序上下文的一个很好的理由是,当你想要处理configuration更改(如方向更改),并且希望保留需要上下文(如Views)的对象时。 如果你看这里: 运行时间变化有一个关于使用活动上下文,可以创build一个泄漏警告。 这可以避免与应用程序上下文与保留的意见(至less这是我的理解)。 在我编写的应用程序中,我打算使用应用程序上下文,因为我试图在方向更改上保留一些视图和其他事物,而且我仍然希望活动在方向更改上被销毁和重新创build。 因此,我必须使用应用程序上下文来避免内存泄漏 (请参阅避免内存泄漏 )。 对我来说,似乎有很多很好的理由使用应用程序上下文,而不是活动上下文,对我来说,似乎你会比活动上下文更频繁地使用它。 这就是我所经历的许多Android书籍似乎都这样做的,这就是我见过的很多Google示例。
Google文档确实使得在大多数情况下使用应用程序上下文似乎是完全正确的,事实上,在他们的示例中(比如我见过的例子中)使用活动上下文的次数更多。 如果使用应用程序上下文确实是一个这样的问题,那么Google确实需要更加重视这一点。 他们需要说清楚,他们需要重做一些例子。 我不会把这完全归咎于没有经验的开发人员,因为权威(谷歌)真的使它看起来像使用应用程序上下文不是一个问题。
我只是使用这个表作为何时使用不同types的上下文(如应用程序上下文 (即: getApplicationContext()
)和活动上下文 ,也是BroadcastReceiver上下文的指导 :
原文在这里获取更多信息。
使用哪种上下文?
有两种types的上下文:
-
应用程序上下文与应用程序相关联,并且在应用程序的整个生命周期中始终保持不变 – 它不会改变。 所以,如果您使用Toast,则可以使用应用程序上下文甚至活动上下文(二者),因为可以从应用程序中的任何位置显示Toast,并且不会附加到特定的窗口。 但是有很多例外情况,例外情况是您需要使用或传递活动上下文。
-
活动上下文与活动相关联,如果活动被破坏, 活动上下文可能会被销毁 – 对于单个应用程序,可能有多个活动(更可能)。 有时你绝对需要活动上下文句柄。 例如,如果您启动新的活动,则需要在其Intent中使用活动上下文,以便新的启动活动以活动堆栈的forms与当前活动相关联。 但是,您也可以使用应用程序的上下文来启动新的活动,但是您需要设置标志
Intent.FLAG_ACTIVITY_NEW_TASK
以将其视为新任务。
我们来考虑一些情况:
-
MainActivity.this
指的是扩展Activity类的MainActivity上下文,而基类(activity)也扩展了Context类,所以它可以用来提供活动上下文。 -
getBaseContext()
提供活动上下文。 -
getApplication()
提供应用程序上下文。 -
getApplicationContext()
也提供了应用程序上下文。
欲了解更多信息,请查看此链接 。
当您使用Activity上下文与Application上下文时,两个很好的例子是在显示Toast消息或内置的对话消息时,因为使用Application上下文会导致exception:
ProgressDialog.show(this, ....);
要么
Toast t = Toast.makeText(this,....);
这两个都需要来自Activity上下文的信息,这个信息在Application上下文中没有提供。
应用程序上下文 直到您的应用程序仍然活着 ,它不依赖于活动生命周期,而是保持上下文对象的长久性 。 如果临时使用的对象,那么使用应用程序上下文和活动上下文是完全相反的应用程序上下文。
我想知道为什么不为每个它支持的操作使用应用程序上下文。 最后,它降低了内存泄漏的机会,并且对getContext()或getActivity()(当使用注入的应用程序上下文或通过应用程序中的静态方法获取时)缺lessnull检查。 像Hackborn女士所说的那样 ,只有在需要时才使用应用上下文的陈述对我来说似乎没有说服力,没有解释为什么。 但是,我似乎发现了一个不安的原因:
发现某些Android版本/设备组合中存在不遵循这些规则的问题。 例如,如果我有一个传递Context的BroadcastReceiver,并将该Context转换为Application Context,然后尝试在Application Context上调用registerReceiver(),则有许多实例可以正常工作,但也有很多实例由于ReceiverCallNotAllowedException而导致崩溃。 这些崩溃发生在从API 15到22的各种Android版本上。https://possiblemobile.com/2013/06/context/#comment-2443283153
因为不能保证下表中所描述的应用程序上下文支持的所有操作都适用于所有Android设备!