androidkotlinandroid-alertdialogandroid-application-class

Handling errors in Application class


I have an Android app with most of my logic written in C++ and Kotlin is used for UI or interacting with Android's SDK.

When a non-recoverable error occurs, the preference is to display an alert informing user of the remedy steps. AlertDialog is used to inform the user.

// In Activity class
public fun displayAlert() {

    log("MainActivity.displayAlert()")

    val builder: AlertDialog.Builder = AlertDialog.Builder(MainApplication.sForeGroundActivity)

    builder.setTitle("PANIC")
    builder.setMessage("Something went wrong!!")
    builder.setCancelable(false)
    builder.setPositiveButton("Restart") { _, _ ->
        log("Restart the app...")
    }

        builder.setNegativeButton("Cancel") { _, _ ->
        log("Cancel!")
    }

    val alertdialog: AlertDialog = builder.create()
    alertdialog.show()
}

But what if an error occurs in the Application class? The above method cannot be used because AlertDialog.Builder needs Activity context.
Perhaps it's possible to defer the error display until an Activity is launched. One way to achieve this is to use a static variable to record if an error occurred or not... If error occurred in Application class, there can be if-checks in all lifecycle methods of all Activitys to not execute their logic but to display an alert in Activity's onCreate method. Another way is to 'post' an event to the main thread... It is an observation that whenever an event is 'posted', it always gets picked up after the Activity gets invoked.

Here's an example of a failure in Application's onCreate method. The C++ library is loaded and in JNI_onLoad, JNI classes and methodIDs, which are later needed for JNI, are loaded.

class MainApplication : Application() {

    override fun onCreate() {

        log("MainApplication.onCreate()")
        super.onCreate()

        try {
            // Loading our Native Library
            System.loadLibrary("androidfailureresponse")
        } catch (pException: UnsatisfiedLinkError) {
            log(pException.toString())

            log("Posting an error screen event!")
            Handler(Looper.getMainLooper()).post {

                log("About to display an error screen!")

                // This is invoked on the Main thread
                MainActivity.displayAlert()
            }
        }
    }

    companion object {
        public var sForeGroundActivity: Activity? = null
    }
}

When JNI_OnLoad fails with JNI_ERR, the catch block gets invoked.

This way of displaying an error screen is not documented. It was only an observation that the event posted to the main thread is invoked after an Activity is launched. Uf the event is picked up immediately after Application's onCreate method, the above way of 'posting' an event won't work. Is there an elegant way to handle failure when Activity context is not available?

Update 1: In the scenario described in this post, JNI_OnLoad was taken as an example. The errors in this code should ideally be solved while testing itself, but there are lot of other things happening in Application.onCreate. The entire initialize which is required for the Application is happening here. In addition to Activity, there are the following components:

The common application-level initialize (such as initializing C++ objects) happens in Application.onCreate.


Solution

  • Ok, a lot of bad code here.

    First off- I really wouldn't worry about unsatisified link errors like that. If one occurs, it will always occur since the library is bundled with your app. SO that should be caught in testing. Also, given your app's main logic is in C, there isn't much value in catching it and continuing- you won't work properly anyway.

    Secondly- MainApplication.sForeGroundActivity. If this means you're holding a reference to the foreground activity in a variable- NEVER do that. First off it can change for dozens of reasons, activity recreation is a thing. Secondly, it's a MASSIVE memory leak. Activities should never be held in companion object (or in Java, static) variables.

    Third- you couldn't call on a function of an Activity from Application anyway. You haven't created one yet.

    Fourth- you can't display an AlertDialog from the Application class. IT doesn't have the right to draw tot he screen.

    Fifth- While its not necessarily a bug, it is very unusual to load a library from the application class. Generally whatever class is providing the JNI bindings will handle the loading of the library.

    Sixth- even if the rest of this wasn't true, that post to the main looper would be a giant race condition anyway. Lots of things happen on the main looper, and you have no reason to think that the Activity would be displayed at the time the message is processed.

    If you really want to show an error screen in this case (I don't think its needed), and if you really want to load the library is the Application class, set a variable on the Application instance that holds the success/failure status. Then have MainActivity check that variable and display the AlertDialog itself if necessary. But under no circumstances should an Application attempt to draw.