问题
In the latest version of my app, some users are experiencing a crash that I'm unable to reproduce. Currently only Samsung
devices running Lollipop
are having the issue, but that might just be coincidence.
After analyzing the stack trace and relevant code, I think that I may have found the culprit. To test my assumption, I simplified the code to the snippet below:
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");
}
}
Every time I test the above application by first tapping the Click me button and then the back button, listenerNotified
is printed to the console before onDestroy()
.
I'm however not sure if I can rely on this behavior. Does Android
make any guarantees about the above scenario? Can I safely assume that my Runnable
will always be executed before onDestroy()
or is there a scenario where that won't be the case? In my real app, there is of course a lot more happening (like other threads posting to the main thread and more operations happening in the callback). But this simple snippet seemed sufficient to demonstrate my concern.
Is it every possible (possibly due to the influence of other threads or callbacks posted to the main thread) that I get the debug output below?
D/TAG: onDestroy
D/TAG: listenerNotified
I would like to know this, since that outcome being possible would explain the crash.
回答1:
Is it possible for a callback method to be called after
onDestroy()
?
Yes.
Let's change a bit your sample code regarding posting a Runnable
to the Handler
. I also assume (according to your description) that you may have multiple Runnable
s posted to the main thread, so at some point there might be a queue of Runnable
s which brings me to a delay in the experiment below:
public void onClick(View view) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// This is the callback method
Log.d("TAG", "listenerNotified");
}
}, 3000);
}
Now push the button b
, then press the back button and you should see the output in question.
Might it be the reason of your app crash?
It's hard to say without seeing what you got. I'd just like to note that when new Handler()
is instantiated on a thread (the main thread in your case), the Handler
is associated with the Looper
's message queue of the thread, sending to and processing Runnable
s and messages from the queue. Those Runnable
s and messages have a reference to the target Handler
. Even though Activity
's onDestroy()
method isn't a "destructor", i.e. when the method returns the Activity
's instance won't be immediately killed (see), the memory cannot be GC-ed because of the implicit reference* to the Activity
. You'll be leaking until the Runnable
will be de-queued from the Looper
's message queue and processed.
A more detailed explanation can be found on How to Leak a Context: Handlers & Inner Classes
* Instance of anonymous inner class Runnable
has a refers to an instance of anonymous inner class View.OnClickListener
that, in its turn, has a reference to the Activity
instance.
回答2:
You might need to consider not just posting delayed runnable to handler. You might encounter issues when your running task into a separate thread and your activity is already destroyed. You can do and implement like this.
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);
}
}
}
By this, any pending task on handler message queue will be destroyed upon activity is destroyed.
Only to make consider that you are aware that you dont need to run any task after the activity is destroyed.
回答3:
The answer comes to be "yes".And this may cause Memory Leak
by the way.
来源:https://stackoverflow.com/questions/39562914/is-it-possible-for-a-callback-method-to-be-called-after-ondestroy