Can I call the callback method after onDestroy?

In the latest version of my application, some users experience a failure that I cannot reproduce. Currently, only Samsung devices running Lollipop have a problem, but it might just be a coincidence. After analyzing the stack trace and related code, I think I might have found the culprit. To test my assumption, I simplified the code 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"); } } 

Each time I test the above application, first clicking the "Click me" button and then the "Back" button, listenerNotified is printed on the console before onDestroy() .

However, I'm not sure if I can rely on this behavior. Does Android provide any warranties in the above scenario? Is it safe to assume that my Runnable will always be executed before onDestroy() or is there a script where this is not so? In my real application, of course, much more happens (like other threads sent to the main thread, and more operations happening in the callback). But this simple passage seemed sufficient to demonstrate my concern.

Is it possible (possibly due to the influence of other threads or callbacks sent to the main thread) that I get the debug output below?

 D/TAG: onDestroy D/TAG: listenerNotified 

I would like to know this, as this result would be possible, explaining the failure.

+5
source share
3 answers

Is it possible to call the callback method after onDestroy() ?

Yes.

Modify your sample code slightly regarding the placement of Runnable in the Handler . I also assume (according to your description) that you may have several Runnable sent to the main thread, so at some point the Runnable queue may occur, which will lead me to a delay in the following experiment:

 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 press the b button, then press the "Back" button and you will see the corresponding result.

Might it be the reason of your app crash? It's hard to say without seeing what you have. I would like to note that when a new Handler() is created in the thread (the main thread in your case), the Handler is connected to the thread's Looper message chain, sends and processes Runnable and messages from the queue. Those Runnable and posts have a link to the Handler target. Although the Activity onDestroy() method is not a "destructor", i.e. When a method returns an instance of an Activity , it will not be immediately killed ( see ), memory cannot be GC-ed due to an implicit * reference to an Activity . You will continue to Runnable until the Runnable is queued from the Looper message queue and processed.

A more detailed explanation can be found on How to leak a context: handlers and inner classes


* An instance of the anonymous inner class Runnable has a refers to an instance of the anonymous inner class View.OnClickListener , which, in turn, has a reference to an instance of Activity .Sub>

+3
source

You may need to do more than just delay the execution delay. You may encounter problems when the task being performed in 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); } } } 

Thus, any waiting task in the message queue of the handler will be destroyed when the activity is destroyed.

Just to make sure that you know that you do not need to run any tasks after the activity is destroyed.

+1
source

The answer is yes. And that could lead to Memory Leak way.

0
source

All Articles