是否可以在 onDestroy 之后调用回调方法?
Posted
技术标签:
【中文标题】是否可以在 onDestroy 之后调用回调方法?【英文标题】:Is it possible for a callback method to be called after onDestroy? 【发布时间】:2017-01-26 12:54:29 【问题描述】:在我的应用程序的最新版本中,一些用户遇到了我无法重现的崩溃。目前只有运行Lollipop
的Samsung
设备存在问题,但这可能只是巧合。
在分析了堆栈跟踪和相关代码之后,我认为我可能已经找到了罪魁祸首。为了测试我的假设,我将代码简化为下面的 sn-p:
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()
之前执行,或者是否有一种情况并非如此?在我的真实应用程序中,当然会发生更多事情(例如其他线程发布到主线程以及回调中发生的更多操作)。但这个简单的 sn-p 似乎足以表明我的担忧。
是否有可能(可能由于其他线程或发布到主线程的回调的影响)我得到下面的调试输出?
D/TAG: onDestroy
D/TAG: listenerNotified
我想知道这一点,因为可能的结果可以解释崩溃。
【问题讨论】:
为什么要通过处理程序发布可运行文件?同时你可以看看***.com/questions/31432014/… 当你有这样的异步回调时,如果Activity
仍然存在,你应该在处理回调之前检查回调。最简单的方法是调用isFinishing()
,如果Activity
不再“活动”,则返回true
。
【参考方案1】:
onDestroy()
之后可以调用回调方法吗?
是的。
让我们稍微修改一下关于将Runnable
发布到Handler
的示例代码。我还假设(根据您的描述)您可能有多个Runnable
s 发布到主线程,所以在某些时候可能会有Runnable
s 的队列,这使我在下面的实验中延迟:
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
的消息队列相关联,发送到并处理@ 987654334@s 和来自队列的消息。那些Runnable
s 和消息都引用了目标Handler
。即使Activity
的onDestroy()
方法不是“析构函数”,即当方法返回时Activity
的实例不会立即被杀死(see),内存不能被GC-ed因为对Activity
的隐式引用*。在Runnable
从Looper
的消息队列中出列并处理之前,您将一直泄漏。
更详细的解释可以在How to Leak a Context: Handlers & Inner Classes找到
* 匿名内部类Runnable
的实例引用了匿名内部类View.OnClickListener
的实例,而该实例又引用了Activity
实例。
【讨论】:
Handler
没有对 Activity
的引用。是什么让你这么认为?
@David Wasser 非常感谢您指出我所犯的错误。
我读过那篇关于Handler
和Context
泄漏内存的文章。在实践中,这通常不是一个真正的问题,除非您有一个 static
变量引用了一个死的 Context
。那将是真正的内存泄漏(即:永远不会回收的内存)。大多数情况下,这些“泄漏”是短暂的,因此它们不是真正的泄漏。正如您所说的“您将一直泄漏,直到 Runnable
被取消队列......”这通常是一个很短的时间。没什么好担心的。【参考方案2】:
您可能需要考虑的不仅仅是将延迟的可运行文件发布到处理程序。当您将任务运行到单独的线程中并且您的活动已被破坏时,您可能会遇到问题。您可以像这样执行和实现。
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);
这样,处理程序消息队列上的任何待处理任务都将在活动被销毁时被销毁。
只是考虑到您知道在 Activity 被销毁后您不需要运行任何任务。
【讨论】:
【参考方案3】:答案是“是”。顺便说一下,这可能会导致Memory Leak
。
【讨论】:
以上是关于是否可以在 onDestroy 之后调用回调方法?的主要内容,如果未能解决你的问题,请参考以下文章
使用 IntentService 进行位置监听,但在 onHandleIntent 之后立即调用 onDestroy
在 JobIntentService 中可以在没有 onHandleWork() 的情况下调用 onDestroy() 吗?