Android: Postmortem Reports via email

How can I get postmortem exception information from my deployed app without requiring net access?

One of the major breakthroughs I have had with my Android development has been the addition of a postmortem exception reporter into my apps. I started out my development on the emulator only as I did not have an Android phone of my own yet. I ran into the dilemma of having code that worked fine on the emulator, but crashed on some phones out in the field. Comments were pouring into my app with 1 star ratings and “FC on open”. I searched far and wide for a solution to use with postmortem crash analysis, but each one I researched required net access on the phone in order to report back. I was not going to force the requirement onto my fans because I don’t trust apps that should not need net access but do, so why should I force my fans to trust me?

I found out about the Thread.getDefaultUncaughtExceptionHandler() and decided to look into the matter myself. One of the major inspirations for an eventual solution came from this article (it’s a good read, you should check it out, too). I came up with a class that will catch the exception and save it to disk. That much has been done before, but what I did differently was to immediately read the contents of that file and send out an Intent to the system to email it back to my support email address. I don’t forcibly send the email, which would require net permission. Instead, the Intent brings up the user’s email client and populates an email with my report, ready to send. I decided against using a file attachment because the goal here is to be transparent about what we are reporting back so that it calms any fears the user may have about exposing personal information. By placing the report directly into the body of the email, they can read it for themselves to be certain about what is, and is not, being sent. There is a message at the top of the email to the user explaining the situation and asking them nicely to send the report by just hitting the Send button. I arranged the email layout so that there is a blank line at the top so they can type in anything they wish to add to the report. Encouraging them to describe what they were doing at the time and actually getting a report with such info is sometimes invaluable. They can just as easily hit Discard, too, but there is usually someone out there that is willing to send the report to you. Sometimes all it takes is one report to fix an issue. It has been my experience that even those who are using a pirated version of your app will send in error reports.

The class is fairly generic and should compile and work with minimal changes to adapt it to your needs. The static strings at the beginning of the class are the main ones you will have to change in order to make it a part of your Android app infrastructure: package namespace, email address, message to your fans, etc. The getDebugReport() method is where the report itself is constructed. It is coded to work on Android 1.5+ and contains the exception message, the activity stack trace (see next paragraph), the instruction stack trace, the cause trace (if available) and a smattering of non-personal phone environment information like make, model, and OS version. Feel free to add log dumps and whatever else you need in order to make your life debugging field errors bearable. Keep in mind that whatever information you gather will be reviewed by the person using the phone, so make certain you do not report on anything people would object to providing or you may upset them. I would advise against localizing any of the report itself as you will need it in whatever language your developers want and don’t really care if it is understandable by your end user. That is why I provided the “MSG_BODY” static string as something that could be localized for a user. Seeing a bunch of stack traces isn’t going to make anything more understandable whether it’s in their local language or not.

If you have several Activities in your app, you will want to place a¬†PostMortemReportExceptionHandler¬†object in each Activity. The reason you want one in each Activity is not for extra protection, but for additional debug information not easily gathered in any other way. An Activity stack trace is created by querying the chain of debug handlers currently in effect. The Activity’s class name and current title will appear in the stack. Additionally, if getCallingActivity() returns a non-null value, the Intent information supplied by the caller will also appear in the stack. The Activity stack information may help retrace the steps taken to encounter the exception being reported. The debug report will only be sent once no matter how many handlers you have in the chain.

Now to show you how easy this class is to use. First, you want to create a field in your activity class to create an instance of this postmortem reporter. Then, in the onCreate method of the Activity, call the initialize() method. Anything after that line of code will be “protected” by the damage report mechanism and will remain in effect until the class is destroyed. onDestory() calls restoreOriginalHandler() explicitly so that we don’t rely on the garbage collector to restore the original handler chain. That’s it!

public class MyActivity extends Activity {
    protected PostMortemReportExceptionHandler mDamageReport = new PostMortemReportExceptionHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDamageReport.initialize();
    }

    @Override
    protected void onDestroy() {
        mDamageReport.restoreOriginalHandler();
        mDamageReport = null;
        super.onDestroy();
    }
}

In addition to providing uncaught exception reporting, you can call the reporter with exceptions you do catch and wish to know about, maybe as a way of providing a graceful death experience. The method to use in that case would be submit().

try {
    //... do stuff here ...
} catch (Exception e) {
    mDamageReport.submit(e);
}

I don’t really recommend using the submit() method since you should be handling known exception possibilities without needing to report them back to you from the field, but there is a rare instance where it might be desirable to do so.

By incorporating this class into my apps, I have been able to greatly improve my quality and reaction time to fix bugs that get missed during development. Sometimes the various phone manufacturers have bugs in their phones that you need to work around and cannot predict until you’ve deployed (rare, but I have had once instance where this happened). In any case, I hope this helps you out as much as it has for me.

Enjoy!

=> Full Source

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>