是否有可能在onDestroy之后调用回调方法?

时间:2016-09-18 21:35:09

标签: java android multithreading android-activity android-lifecycle

在我的应用的最新版本中,有些用户遇到了我无法重现的崩溃。目前只有运行Samsung的{​​{1}}设备遇到了问题,但这可能只是巧合。  在分析了堆栈跟踪和相关代码之后,我认为我可能已经找到了罪魁祸首。为了测试我的假设,我将代码简化为以下代码段:

Lollipop

每次我通过首先点击点击我按钮测试上述应用程序,然后再按后退按钮,public class TestActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button b = new Button(this); b.setText("Click me!"); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Handler().post(new Runnable() { @Override public void run() { // This is the callback method Log.d("TAG", "listenerNotified"); } }); } }); setContentView(b); } @Override protected void onDestroy() { super.onDestroy(); Log.d("TAG", "onDestroy"); } } 之前会将listenerNotified打印到控制台。

但是我不确定我是否可以依赖这种行为。 onDestroy()是否对上述情况做出任何保证?我可以安全地假设我的Android将始终在Runnable之前执行,或者是否存在不会出现这种情况的情况?在我的真实应用程序中,当然会发生更多事情(比如发布到主线程的其他线程以及回调中发生的更多操作)。但这个简单的片段似乎足以证明我的担忧。

是否一切可能(可能是由于其他线程的影响或发布到主线程的回调)我得到下面的调试输出?

onDestroy()

我想知道这一点,因为可能的结果可以解释崩溃。

3 个答案:

答案 0 :(得分:3)

  

是否可以在onDestroy()之后调用回调方法?

让我们更改一下有关将Runnable发布到Handler的示例代码。我还假设(根据你的描述)你可能有多个Runnable发布到主线程,所以在某些时候可能会有一个Runnable的队列,这会让我延迟实验如下:

public void onClick(View view) {
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            // This is the callback method
            Log.d("TAG", "listenerNotified");
        }
    }, 3000);
}

现在按下按钮b,然后按后退按钮,你会看到有问题的输出。

Might it be the reason of your app crash? 如果没有看到你的内容,很难说。我只想指出,当在一个线程(在你的情况下是主线程)上实例化new Handler()时,Handler与线程的Looper的消息队列相关联,从队列发送和处理Runnable和消息。这些Runnable和消息都引用了目标Handler。即使Activity的{​​{1}}方法不是“析构函数”,即当方法返回时,onDestroy()的实例也不会立即被杀死(see) ,由于对Activity的隐式引用*,因此无法对内存进行GC编辑。您将泄漏,直到ActivityRunnable的消息队列中排队并进行处理。

可以在How to Leak a Context: Handlers & Inner Classes

上找到更详细的说明

*匿名内部类Looper的实例引用了一个匿名内部类Runnable的实例,该实例又具有对View.OnClickListener实例的引用。

答案 1 :(得分:1)

您可能需要考虑不仅仅将延迟的runnable发布到处理程序。当您将任务运行到一个单独的线程并且您的活动已被销毁时,您可能会遇到问题。你可以这样做和实施。

your class activity
{
  Handler mHandler;

  .. onCreate ()
  {
    mHandler = new Handler();
  }

  .. onDestory ()
  {
    if (mHandler != null)
    {
      mHandler.removeCallbacksAndMessages(null);
      mHandler = null;
    }
  }

  private void post (Runnable r)
  {
    if (mHandler != null)
    {
      mHandler.post(r);
    }
  }
}

这样,处理程序消息队列上的任何挂起任务都将在活动被销毁时被销毁。

只是考虑到你知道在活动被销毁后你不需要运行任何任务。

答案 2 :(得分:0)

答案是"是"。这可能会导致Memory Leak