Android saves program crash information to local files

Source: Internet
Author: User

As we all know, the versions and devices used to install the Android system vary widely. A program running well on the simulator may crash when it is installed on a certain mobile phone, individual developers cannot buy all devices for debugging one by one. Therefore, after the program is released, if a crash occurs, the developer should promptly obtain the information that causes the crash on the device, this is of great help to fix bugs in the next version. So today we will introduce how to collect related device parameter information and specific exception information when the program crashes, and send the information to the server for developers to analyze and debug the program.

 

Source code: http://download.csdn.net/detail/weidi1989/4588310

 

We first set up a crash project and project structure

 

In mainactivity. Java code, we intentionally create an incorrect example to facilitate our experiment:

package com.scott.crash;import android.app.Activity;import android.os.Bundle;public class MainActivity extends Activity {private String s;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        System.out.println(s.equals("any string"));    }}

We intentionally created a potential runtime exception here. When we run the program, the following interface will appear:

When an exception is not captured by the software, the default force close dialog box is displayed.

Of course, we do not want users to see this phenomenon. It is a blow to the user's mind, and it is useless to fix our bugs. What we need is that the software has a global exception catcher. When an exception is not found, capture the exception and record the exception information, upload the file to the server for public publishing.

Next we will implement this mechanism, but first we will understand the following two classes: Android. App. Application and Java. Lang. thread. uncaughtexceptionhandler.

Application:Used to manage the global status of an application. When the application starts, the application will first create and then start the corresponding activity and service according to the situation (intent. In this example, no exception processor is registered in the Custom enhanced application.

Thread. uncaughtexceptionhandler:The thread does not capture an exception processor, which is used to handle an uncaptured exception. If an uncaptured exception occurs in the program, the force close dialog box is displayed by default. We need to implement this interface and register it as a program without capturing exception handling by default. In this way, some personalized exception handling operations can be performed when no exceptions are captured.

The crashhandler. Java we saw in the project structure just now implements thread. uncaughtexceptionhandler, so that we can use it to handle major members who do not capture exceptions. The Code is as follows:

Package COM. way. crash; import Java. io. file; import Java. io. filenotfoundexception; import Java. io. fileoutputstream; import Java. io. ioexception; import Java. io. printwriter; import Java. io. stringwriter; import Java. io. writer; import Java. lang. thread. uncaughtexceptionhandler; import Java. lang. reflect. field; import Java. text. simpledateformat; import Java. util. date; import Java. util. hashmap; import Java. util. map; impo RT android. content. context; import android. content. PM. packageinfo; import android. content. PM. packagemanager; import android. content. PM. packagemanager. namenotfoundexception; import android. OS. build; import android. OS. environment; import android. OS. logoff; import android. util. log; import android. widget. toast;/*** uncaughtexception processing class. When an uncaught exception occurs in a program, this class takes over the program and records the sending of error reports. ** @ author way **/public class C Rashhandler implements uncaughtexceptionhandler {Private Static final string tag = "crashhandler"; private thread. uncaughtexceptionhandler mdefaulthandler; // system default uncaughtexception processing class Private Static crashhandler instance = new crashhandler (); // crashhandler instance private context mcontext; // context object private map of the Program <string, string> info = new hashmap <string, string> (); // used to store device information and exception information private simpledateform At format = new simpledateformat ("yyyy-mm-dd-hh-mm-SS"); // used to format a date, as part of the log file name/** ensure that there is only one crashhandler instance */private crashhandler () {}/ ** to obtain the crashhandler instance. Singleton mode */public static crashhandler getinstance () {return instance;}/*** initialize ** @ Param context */Public void Init (context) {mcontext = context; mdefaulthandler = thread. getdefaultuncaughtexceptionhandler (); // gets the default uncaughtexception processor threa. D. setdefaultuncaughtexceptionhandler (this ); // set the crashhandler as the default processor of the program}/*** when uncaughtexception occurs, it will be transferred to the rewrite method for processing */Public void uncaughtexception (thread, throwable ex) {If (! Handleexception (Ex) & mdefaulthandler! = NULL) {// If the Custom Handler is not processed, the system's default exception processor is used to handle mdefaulthandler. uncaughtexception (thread, ex);} else {try {thread. sleep (3000); // If the processing is completed, let the program continue to run for 3 seconds and exit. Ensure that the file is saved and uploaded to the server} catch (interruptedexception e) {e. printstacktrace ();} // exit the android program. OS. process. killprocess (Android. OS. process. mypid (); system. exit (1) ;}/ *** custom error handling, collecting error information, and sending error reports are all completed here. ** @ Param ex * exception information * @ return true if the exception information is processed; otherwise, false is returned. */Public Boolean H Andleexception (throwable ex) {If (EX = NULL) return false; new thread () {public void run () {loid. prepare (); toast. maketext (mcontext, "Sorry, the program has an exception and is about to exit", 0 ). show (); logoff. loop ();}}. start (); // collect device parameter information collectdeviceinfo (mcontext); // Save the log file savecrashinfo2file (Ex); return true ;} /*** collect device parameter information ** @ Param context */Public void collectdeviceinfo (context) {try {packagemanager PM = context. getpackagema Nager (); // obtain packageinfo Pi = PM. getpackageinfo (context. getpackagename (), packagemanager. get_activities); // get the application information, that is, the main activityif (Pi! = NULL) {string versionname = pi. versionname = NULL? "Null": pi. versionname; string versioncode = pi. versioncode + ""; info. put ("versionname", versionname); info. put ("versioncode", versioncode) ;}} catch (namenotfoundexception e) {e. printstacktrace ();} field [] fields = build. class. getdeclaredfields (); // reflection mechanism for (field: fields) {try {field. setaccessible (true); info. put (field. getname (), field. get (""). tostring (); log. D (TAG, field. getname () + ":" + FIE LD. get ("");} catch (illegalargumentexception e) {e. printstacktrace ();} catch (illegalaccessexception e) {e. printstacktrace () ;}} private string savecrashinfo2file (throwable ex) {stringbuffer sb = new stringbuffer (); For (map. entry <string, string> entry: info. entryset () {string key = entry. getkey (); string value = entry. getvalue (); sb. append (Key + "=" + value + "\ r \ n");} writer = new stringwri Ter (); printwriter PW = new printwriter (writer); Ex. printstacktrace (PW); throwable cause = ex. getcause (); // cyclically write all exception information into writer while (cause! = NULL) {cause. printstacktrace (PW); cause = cause. getcause ();} PW. close (); // remember to close string result = writer. tostring (); sb. append (result); // save the file long timetamp = system. currenttimemillis (); string time = format. format (new date (); string filename = "crash-" + time + "-" + timetamp + ". log "; if (environment. getexternalstoragestate (). equals (environment. media_mounted) {try {file dir = new file (environment. getex Ternalstoragedirectory (). getabsolutepath () + file. Separator + "crash"); log. I ("crashhandler", dir. tostring (); If (! Dir. exists () dir. mkdir (); fileoutputstream Fos = new fileoutputstream (new file (Dir, filename); FOS. write (sb. tostring (). getbytes (); FOS. close (); Return filename;} catch (filenotfoundexception e) {e. printstacktrace ();} catch (ioexception e) {e. printstacktrace () ;}} return NULL ;}}

 

Then, we need to register in the application when the application is started:

package com.way.crash;import android.app.Application;public class CrashApplication extends Application {@Overridepublic void onCreate() {super.onCreate();CrashHandler crashHandler = CrashHandler.getInstance();crashHandler.init(this);}}


Finally, in order for our crashapplication to replace Android. App. Application to take effect in our code, we need to modify androidmanifest. xml:

 <application android:name=".CrashApplication"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >   ... </application>


Because in the crashhandler above, the device parameters and specific exception information must be saved to the sdcard after an exception occurs, we need to add the Read and Write sdcard permission to androidmanifest. xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

After completing the above steps, let's run the project:

As you can see, there will not be a forced close dialog box, instead we have a better prompt.

Next, let's take a look at the sdcard file:

Open the log file in a text editor to view a piece of log information:

CPU_ABI=armeabiCPU_ABI2=unknownID=FRF91MANUFACTURER=unknownBRAND=genericTYPE=eng......Caused by: java.lang.NullPointerExceptionat com.scott.crash.MainActivity.onCreate(MainActivity.java:13)at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)... 11 more

This information is of great help to developers, so we need to upload this log file to the server.

The following is a method for submitting an error report by email (added on April 9, June 06, 2013 ):

Since context is a non-activity context, I used the system Windows attribute for the pop-up dialog box. Remember to add the following permissions:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> 

/*** Uncaughtexception processing class. When an uncaught exception occurs in a program, this class takes over the program and records the error report. ** @ author way **/public class crashhandler implements uncaughtexceptionhandler {private thread. uncaughtexceptionhandler mdefaulthandler; // The default uncaughtexception class Private Static crashhandler instance; // The crashhandler instance private context mcontext; // The context object of the Program/** ensure that there is only one crashhandler instance */private crashhandler () {}/ ** to obtain the crashhandler instance, Singleton mode */public static crashhandler getinstance () {If (instance = NULL) instance = new crashhandler (); Return instance ;} /*** initialize ** @ Param context */Public void Init (context) {mcontext = context; mdefaulthandler = thread. getdefaultuncaughtexceptionhandler (); // gets the default uncaughtexception processor thread. setdefaultuncaughtexceptionhandler (this); // set the crashhandler as the default processor of the program}/*** transfer when uncaughtexception occurs This override method is used to handle */Public void uncaughtexception (thread, throwable ex) {If (! Handleexception (Ex) & mdefaulthandler! = NULL) {// If the Custom Handler is not processed, the system's default exception processor is used to handle mdefaulthandler. uncaughtexception (thread, ex) ;}/ *** custom error handling, collecting error information and sending error reports are all completed here. ** @ Param ex * exception information * @ return true if the exception information is processed; otherwise, false is returned. */Public Boolean handleexception (throwable ex) {If (EX = NULL | mcontext = NULL) return false; final string crashreport = getcrashreport (mcontext, ex); log. I ("error", crashreport); New thread () {public void run () {Looper. P Repare (); file = save2file (crashreport); sendappcrashreport (mcontext, crashreport, file); logoff. loop ();}}. start (); Return true;} private file save2file (string crashreport) {// todo auto-generated method stubstring filename = "crash-" + system. currenttimemillis () + ". TXT "; if (environment. getexternalstoragestate (). equals (environment. media_mounted) {try {file dir = new file (environment. getexternals Toragedirectory (). getabsolutepath () + file. Separator + "crash"); If (! Dir. exists () dir. mkdir (); file = new file (Dir, filename); fileoutputstream Fos = new fileoutputstream (File); FOS. write (crashreport. tostring (). getbytes (); FOS. close (); Return file;} catch (filenotfoundexception e) {e. printstacktrace ();} catch (ioexception e) {e. printstacktrace () ;}} return NULL;} private void sendappcrashreport (final context, final string crashreport, final file) {// todo Auto-generated method stubalertdialog mdialog = NULL; alertdialog. builder = new alertdialog. builder (context); builder. seticon (Android. r. drawable. ic_dialog_info); builder. settitle ("program error"); builder. setmessage ("Please submit the error report to us by email. Thank you! "); Builder. setpositivebutton (Android. r. string. OK, new dialoginterface. onclicklistener () {public void onclick (dialoginterface dialog, int which) {// send the exception report try {// the comments section contains the error message sent in text format // intent = new intent (intent. action_sendto); // intent. settype ("text/plain"); // intent. putextra (intent. extra_subject, // "android client-Error Report"); // intent. putextra (intent. extra_text, crashreport); // intent. setdata (Uri/ /. Parse ("mailto: way.ping.li@gmail.com"); // intent. addflags (intent. flag_activity_new_task); // context. startactivity (intent); // The email intent = new intent (intent. action_send); intent. addflags (intent. flag_activity_new_task); string [] TOS = {"way.ping.li@gmail.com"}; intent. putextra (intent. extra_email, TOS); intent. putextra (intent. extra_subject, "android client-Error Report"); If (file! = NULL) {intent. putextra (intent. extra_stream, Uri. fromfile (File); intent. putextra (intent. extra_text, "Please send this error report to me so that I can fix this problem as soon as possible. Thank you for your cooperation! \ N ") ;}else {intent. putextra (intent. extra_text," Please send this error report to me so that I can fix it as soon as possible. Thank you for your cooperation! \ N "+ crashreport);} intent. settype ("text/plain"); intent. settype ("message/rfc882"); intent. createchooser (intent, "Choose email client"); context. startactivity (intent);} catch (exception e) {toast. maketext (context, "There are no email clients installed. ", toast. length_short ). show ();} finally {dialog. dismiss (); // exit android. OS. process. killprocess (Android. OS. process. mypid (); system. exit (1) ;}}}); builder. setnegativebutton (Android. r. string. cancel, new dialoginterface. onclicklistener () {public void onclick (dialoginterface diich, int which) {dialog. dismiss (); // exit android. OS. process. killprocess (Android. OS. process. mypid (); system. exit (1) ;}}); mdialog = builder. create (); mdialog. getwindow (). settype (windowmanager. layoutparams. type_system_alert); mdialog. show ();}/*** get the app crash exception report ** @ Param ex * @ return */private string getcrashreport (context, throwable ex) {packageinfo pinfo = getpackageinfo (context); stringbuffer exceptionstr = new stringbuffer (); exceptionstr. append ("version:" + pinfo. versionname + "(" + pinfo. versioncode + ") \ n"); exceptionstr. append ("Android:" + android. OS. build. version. release + "(" + android. OS. build. model + ") \ n"); predictionstr. append ("exception:" + ex. getmessage () + "\ n"); stacktraceelement [] elements = ex. getstacktrace (); For (INT I = 0; I <elements. length; I ++) {predictionstr. append (elements [I]. tostring () + "\ n");} return exceptionstr. tostring ();}/*** get app installation package information ** @ return */private packageinfo getpackageinfo (context) {packageinfo = NULL; try {info = context. getpackagemanager (). getpackageinfo (context. getpackagename (), 0);} catch (namenotfoundexception e) {// E. printstacktrace (system. err); // L. I ("getpackageinfo err =" + E. getmessage () ;}if (Info = NULL) info = new packageinfo (); Return info ;}}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.