在我的应用的最新版本中,有些用户遇到了我无法重现的崩溃。目前只有运行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()
我想知道这一点,因为可能的结果可以解释崩溃。
答案 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编辑。您将泄漏,直到Activity
从Runnable
的消息队列中排队并进行处理。
可以在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
。