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[]{"ajit.jati@oodlestechnologies.com"});
        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

Request for Proposal

Recaptcha is required.

Sending message..