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

s1m*_*m0n 5 java multithreading android android-lifecycle android-activity

在我的应用程序的最新版本中,一些用户遇到了我无法重现的崩溃.目前只有Samsung运行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");
    }

}
Run Code Online (Sandbox Code Playgroud)

每次我通过首先点击Click me按钮然后再按下后退按钮listenerNotified来测试上述应用程序之前,都会打印到控制台onDestroy().

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

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

D/TAG: onDestroy
D/TAG: listenerNotified
Run Code Online (Sandbox Code Playgroud)

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

Oni*_*nik 3

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

是的。

让我们稍微更改一下有关将 a 发布RunnableHandler. 我还假设(根据您的描述)您可能有多个Runnables 发布到主线程,因此在某些时候可能会有一个 s 队列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);
}
Run Code Online (Sandbox Code Playgroud)

现在按下按钮b,然后按下后退按钮,您应该会看到相关的输出。

Might it be the reason of your app crash?如果没有看到你得到的东西,很难说。我只是想指出,当在new Handler()线程(您的情况下是主线程)上实例化时,与线程的消息队列Handler相关联,发送到队列并处理队列中的消息和消息。这些s 和 messages 引用了目标。即使's方法不是“析构函数”,即当该方法返回's 实例时不会立即终止(请参阅),但由于对 的隐式引用*,内存无法被 GC 回收。您将发生泄漏,直到将从 的消息队列中出队并得到处理。LooperRunnableRunnableHandlerActivityonDestroy()ActivityActivityRunnableLooper

更详细的解释可以在如何泄漏上下文:处理程序和内部类中找到


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