Android中静态变量的生命周期

46

当我在我的主活动中声明和初始化一个变量为静态变量,并且该活动被销毁后。我仍然可以访问该变量的内容吗?

例如,我想始终访问存储在此变量中的AsyncTask吗? 我希望能够在方向更改后也能访问它。


1
我因为静态变量遇到了很大的问题。内存清理是不可预测的,可能会导致意外的失败。您可以通过清除RAM(按住主页按钮即可)来强制进行内存清理。如果您的应用程序仍然正常工作,则应该是安全的;-) - Vincent van der Weele
@EmmanuelMess:我不清楚你需要什么,而当前的答案已经提供了,比如我的回答 - CommonsWare
2
@CommonsWare您提到变量的生存期与进程的生存期一样长,但是您没有提供参考资料。此悬赏大部分是为了寻找一个有支持依据的答案,而不是“那个SO的回答说...”。 - EmmanuelMess
15个回答

25

静态变量与类相关联,只要类在内存中存在,它们就会一直存在,并在卸载类时销毁(这种情况非常少见)。

在Android中,当我们关闭任何应用程序时,它并不完全关闭,而是保留在最近使用的应用程序堆栈中。您可以通过长按主按钮(在大多数设备上)查看。

当其他应用程序需要内存时,Android本身会将这些最近使用的应用程序清除


3
证明,链接? 为什么那个类和它的类加载器无法从内存中卸载,而应用程序继续运行? - k4dima

9

如果进程被杀死,则所有的静态变量将会被重新初始化为默认值。

这主要是因为,当您重新启动应用程序时,会创建一个新的实例,而静态变量会被重新初始化。


这意味着,如果活动的启动模式为singleInstance,那么它将一直存在,直到设备重新启动,对吧? - Budimir Grom
不要按主页按钮。打开DDMS并杀死进程。返回应用程序。这是重现操作系统在感觉需要时会随机对应用程序执行的简单方法,您将看到静态变量已经消失。 - dabluck
这并不是我在其他地方看到的所声称的。有时候,Android可能会将您的应用程序从内存中驱逐出去,同时保留一个空进程(以加快重新启动速度)。如果应用程序重新启动,则旧的静态字段可能仍然存在,因为类从未被卸载。至少这是我看到的说法(Reddit)。如果这是真的,那么有时静态字段可能会被清除,有时则不会。 - Merk

8
静态变量与类本身相关联。只要类在内存中,变量就会被保留。
由于类存在于内存空间的“永久代”中,因此很少被垃圾回收(您可以在这里了解更多关于分代GC的工作原理 https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html)。
您可以查看https://developer.android.com/topic/performance/memory-overview以更好地了解Android中如何管理内存,但是除非您的应用程序正在执行非常不寻常的操作,否则永久代将分配所有需要保存其所有类的内存,并且不会被垃圾回收。
方向改变不会清除静态变量,但如果这是你的目标,使用静态变量并不是很合适。您可以通过使用setRetainInstance或类似方法(请参见Android:如何防止在方向更改时清除类变量的答案)来保留实例状态。

1
现在的Arch组件中,LiveData不需要通过setRetainInstance来保留数据。https://developer.android.com/topic/libraries/architecture/livedata - Gem

4
静态变量是在封装类加载到内存中时创建的。您可以在静态块中或代码已经运行时初始化静态变量,就像您的情况一样。静态变量与类型相关,而不是类型的单个实例,因此一旦创建静态变量,它将与包含它的进程一样长时间存在(在Android中,这意味着它将与应用程序一起存在)。它可能带来的问题是:
  • 上下文泄漏(如Activity):静态变量保留对上下文或对象(如视图)的引用,这些上下文或对象又保留对它的引用。
  • 对内存的巨大负担:如果您在生命周期短于应用程序的类型中使用静态变量,则进程将使所有静态变量保持活动状态。例如,在十个活动中,您将位图作为静态变量保留。在这种情况下,所有位图都处于活动状态并占据内存空间。最后,内存和系统无法承受这种负担,并抛出MemoryOutBoundException。
  • 崩溃:例如,在一个场景中,您在一个活动中创建了一个对象,然后将其存储在活动中作为静态变量。然后,您进入下一个活动。在第二个活动中,您可以确定前一个活动的静态变量不为空,并且大多数情况下这是正确的。但是如果系统杀死进程并重新创建它,则静态变量不会保留其对象。我的意思是,在事件驱动的环境中,管理静态变量不是一项容易的任务。

对于您的情况: ViewModel架构组件在配置更改期间保持对象持久性。您可以使用它,但仍需要注意上下文泄漏。另一个选项是使用没有UI的片段。您在片段中调用函数setRetainInstance(true),系统将在配置更改时保留此片段。该片段保留您的数据,您可以在配置更改后通过片段管理器获取此片段。实际上,后一种选项是ViewModel的基础机制。对于像AsyncTask这样的多线程情况,运行在单独线程中的操作不应保留对上下文的引用。您应在单独的层中运行任务,然后更新ViewModel或保留片段中的必要字段。


4

我还能访问变量的内容吗?

假设你所说的"销毁"是指用户按下BACK键之类的操作,那么可以。

静态数据成员在整个进程的生命周期内都存在。

例如,我想始终访问存储在此变量中的AsyncTask?我希望在方向更改后也能访问它。

这不是一个合适的解决方案。使用保留的片段或 使用 onRetainNonConfigurationInstance()


1
@Simon:那么请使用保留的片段(即,您已调用setRetainInstance(true)的片段,该片段管理AsyncTask)。请参阅https://github.com/commonsguy/cw-omnibus/tree/master/Threads/AsyncTask。 - CommonsWare
哦,我还是不太明白该怎么做。我有一个主活动,它是一个SherlockFragmentActivity,并且它有一个刷新方法。在这个方法中,创建并执行了我的AsyncTask的新对象。当任务开始时(onPreExecute()),会创建一个Dialog,并在doInBackground()中三次更改对话框的消息,在onPostExecute()中对话框被关闭。但是当方向改变时,就会出现一些问题。例如,没有对话框了。那么我该怎么办?我应该提出一个新问题,详细解释吗? - maysi
1
@Simon:你应该使用DialogFragment来管理你的对话框。据我所知,那个DialogFragment可以被保留用于管理你的AsyncTask,虽然我从不使用对话框。 - CommonsWare
好的,我会尝试使用这个。到目前为止谢谢 :) 避免对话框的原因是什么? - maysi
好的,听起来很容易理解。我也考虑过这个问题,但是你如何通知用户进度呢?当用户更改活动并且您需要访问上下文(例如Toast)时,是否存在问题? - maysi
显示剩余4条评论

1
即使你按下返回按钮关闭应用程序,只要你不从最近应用程序中清除它们,它们仍然保持。证据:我已经测试过了,你也可以轻松测试一下;)。

0
Android有一个空进程的概念,即使所有组件(活动、服务和/或广播接收器)被销毁,如果用户经常使用您的应用程序,则可能不会将其从内存中删除,在这种情况下,静态变量将不会完全清除。
应用程序类是在应用程序启动时正确创建并在用户退出应用程序时清除的临时变量之间共享的最佳方法。
参考:http://skillgun.com/question/9849/android-provab/face-to-face-round/if-i-close-the-application-will-it-destroy-all-the-static-variables

链接已损坏。 - EmmanuelMess

0

我相信我终于为你找到了一个参考资料 -

垃圾收集器会自动清理未使用的对象。如果程序不再持有某个对象的引用,则该对象将被视为未使用。您可以通过将持有该引用的变量设置为 null 来显式地丢弃引用。

https://docs.oracle.com/javase/tutorial/java/javaOO/summaryclasses.html

要明确的是,静态变量可能保持初始化状态,从而防止类被正确地垃圾回收(也称为内存泄漏)。

这里的对象是持有静态变量引用的类对象?在 Android 应用程序中,这些类型的对象何时进行垃圾回收?这个引用是否适用于 Android(静态变量的垃圾回收是否与普通的 Java 中的方式相同)? - EmmanuelMess
是的,在Android中它的工作方式相同,只要没有任何变量/实例的附加,它就会被清理/垃圾回收。 - Brandon McAnsh
你只回答了最后一个问题,而且似乎忽略了添加“在Android中”的参考资料的重点,正如悬赏细节所述。 - EmmanuelMess
我不相信默认的类加载器会卸载一个类。 - rds
类加载器肯定会根据需要卸载类;否则,在分析应用程序时,内存使用将始终增加(而不是减少)。只要静态变量正确未初始化,它们就会随着类一起被取消引用/卸载。 - Brandon McAnsh
显示剩余2条评论

0

静态变量的值将持续存在于类加载期间 - 它几乎与Activity生命周期(onCreate,...,onDestroy)无关。

当你第一次从代码中访问一个类时,它会被加载,然后直到有理由卸载它才会消失。

如果您的应用从内存中完全删除 - 通过任务杀手或当您的应用程序不再处于活动状态且内存不足时,Android将卸载该类。

因此,如果您创建了一个Android应用程序并初始化了一个静态变量,则它将在JVM中保留,直到发生以下情况之一: 1. 类已被卸载 2. JVM关闭 3. 进程死亡


  1. 类被卸载 2. JVM关闭 3. 进程死亡。这些过程何时被触发?
- EmmanuelMess
如前所述,即应用程序完全从内存中删除 - 无论是通过任务杀手还是当您的应用程序不再活动且内存变得较低时。 - Ashok Kumar
您有任何参考资料表明这些情况是唯一触发 Android 应用中静态变量在内存中被移除的吗? - EmmanuelMess

0

是的,即使活动关闭,您在其中设置的值仍然保持持久化,但在应用程序关闭后不会。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接