我在Activity类
中初始化一个成员变量private String test = new String("A");
然后我用它来编写一个长时间消耗的循环,在doInBackground()
方法中从Activity中启动一个匿名的AsyncTask
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(Void result) {
}
@Override
protected Void doInBackground(Void... params) {
for (int j = 10; j >= 0; j--) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("DOINBACKGROUND ", test);
}
}.execute();
问题:
当我在Asynctask
仍在执行时离开Activity,并且在执行了onDestroy()
之后时,我在日志中看到成员变量仍处于活动状态且未被销毁。有人可以向我解释这有可能吗?
BOUNTY QUESTION :
成员变量仍然存在,因为即使在onDestroy()
之后,由于gc标准和gc优先级,它还没有被包含。没关系。
但我怀疑是否
doInBackground()
而不会崩溃(尽管具有临时内存消耗)< / LI>
或者
答案 0 :(得分:8)
不要混淆垃圾收集和活动生命周期。
一旦从GC根对象可追溯到它的所有引用都消失了,就可以对对象进行垃圾回收。
onDestroy()
是活动生命周期的一部分。本质上,框架是通过活动完成的,并且放弃了它可能持有的对活动和相关资源的任何引用。
当您实例化一个匿名内部类时,它会获得对父对象的隐式引用。换句话说,anon内部类总是一个非静态的内部类。此父参考是对Activity
的引用。然后,您将异步任务对象传递给执行程序,并调用execute()
,执行程序只要需要就保存异步任务引用,同时防止引用的活动被垃圾回收。
因此,我的代码段示例中的asynctask将始终完成其doInBackground()并且肯定不会因为NPE而崩溃?
是。但请考虑以下事项:
制作内部类static
,除非他们特别需要访问父对象。由于匿名内部类总是非static
,因此请将它们设为非匿名。
在具有单独生命周期的对象(例如活动或片段)中混合异步操作是脆弱的,最好避免使用。问题包括例如取消,结果传递到已经消失的对象,并保持GC阻止引用昂贵的对象,如活动。
答案 1 :(得分:3)
首先,onDestroy()在销毁活动之前发生,并要求活动管理器释放与该活动相关的所有资源。这意味着所有活动的资源都将成为被gc删除的候选资源。但是,它并没有强制gc从内存中删除资源,它们只是候选者。这些候选人将根据他们的大小,年龄,新近度,类型等对gc进行评分,每当系统需要更多空间时,它会要求gc删除候选人,并根据他们的分数来做。分数较高的候选人最有可能先被删除。
即使您退出应用程序,即使在您看到崩溃之后,这也是显而易见的。
如果您创建另一个活动并在其上调用System.gc(),您可能会看到此崩溃。
干杯 甲
答案 2 :(得分:3)
在对Activity的实例进行垃圾回收之前,垃圾回收器不会回收成员变量test
。
在AsyncTask完成之前,垃圾收集器不会回收Activity实例,因为AsyncTask持有对Activity实例的引用。
AsyncTask实例在完成其工作之前不会被垃圾收集。
AsyncTask将完成doInBackground()
方法而不会崩溃。当然可以。
答案 3 :(得分:2)
AsyncTask与包含它的Activity的生命周期无关。因此,例如,如果在Activity中启动AsyncTask并且用户旋转设备,则Activity将被销毁(并且将创建一个新的Activity实例)但是AsyncTask不会死亡,而是继续生存直到它完成。
然后,当AsyncTask完成时,它不是更新新Activity的UI,而是更新Activity的前一个实例(即创建它的那个实例,但不再显示它!)。这可能导致异常(类型为java.lang.IllegalArgumentException:如果您使用findViewById来检索Activity中的视图,则View未附加到窗口管理器。)
由于AsyncTask维护对Activty的引用,因此可能会导致内存泄漏,只要AsyncTask保持活动状态,就会阻止Activity被垃圾回收。
由于这些原因,使用AsyncTasks进行长时间运行的后台任务通常是一个坏主意。相反,对于长时间运行的后台任务,应该采用不同的机制(例如服务)。
答案 4 :(得分:0)
我认为异步任务不是静态的,它包含对封闭活动的引用,防止活动被垃圾收集。
如果您想知道匿名类如何导致活动泄漏,请参阅 - link
此外,您可以尝试旋转设备多个设备,并使用以下命令检查是否存在多个相同活动的实例,并检查no活动。
adb shell dumpsys meminfo your.app.packagename
Applications Memory Usage (kB):
Uptime: 40343748 Realtime: 164852669
** MEMINFO in pid 16561 [samsung.svl.com.graph] **
Pss Private Private Swapped Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 5708 5664 16 2380 20480 8849 11630
Dalvik Heap 1163 972 136 27080 37459 29598 7861
Dalvik Other 604 604 0 4
Stack 288 288 0 0
Other dev 4 0 4 0
.so mmap 3569 992 72 2120
.apk mmap 39 0 0 0
.ttf mmap 0 0 0 0
.oat mmap 539 0 4 0
.art mmap 747 524 4 704
Other mmap 5 4 0 0
GL mtrack 10951 10951 0 0
Unknown 2260 2260 0 92
TOTAL 25877 22259 236 32380 57939 38447 19491
Objects
Views: 17 ViewRootImpl: 1
AppContexts: 3 **Activities: 1**
Assets: 3 AssetManagers: 3
Local Binders: 8 Proxy Binders: 23
Parcel memory: 3 Parcel count: 12
Death Recipients: 0 OpenSSL Sockets: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0