onDestroy not fired

Hello,

I want to release ressources when an app is closed (destroyed). I implemented interface OnDestroyListener, but onDestroy was never called. onPause, onStop and onResume work perfectly. I found other having the same problem https://puravidaapps.com/tools.php

Is it a bug in AI2 or what do I have to do to catch this event?

Kind regrards Ulli

1 Like

Did you read the instructions?

“Unfortunately it seems to be not possible to catch the ‘onDestroy’ activity state…”

1 Like

This was an easy fix

2 Likes

https://developer.android.com/reference/android/app/Activity.html#onDestroy()

“Note: do not count on this method being called…There are situations where the system will simply kill the activity’s hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.”

Calling the super method would kill the app and the following code wouldn’t execute. By keeping the super call at the end ensures that the code above would execute. But as you have quoted, there are cases like process death, that can kill the app without even calling onDestroy

2 Likes

Still not firing. As pavi2410 mentioned it results on calling super.onDestroy() before calling the listeners. Some components, e.g. Sound use this method for cleaning up resources. You should fix it.

In particular when your app is in the background it is subject to being killed with a signal 9 (hard kill, no opportunity for cleanup etc.) by the Kernel “Out of Memory (OOM)” handler. I am reviewing Pavi’s change now and it will be in the next release (currently scheduled for March 27th) and likely on ai2-test tonight (I’ll post a message here when ai2-test is updated).

3 Likes

Once again onDestroy event.

I developed an app with two screens (Screen1 & Screen2). I displayed the Log on my PC (tag = “Form”). I got this output:

04-20 20:06:00.751 10531 10531 I Form : Form Screen1 OtherScreenClosed, otherScreenName = Screen2, result =
04-20 20:06:00.754 10531 10531 I Form : Form Screen1 got onResume
04-20 20:06:01.179 10531 10531 I Form : Form Screen2 got onStop
04-20 20:06:01.183 10531 10531 I Form : Form Screen2 got onDestroy
04-20 20:06:06.908 10531 10531 I Form : Form Screen1 got onPause
04-20 20:06:08.065 10531 10531 I Form : Form Screen1 got onStop
04-20 20:06:08.066 10531 10531 I Form : Form Screen1 got onDestroy

You can see: the form class catches the event:

  • When closing Screen2 and returning to Screen1
  • When closing Screen1 and closing the app.

Your hints for “kill task” are true but only for extrem situations. Normally onDestroy is fired correctly.

Looking at the code of form class:

@Override
protected void onDestroy() {

// for debugging and future growth
Log.i(LOG_TAG, "Form " + formName + " got onDestroy");

// Unregister events for components in this form.
EventDispatcher.removeDispatchDelegate(this);

for (OnDestroyListener onDestroyListener : onDestroyListeners) {
  onDestroyListener.onDestroy();
}
super.onDestroy();

}

I compared it with onStop which is fired as expected. I have looked at fields onDestroyListeners and onStopListeners with reflection. Both have the same entry. That means filling the Sets is correctly done.

The only difference between the onDestroy and onStop mechanism is the call to EventDispatcher.removeDispatchDelegate(this). EventDispatcher ist not easy to understand. So I don’t know what removeDispatchDelegate exactly does. But the this call seems to be the problem.

I think the oder of call must be:

  1. call to the listeners in a for loop
  2. call removeDispatchDelegate
  3. call super.onDestroy

You should fix this problem. A lot of your internal components use onDestroy to clean up, e.g VideoPlayer, Sprite, Sound …

This looks right to me. I didn't get what's the issue here. Can you please explain what are you expecting so we can help you better?

All EventDispatcher.removeDispatchDelegate does is remove the Form from the set of delegates for dispatching events. After this call, no more calls to EventDispatcher.dispatchEvent will be emitted to the components of the Form, i.e., no more block event handlers can run. This doesn't affect onDestroy because onDestroy is triggered by the Android runtime.

If you're testing in the companion, make sure you've updated to 2.58a or 2.58au to ensure you have @pavi2410's fix.

Yes, I’m using the newest version.

Now I understand the problem. My misunderstanding (and of others) was:

  • onDestroy is fired (I checked it via Log)
  • but inside onDestroy you cannot expose the event to the app via EventDispatcher.dispatchEvent because EventDispatchers listener list is cleared before the extensions onDestroy is called.

Is it possible to change this behavior or does it lead to conflicts?

I don't think this is something we will plan to support. Generally, App Inventor aims to hide as much as the application lifecycle from users as possible. Once the activity is shutting down there shouldn't be any more blocks code running.

Hi Evan,

your policy is quite strange. I have already found that elsewhere. You decide what is useful for the user and what is not and deprive him of a lot of freedom.

Ok, it’s your product and it’s free. You can do whatever you want with it. But that’s not user-friendly.

In principle, AI2 is a very good product. Thank you for providing it. You can easily write apps without having to worry about the very complex object model of Android. This will attract thousands of users and AI2 is a widely used product. Ok, you shouldn’t expect a user of your product to be very familiar with the Android internals. That’s why he uses AI2. But you should expect that from an extension developer.

Android fires onDestroy so that the app can release resources. Why shouldn’t that apply to AI2 apps? Can’t you imagine that applications or extensions need resources other than those that you provide in your product and that need to be released? Another point is that it is not allowed to create visible extensions. You can see that this is necessary from the many existing workarounds. Unfortunately, it is very time-consuming to design a tidy user interface because you only see these elements when you run the app.

There are now a number of competing products with much more options. Maybe they were created because you are so restrictive? But all of them want to be compatible with the AI2. AI2 is the leader. So all of them have the same restrictions that you dictate. You should think about your policies.

I want to write an app that establishes a TCP connection and receives messages even when the screen is not visible (state paused or stopped). Because of NAT it must be TCP. The app is intended to inform the remote site when it is no longer available. The app should determine how information should be provided and not the extension. Another application is a UDP broadcast when the app is closed.

Triggering onDestroy is not guaranteed. One has to reckon with undefined conditions. But these cases are very rare. That does not justify giving it up entirely. Especially when the state affects the UI of another application.

Kind regards
Ulrich

That's a very niche use case we'll have to think about. Does the data depend on any state at the time the onDestroy event would be fired? If not, then you might be able to provide a property to set the data that needs to be sent and then just have the onDestroy handler consume that and send without involving the blocks code.

To address your comment more broadly, the issue with any change we make to existing functionality is that we need to be reasonably certain it won't break any existing apps. My gut feeling is that this probably wouldn't affect anything, but a gut feeling is not the same as proof. We're currently at 11.3 million users and 48.7 million projects---that's a lot of potential risk to take on that we might break something. It's something we can play with but I can't make any promises it would make it into the base system.