Gracefully handle uncaught exceptions and force close issue in android

Posted By : Ajit Jati | 04-May-2016

Yeah, we have the ability to handle uncaught java exceptions in android that causes the app to force close.

This method is tested and seems to be very handy in terms of having control of the actions that needs to be done after an uncaught exception caused in the app.

Follow these below instructions to understand the process :

  1. Create a class ExceptionHandler which implements Thread.UncaughtExceptionHandler

import java.io.PrintWriter;
import java.io.StringWriter;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

public class ExceptionHandler implements
        Thread.UncaughtExceptionHandler {
    private static Activity myContext=null;
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");

    public ExceptionHandler(Activity context) {
        myContext = context;
    }

    public void uncaughtException(Thread thread, Throwable exception) {
        prepareLogs(exception);
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(10);
    }

    //*********************************************************
    public static void reportLogs(String errorLogs) {
        Logger.LogError("custom error",errorLogs.toString());
        //Open Send log activity
        Intent intent = new Intent();
        intent.setAction("**.controller.logger.SendLogActivity"); // see step 5.
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
        intent.putExtra("logs", errorLogs.toString());
        myContext.startActivity(intent);
    }

    public static void prepareLogs(Throwable exception){
        StringWriter stackTrace = new StringWriter();
        exception.printStackTrace(new PrintWriter(stackTrace));
        StringBuilder errorReport = new StringBuilder();
        errorReport.append("************ CAUSE OF ERROR ************" + LINE_SEPARATOR);
        errorReport.append(stackTrace.toString());

        Long tsLong = System.currentTimeMillis()/1000;
        String ts = tsLong.toString();
        errorReport.append( "************ Timestamp ************" + ts);
        errorReport.append(LINE_SEPARATOR+ "************ DEVICE INFORMATION ***********" + LINE_SEPARATOR);
        errorReport.append("Brand: "+Build.BRAND);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append("Device: "+Build.DEVICE);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append("Model: "+Build.MODEL);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append("Id: "+Build.ID);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append("Product: "+Build.PRODUCT);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append(LINE_SEPARATOR + "************ BUILD INFO ************" + LINE_SEPARATOR);
        errorReport.append("SDK: "+Build.VERSION.SDK);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append("Release: "+Build.VERSION.RELEASE);
        errorReport.append(LINE_SEPARATOR);
        errorReport.append("Incremental: "+Build.VERSION.INCREMENTAL);
        errorReport.append(LINE_SEPARATOR);
        reportLogs(errorReport.toString());
    }
}
        

 

2. Add this line in every activity where you want to handle the exceptions :

Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));

3. Now create a custom SendLogActivity which will use the collected exception logs.In our case we will prompt user to send to the developer



import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.Window;



/**
 * Created by Ajit on 18/4/16.
 */
public class SendLogActivity extends Activity implements View.OnClickListener {
    private AlertDialog alertDialog;
    private String logs;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
        setFinishOnTouchOutside(false); // prevent users from dismissing the dialog by tapping outside
        setContentView(R.layout.log_activity);
        logs = getIntent().getStringExtra("logs");
        showConfirmation();
    }

    @Override
    public void onClick(View v) {
        // respond to button clicks in your UI

    }

    private void sendLogFile() {
        if (logs == null)
            return;

        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("plain/text");
        intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"[email protected]"});
        intent.putExtra(Intent.EXTRA_SUBJECT, "Error reported from MyAPP");
        intent.putExtra(Intent.EXTRA_TEXT, "Log file attached."+logs); // do this so some email clients don't complain about empty body.
        startActivity(intent);
    }

    private void showConfirmation() {
        // method as shown above

            alertDialog = new AlertDialog.Builder(SendLogActivity.this).create();
            alertDialog.setTitle("Report Error!");
            alertDialog.setMessage("Ah, shoot. Seems like MyAPP faced an unhandled error.Would you like to report it to the developer team?");
            alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Report", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    sendLogFile();
                    finish();

                }
            });
            alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    alertDialog.dismiss();
                    finish();
                }
            });

            alertDialog.show();

    }
}

 

4. Add the activity info in menifestfile

<activity
    android:name="*.controller.logger.SendLogActivity"
    android:theme="@android:style/Theme.Dialog"
    android:textAppearance="@android:style/TextAppearance.Large"
    android:windowSoftInputMode="stateHidden">
    <intent-filter>
        <action android:name="*.controller.logger.SendLogActivity" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

5. please note : Use any blank layout for log_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone">

</LinearLayout>

Now we are good to go. We can generate any exceptions to test the process.

Please add the below line anywhere in the activity where you have added the uncaughtException hook.

int testingInt = Integer.parseInt("10.576");

Cheers!!

 

THANKS

About Author

Author Image
Ajit Jati

Ajit is an proficient Project Manager specializing in Mobile technologies. He possesses extensive development experience in Titanium, Android, Kotlin, Roku, and PHP. Ajit has successfully delivered various projects in the OTT, AR/VR, and Travel industries. His skill set includes developing and maintaining project plans, schedules, and budgets, ensuring timely delivery while staying within the allocated budget. He excels in collaborating closely with clients to define project scope and requirements, establish project timelines and milestones, and effectively manage expectations. He conducts regular project status meetings, ensuring effective communication and providing updates to clients and stakeholders on project progress, risks, and issues. Furthermore, Ajit takes on the role of a coach and mentor for team,offering guidance on project management best practices and assisting in their skill development.

Request for Proposal

Name is required

Comment is required

Sending message..