Activity的成员范围和Asynctask

时间:2015-06-24 16:09:38

标签: java android android-activity android-asynctask scope

我在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优先级,它还没有被包含。没关系。

但我怀疑是否

  • 'test'成员变量(以及活动的上下文)在引用asynctask结束其内容之前不会被攻击,因此asynctask可以始终并且肯定地完成其doInBackground()而不会崩溃(尽管具有临时内存消耗)< / LI>

或者

  • 不管asynctask正在运行,'test'成员变量迟早会被吞噬,可能会导致asysnctask崩溃

5 个答案:

答案 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