Android Tutorial Auto restart an Android Application after a Crash

Sources used:
Main sources:
https://medium.com/@ssaurel/how-to-...r-a-crash-or-a-force-close-error-1a361677c0ce
https://www.b4x.com/android/forum/t...estartatexact-not-accurate.65117/#post-412286
Additional:
https://stackoverflow.com/a/2903866
https://mobikul.com/auto-restart-application-crashforce-close-android/

Known limitations:
This has only been tested with a single activity (Main) application. More explanation to follow below.

Part 1:
The first part involves installing a default uncaught exception handler. The solution requires some inline Java and the JavaObject library.

Add the following inline Java to your Main class:
B4X:
#if Java
//Sources:
//https://medium.com/@ssaurel/how-to-auto-restart-an-android-application-after-a-crash-or-a-force-close-error-1a361677c0ce
//https://stackoverflow.com/a/2903866
//https://mobikul.com/auto-restart-application-crashforce-close-android/

public void setDefaultUncaughtExceptionHandler() {
   
   Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this));
}

import android.app.Activity;

public class MyExceptionHandler implements Thread.UncaughtExceptionHandler {

  private Activity activity;

  public MyExceptionHandler(Activity a) {
    activity = a;
  }

  @Override
  public void uncaughtException(Thread thread, Throwable ex) {
    activity.finish();
    System.exit(2);
  }
}
#End if

Declare the following JavaObject in Globals (adjust name if necessary to avoid conflicts):
B4X:
Private nativeMe As JavaObject

And add the following code in the Activity_Create sub:
B4X:
nativeMe.InitializeContext
nativeMe.RunMethod("setDefaultUncaughtExceptionHandler", Null)

That finishes installing your own default uncaught exception handler. With this in place, when you Main class crashes, the application will exit without requesting the user to restart the application. This is key in allowing the application to restart without user intervention. For applications that are set as the Home screen, this would suffice, allowing Android to restart it without user intervention.

Part 2:
First, a sub that allows us to schedule the starting of an Activity. It is a modified version of @Erel's
SetExactAndAllowWhileIdle sub he posted (the modification allowing it to start an Activity instead of a Service):
B4X:
'Based on: https://www.b4x.com/android/forum/threads/now-android-6-servicestartatexact-not-accurate.65117/#post-412286
'Time: The time that the Activity should start - in ticks
'ActivityName: The name of the Activity that should be started
'Message: A message that will be passed along in the intent to start the Activity (via Extras)
Sub ScheduleRestartCrashedActivity (Time As Long, ActivityName As String, message As String)

   Dim in As Intent
   in.Initialize("", "")
   in.SetComponent(Application.PackageName & "/." &  ActivityName.ToLowerCase)
   in.PutExtra("Crash", message)
   in.Flags = Bit.Or(FLAG_ACTIVITY_CLEAR_TASK, Bit.Or(FLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_NEW_TASK))
   
   Dim jin As JavaObject = in
   jin.RunMethod("setAction", Array(Null))
   
   Dim ctxt As JavaObject
   ctxt.InitializeContext
   Dim am As JavaObject = ctxt.RunMethod("getSystemService", Array("alarm"))
   Dim pi As JavaObject
   pi = pi.InitializeStatic("android.app.PendingIntent").RunMethod("getActivity", Array(ctxt, 0, in, FLAG_ONE_SHOT))
   
   Dim p As Phone
   If p.SdkVersion < 20 Then
       am.RunMethod("set", Array(ALM_RTC, Time, pi))
   Else If p.SdkVersion < 23 Then
       am.RunMethod("setExact", Array(ALM_RTC, Time, pi))
   Else
       am.RunMethod("setExactAndAllowWhileIdle", Array(ALM_RTC, Time, pi))
   End If
   
End Sub
This sub should be placed in the Starter service. You will need to also declare the following in Process_Globals:
B4X:
Private const FLAG_ACTIVITY_CLEAR_TOP As Int = 0x04000000
Private const FLAG_ACTIVITY_CLEAR_TASK As Int = 0x00008000
Private const FLAG_ACTIVITY_NEW_TASK As Int = 0x10000000
Private const FLAG_ONE_SHOT As Int = 0x40000000
   
Private const ALM_RTC As Int = 0x00000001
Private const ALM_RTC_WAKEUP As Int = 0x00000000
The sub needs to be called from the Application_Error method. For example:
B4X:
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
   ScheduleRestartCrashedActivity(DateTime.Now + 300, "Main", Error)
   Return True
End Sub
With all these pieces in place, if your Main activity crashes, it will be scheduled to restart in 300 milliseconds (the + 300). Additionally, you can check Main's starting intent to determine if your app was restarted as a crash. For example, the following code placed in Activity_Create will produce a log after crash:
B4X:
Dim i As Intent
i = Activity.GetStartingIntent
If i.HasExtra("Crash") Then
    Log("After crash: " & i.GetExtra("Crash"))
End If

Notes:
1) Each Activity may need to register their own default uncaught exception handler. If so, it's left up to the reader to determine which Activity needs to be restarted via ScheduleRestartCrashedActivity.
2) I do not know what happens when a Service crashes.
3) The ScheduleRestartCrashedActivity does not require the Starter Service. It does require that a Service handles the Application_Error event.
4) I'm currently using ALM_RTC to schedule the intents. This will not wake the phone. If that is a requirement, replace with ALM_RTC_WAKEUP.
5) Could not decide between a Snippet post or Tutorial post. This seemed a little to long for a Snippet.
 

OliverA

Expert
Licensed User
Longtime User
Check Application_Error in the starter service. You can probably implement something similar based on Application_Error.

The key here seems to be the System.Exit() call. With the above code in place, if an exception occurs that ends the Activity, Application_Error is called first and then the default exception handler. I guess the question is, does anything else happen after Application_Error is called that needs to happen? If so, I'll need to do this to properly close out the app. If, on the other hand, nothing special happens after Application_Error, I probably can call System.Exit (via inline java) in the Application_Error event and stop processing right there and then.
 

sorex

Expert
Licensed User
Longtime User
I confirm that Erel's method does the job aswell.

I'm storing the errors in a map and send them one by one to a webserver that mails them to me and if that was successful I remove the error from the map.

This way nothing gets lost and I was also able to track down issues that never happend at my end.
(for example an error in the wifi lib happening when people were walking around between access points and lost connection and caused a null pointer exeption during a read out of some parameters)
 

OliverA

Expert
Licensed User
Longtime User
I confirm that Erel's method does the job as well.
Yes, but is the user asked to restart the application? That question to the user blocks auto-restarting. I'm also using Application_Error, but I'm calling System.Exit(a-non-zero-value) from a separate default uncaught exception handler which suppresses the restart question (the application/service just stops). The intent set in Application_Error restarts the application at a future time ( Application_Error is executed before the custom default uncaught exception handler). I could (still not tested) call System.Exit in Application_Error, but that would (I think) exit the application without processing whatever B4A does right after calling the Application_Error event. In other words is Application_Error set up like this (pseudo code follows):
B4X:
some code
call Application_Error event
some more code to finish application
or
B4X:
some code
call Application_Error event
//No other code follows the event. The event is the last code the application processes.
//I can just go ahead and make System.Exit my last call in Application_Error
 

sorex

Expert
Licensed User
Longtime User
in my case nothing is asked to the user. the app just continues to work.

I guess the first code block is valid in my case with the exeption that the app doesn't get finished.
 

OliverA

Expert
Licensed User
Longtime User
If the app just continuous, then you are returning False from Application_Error (my assumption is that you are not using my code here to restart your app). The issue with that could be that your app could be in a precarious state, since you did have an uncaught exception. It is up to the programmer to determine if an exception was severe enough to exit an application (therefore the option of true and false as return values for Application_Error).
 

sorex

Expert
Licensed User
Longtime User
well, it's a special case...

it's sound monitoring children with disabilities during nights so the 'sleeping guard' needs to have working apps or he/she needs to walk through the buildings all night to validate that nothing is wrong.

but indeed for some apps a restart or partial reload might be better.
 
Top