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 :
- 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
Cookies are important to the proper functioning of a site. To improve your experience, we use cookies to remember log-in details and provide secure log-in, collect statistics to optimize site functionality, and deliver content tailored to your interests. Click Agree and Proceed to accept cookies and go directly to the site or click on View Cookie Settings to see detailed descriptions of the types of cookies and choose whether to accept certain cookies while on the site.
About Author
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.