活动的成员作用域和异步任务

我在活动类中初始化成员变量

private String test = new String("A");

然后我用它来写入登录从活动启动的匿名异步任务的方法的长时间耗时的循环doInBackground()

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(); 

:当我在活动仍在执行时离开活动,并且在执行活动的 onDestroy() 之后,我在日志中看到成员变量仍然处于活动状态且未被销毁。有人可以向我解释一下这怎么可能吗?Asynctask

赏金问题:成员变量仍然处于活动状态,因为即使在 之后,由于 gc 条件和 gc 优先级,它尚未被丢弃。这没关系。onDestroy()

但我的怀疑是,如果

  • 在引用异步任务结束其内容之前,'test'成员变量(以及活动的上下文)不会被丢弃,因此异步任务可以始终且肯定地完成其而不会崩溃(尽管会暂时消耗内存)doInBackground()

或者相反

  • 无论异步任务是否正在运行,'test'成员变量迟早都会被丢弃,这可能会导致asysnctask崩溃

答案 1

不要混淆垃圾回收和活动生命周期。

一旦从 GC 根对象中可追溯到该对象的所有引用都消失,就可以对该对象进行垃圾回收。

onDestroy()是活动生命周期的一部分。从本质上讲,框架是与活动一起完成的,并且放弃了它可能持有的对活动和相关资源的任何引用。

实例化匿名内部类时,它将获取对父对象的隐式引用。换句话说,anon 内部类始终是非静态内部类。此父引用是对 .然后,您将异步任务对象传递给执行器,并调用 执行器,并且执行程序在需要时保留异步任务引用,同时还可以防止引用的活动被垃圾回收。Activityexecute()


因此,我的片段示例中的异步任务将始终完成其doInBackground(),并且肯定不会因NPE而崩溃?

是的。但请考虑以下事项:

  • 创建内部类,除非它们特别需要访问父对象。由于 anon 内部类始终是非匿名的,因此请使它们成为非匿名的。staticstatic

  • 在具有单独生命周期(如活动或片段)的对象中混合异步操作是脆弱的,最好避免。问题包括例如取消,结果传递到消失的对象,以及保持GC防止对昂贵对象(如活动)的引用。


答案 2

首先,onDestroy() 发生在销毁活动之前,并要求活动管理器释放与该活动绑定的所有资源。这意味着作业的所有资源都将是 gc 删除的候选资源。但是,它不会强制gc从内存中删除资源,它们只是候选者。这些候选人将由gc根据其大小,年龄,新近度,类型等进行评分,每当系统需要更多空间时,它就会要求gc删除候选人,并根据他们的分数进行评分。得分较高的候选者最有可能首先被删除。

当您看到突然崩溃时,即使您退出应用程序也是如此,这是显而易见的。

如果您创建另一个活动并对其调用 System.gc(), 则可能会看到此崩溃。

干杯 A.