There is any way to prevent MainActivity to be killed?

So, i have a code that opens a media intent on android, to take a picture from the camera, load from an external app or from the gallery, and returns the path to the file and the orientation (an String) to haxe. i found that in a low end device phone, with android 4.4.4, the MainActivity get’s killed randomly (if you choose a camera or an app), so the app crashes why it can’t do the callback to the haxe part.
i’m having a problem like this one: http://stackoverflow.com/questions/9743730/activity-gets-killed-while-executing-the-camera-intent

i’m using openfl legacy btw

i tried 3 approaches:

  1. using a class and an activity directly from the main android manifest, that loads the intent: (like here: https://github.com/ipsilondev/getimagext with the lastest updated files as manifest )

  2. using an Extension based class that loads and Activity, that loads the intent (like the first option, but loading the first Activity from an Extension based class)

  3. using an Extension based class that load the intent directly from mainActivity (like this: https://github.com/josuigoa/CameraMic/blob/master/dependencies/android/src/org/haxe/extension/cameramict/CameraMic.java#L117 )

if it’s helps, this is the best log i could get:

04-30 22:45:24.663: D/ActivityThread(29346): handleBindApplication:com.ipsilondev.salefad 04-30 22:45:26.024: I/Timeline(574): Timeline: Activity_windows_visible id: ActivityRecord{435a4548 u0 com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ t78} time:107617027 04-30 22:45:35.183: I/WindowState(574): WIN DEATH: Window{432584f8 u0 com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ} 04-30 22:45:35.183: I/ActivityManager(574): Process com.ipsilondev.salefad (pid 28342) (adj 9) has died.(41,49) 04-30 22:45:35.203: I/WindowState(574): WIN DEATH: Window{42ef85e0 u0 com.ipsilondev.salefad/com.ipsilondev.salefad.MainActivity} 04-30 22:45:35.223: I/ActivityManager(574): Process com.ipsilondev.salefad (pid 29346) (adj 9) has died.(40,49) 04-30 22:45:41.159: I/ActivityManager(574): Start proc com.ipsilondev.salefad for activity com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ: pid=31283 uid=10112 gids={50112, 3003, 1028, 1015} 04-30 22:45:41.209: D/ActivityThread(31283): handleBindApplication:com.ipsilondev.salefad 04-30 22:45:41.329: D/shadow(31283): the getExternalFileDir1 is = /storage/emulated/0/Android/data/com.ipsilondev.salefad/files 04-30 22:45:41.449: E/AndroidRuntime(31283): Process: com.ipsilondev.salefad, PID: 31283 04-30 22:45:41.449: E/AndroidRuntime(31283): java.lang.RuntimeException: Unable to resume activity {com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1888, result=-1, data=Intent { dat=content://media/external/images/media/3150 (has extras) }} to activity {com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ}: java.lang.NullPointerException 04-30 22:45:41.449: E/AndroidRuntime(31283): Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1888, result=-1, data=Intent { dat=content://media/external/images/media/3150 (has extras) }} to activity {com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ}: java.lang.NullPointerException 04-30 22:45:41.559: D/CrashAnrDetector(574): processName: com.ipsilondev.salefad 04-30 22:45:41.559: D/CrashAnrDetector(574): broadcastEvent : com.ipsilondev.salefad data_app_crash 04-30 22:45:41.869: W/ActivityManager(574): Activity pause timeout for ActivityRecord{435a4548 u0 com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ t78} 04-30 22:45:41.889: I/ActivityManager(574): Start proc com.ipsilondev.salefad for activity android/com.android.internal.app.ChooserActivity: pid=31303 uid=1000 gids={41000, 3003, 3002, 3001, 1028, 1015, 1023, 1007, 2001, 1001, 1024} 04-30 22:45:41.919: D/ActivityThread(31303): handleBindApplication:com.ipsilondev.salefad 04-30 22:45:52.370: W/ActivityManager(574): Activity stop timeout for ActivityRecord{435a4548 u0 com.ipsilondev.salefad/com.ipsilondev.tryoutonme.IntentManagerZ t78}

the best results, i had it with 1), but i have the same with all. is really killing me why i can’t have those kind of random crashes on production. if anyone has ANY idea of what i could do, i’m all ears :smiley:

Android can kill you app when app is not in foreground.
This is really normal situation, you shouldn’t fight with it.

I understand, that it pretty easy to do it right in native android app, but it much harder to do it right for android in openfl app.

There is one bulletproof solution, which should work well for any app (native or openfl):

  • onResume() - restore app state from the file (if it exists)
  • onPause() - save app state to file

By app state I mean everything you need to restore app, including current opened screen and etc.

So:

  • User opens your app
  • onResume() called, but nothing restored, because there is no saved state
  • User press some button to take a photo
  • Camera activity starts to open
    • onPause() called, your app save state to file
    • Camera opened
  • App may be killed, may be not
  • User takes a photo
  • Camera activity starts to close
    • Camera closed
    • If app was killed, it will be restarted
    • onResume() called, your app restore its state (to simplify the process, app restore its state even if it is not killed)
  • onActivityResult() called, app retrieves photo

See activity lifecycle for more info: http://developer.android.com/intl/ru/images/activity_lifecycle.png


onResume() / onPause() is preferred way to deal with android.

But I understand that saving and restoring of the app state is not easy to implement, especially if you didn’t care of it from the beginning.

There is a trick. You can ask android not to kill your app.

You need a service. Just before opening camera app, start that service, and stop when camera closed.

context.startService(new Intent(context, YourService.class)); // to start service
...
context.stopService(new Intent(context, YourService.class)); // to stop service

In service:

protected static final int NOTIFICATION_ID = 1;

protected boolean isForegroundStarted = false;

@Override
public void onCreate() {
    super.onCreate();

    isForegroundStarted = false;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (!isForegroundStarted) {
        isForegroundStarted = true;

        NotificationCompat.Builder builder = new NotificationCompat.Builder(App.self)
            .setSmallIcon(R.drawable.some_icon)
            .setContentIntent(...) // you MUST pass some content intent
            .setContentTitle(R.string.some_title)
            .setContentText(R.string.some_description)
            .setAutoCancel(false)
            .setOngoing(true);

        startForeground(NOTIFICATION_ID, builder.build()); // Ask Android not to kill app
    }

    return Service.START_STICKY;
}

But with this approach you will have extra notification in notification bar. There is another trick. You can immediately start another service in foreground using the same notification id, than stop second service.

Voila! First service is in foreground, and without notification (more info - http://android-devhelp.blogspot.com.by/2015/12/making-service-run-in-foreground.html)

Important note: Foreground service didn’t give you 100% guarantee! Even with foreground service, android still can kill your app. So first approach (with app state) is better, but harder.

Hope it helps.

Thanks for the long and detailed help.

In the end, i have used option 1), with a combination of sharedpreferences to save the paths, before restarting the MainActivity (that can’t be resumed, android kills it all for memory purposes) and modified the haxe code to restore the screen based on that.

For things like this, android is really a pain in the ass, it’s like coding for J2ME, “this is consuming too much, so fuck you, i’m going to erase it, don’t care”.

Someday we will get rid of things like this? i hope so :frowning: