本文实例讲述了AndroID实现将应用崩溃信息发送给开发者并重启应用的方法。分享给大家供大家参考,具体如下:
在开发过程中,虽然经过测试,但在发布后,在广大用户各种各样的运行环境和 *** 作下,可能会发生一些异想不到的错误导致程序崩溃。将这些错误信息收集起来并反馈给开发者,对于开发者改进优化程序是相当重要的。好了,下面就来实现这种功能吧。
(更正时间:2012年2月9日18时42分07秒)
由于为历史帖原因,以下做法比较浪费,但抓取异常的效果是一样的。
1.对于UI线程(即AndroID中的主线程)抛出的未捕获异常,将这些异常信息存储起来然后关闭到整个应用程序。并再次启动程序,则进入崩溃信息反馈界面让用户将出错信息以Email的形式发送给开发者。
2.对于非UI线程抛出的异常,则立即唤醒崩溃信息反馈界面提示用户将出错信息发送Email。
效果图如下:
过程了解了,则需要了解的几个知识点如下:
1.拦截UncaughtException
Application.onCreate()是整个AndroID应用的入口方法。在该方法中执行如下代码即可拦截UncaughtException:
ueHandler = new UEHandler(this);// 设置异常处理实例Thread.setDefaultUncaughtExceptionHandler(ueHandler);
2.抓取导致程序崩溃的异常信息
UEHandler是Thread.UncaughtExceptionHandler的实现类,在其public voID uncaughtException(Thread thread,Throwable ex)的实现中可以获取崩溃信息,代码如下:
// fetch Excpetion InfoString info = null;ByteArrayOutputStream baos = null;PrintStream printStream = null;try { baos = new ByteArrayOutputStream(); printStream = new PrintStream(baos); ex.printstacktrace(printStream); byte[] data = baos.toByteArray(); info = new String(data); data = null;} catch (Exception e) { e.printstacktrace();} finally { try { if (printStream != null) { printStream.close(); } if (baos != null) { baos.close(); } } catch (Exception e) { e.printstacktrace(); }}
3.程序抛异常后,要关闭整个应用
悲催的程序员,唉,以下三种方式都无效了,咋办啊!!!
3.1 androID.os.Process.killProcess(androID.os.Process.myPID());
3.2 ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.restartPackage("lab.sodino.errorreport");
3.3 System.exit(0)
好吧,毛主席告诉我们:自己动手丰衣足食。
SoftApplication中声明一个变量need2Exit,其值为true标识当前的程序需要完整退出;为false时该干嘛干嘛去。该变量在应用的启动Activity.onCreate()处赋值为false。
在捕获了崩溃信息后,调用SoftApplication.setNeed2Exit(true)标识程序需要退出,并finish()掉ActErrorReport,这时ActErrorReport退栈,抛错的ActOccurError占据手机屏幕,根据Activity的生命周期其要调用onStart(),则我们在onStart()处读取need2Exit的状态,若为true,则也关闭到当前的Activity,则退出了整个应用了。此方法可以解决一次性退出已开启了多个Activity的Application。详细代码请阅读下面的示例源码。
好了,代码如下:
lab.sodino.errorreport.softApplication.java
package lab.sodino.errorreport;import java.io.file;import androID.app.Application;/** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-6-9 下午11:49:56 */public class SoftApplication extends Application { /** "/data/data/<app_package>/files/error.log" */ public static final String PATH_ERROR_LOG = file.separator + "data" + file.separator + "data" + file.separator + "lab.sodino.errorreport" + file.separator + "files" + file.separator + "error.log"; /** 标识是否需要退出。为true时表示当前的Activity要执行finish()。 */ private boolean need2Exit; /** 异常处理类。 */ private UEHandler ueHandler; public voID onCreate() { need2Exit = false; ueHandler = new UEHandler(this); // 设置异常处理实例 Thread.setDefaultUncaughtExceptionHandler(ueHandler); } public voID setNeed2Exit(boolean bool) { need2Exit = bool; } public boolean need2Exit() { return need2Exit; }}
lab.sodino.errorreport.ActOccurError.java
package lab.sodino.errorreport;import java.io.file;import java.io.fileinputStream;import androID.app.Activity;import androID.content.Intent;import androID.os.Bundle;import androID.util.Log;import androID.vIEw.VIEw;import androID.Widget.button;public class ActOccurError extends Activity { private SoftApplication softApplication; /** Called when the activity is first created. */ @OverrIDe public voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.main); softApplication = (SoftApplication) getApplication(); // 一开始进入程序恢复为"need2Exit=false"。 softApplication.setNeed2Exit(false); Log.d("ANDROID_LAB","ActOccurError.onCreate()"); button btnMain = (button) findVIEwByID(R.ID.btnThrowMain); btnMain.setonClickListener(new button.OnClickListener() { public voID onClick(VIEw v) { Log.d("ANDROID_LAB","Thread.main.run()"); int i = 0; i = 100 / i; } }); button btnChild = (button) findVIEwByID(R.ID.btnThrowChild); btnChild.setonClickListener(new button.OnClickListener() { public voID onClick(VIEw v) { new Thread() { public voID run() { Log.d("ANDROID_LAB","Thread.child.run()"); int i = 0; i = 100 / i; } }.start(); } }); // 处理记录于error.log中的异常 String errorContent = getErrorLog(); if (errorContent != null) { Intent intent = new Intent(this,ActErrorReport.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("error",errorContent); intent.putExtra("by","error.log"); startActivity(intent); } } public voID onStart() { super.onStart(); if (softApplication.need2Exit()) { Log.d("ANDROID_LAB","ActOccurError.finish()"); ActOccurError.this.finish(); } else { // do normal things } } /** * 读取是否有未处理的报错信息。<br/> * 每次读取后都会将error.log清空。<br/> * * @return 返回未处理的报错信息或null。 */ private String getErrorLog() { file fileErrorLog = new file(SoftApplication.PATH_ERROR_LOG); String content = null; fileinputStream fis = null; try { if (fileErrorLog.exists()) { byte[] data = new byte[(int) fileErrorLog.length()]; fis = new fileinputStream(fileErrorLog); fis.read(data); content = new String(data); data = null; } } catch (Exception e) { e.printstacktrace(); } finally { try { if (fis != null) { fis.close(); } if (fileErrorLog.exists()) { fileErrorLog.delete(); } } catch (Exception e) { e.printstacktrace(); } } return content; }}
lab.sodino.errorreport.ActErrorReport.java
package lab.sodino.errorreport;import androID.app.Activity;import androID.content.Intent;import androID.os.Bundle;import androID.util.Log;import androID.vIEw.VIEw;import androID.Widget.button;import androID.Widget.EditText;import androID.Widget.TextVIEw;/** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-6-12 下午01:34:17 */public class ActErrorReport extends Activity { private SoftApplication softApplication; private String info; /** 标识来处。 */ private String by; private button btnReport; private button btnCancel; private BtnListener btnListener; public voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.report); softApplication = (SoftApplication) getApplication(); by = getIntent().getStringExtra("by"); info = getIntent().getStringExtra("error"); TextVIEw txtHint = (TextVIEw) findVIEwByID(R.ID.txtErrorHint); txtHint.setText(getErrorHint(by)); EditText editError = (EditText) findVIEwByID(R.ID.editErrorContent); editError.setText(info); btnListener = new BtnListener(); btnReport = (button) findVIEwByID(R.ID.btnREPORT); btnCancel = (button) findVIEwByID(R.ID.btnCANCEL); btnReport.setonClickListener(btnListener); btnCancel.setonClickListener(btnListener); } private String getErrorHint(String by) { String hint = ""; String append = ""; if ("uehandler".equals(by)) { append = " when the app running"; } else if ("error.log".equals(by)) { append = " when last time the app running"; } hint = String.format(getResources().getString(R.string.errorHint),append,1); return hint; } public voID onStart() { super.onStart(); if (softApplication.need2Exit()) { // 上一个退栈的Activity有执行“退出”的 *** 作。 Log.d("ANDROID_LAB","ActErrorReport.finish()"); ActErrorReport.this.finish(); } else { // go ahead normally } } class BtnListener implements button.OnClickListener { @OverrIDe public voID onClick(VIEw v) { if (v == btnReport) { // 需要 androID.permission.SEND权限 Intent mailintent = new Intent(Intent.ACTION_SEND); mailintent.setType("plain/text"); String[] arrReceiver = { "sodinoopen@hotmail.com" }; String mailSubject = "App Error Info[" + getPackagename() + "]"; String mailBody = info; mailintent.putExtra(Intent.EXTRA_EMAIL,arrReceiver); mailintent.putExtra(Intent.EXTRA_SUBJECT,mailSubject); mailintent.putExtra(Intent.EXTRA_TEXT,mailBody); startActivity(Intent.createChooser(mailintent,"Mail Sending...")); ActErrorReport.this.finish(); } else if (v == btnCancel) { ActErrorReport.this.finish(); } } } public voID finish() { super.finish(); if ("error.log".equals(by)) { // do nothing } else if ("uehandler".equals(by)) { // 1. // androID.os.Process.killProcess(androID.os.Process.myPID()); // 2. // ActivityManager am = (ActivityManager) // getSystemService(ACTIVITY_SERVICE); // am.restartPackage("lab.sodino.errorreport"); // 3. // System.exit(0); // 1.2.3.都失效了,Google你让悲催的程序员情何以堪啊。 softApplication.setNeed2Exit(true); // //////////////////////////////////////////////////// // // 另一个替换方案是直接返回“HOME” // Intent i = new Intent(Intent.ACTION_MAIN); // // 如果是服务里调用,必须加入newtask标识 // i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // i.addcategory(Intent.category_HOME); // startActivity(i); // //////////////////////////////////////////////////// } }}
lab.sodino.errorreport.UEHandler.java
package lab.sodino.uncaughtexception;import java.io.ByteArrayOutputStream;import java.io.file;import java.io.fileOutputStream;import java.io.PrintStream;import androID.content.Intent;import androID.util.Log;/** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-6-9 下午11:50:43 */public class UEHandler implements Thread.UncaughtExceptionHandler { private SoftApplication softApp; private file fileErrorLog; public UEHandler(SoftApplication app) { softApp = app; fileErrorLog = new file(SoftApplication.PATH_ERROR_LOG); } @OverrIDe public voID uncaughtException(Thread thread,Throwable ex) { // fetch Excpetion Info String info = null; ByteArrayOutputStream baos = null; PrintStream printStream = null; try { baos = new ByteArrayOutputStream(); printStream = new PrintStream(baos); ex.printstacktrace(printStream); byte[] data = baos.toByteArray(); info = new String(data); data = null; } catch (Exception e) { e.printstacktrace(); } finally { try { if (printStream != null) { printStream.close(); } if (baos != null) { baos.close(); } } catch (Exception e) { e.printstacktrace(); } } // print long threadID = thread.getID(); Log.d("ANDROID_LAB","Thread.getname()=" + thread.getname() + " ID=" + threadID + " state=" + thread.getState()); Log.d("ANDROID_LAB","Error[" + info + "]"); if (threadID != 1) { // 此处示例跳转到汇报异常界面。 Intent intent = new Intent(softApp,info); intent.putExtra("by","uehandler"); softApp.startActivity(intent); } else { // 此处示例发生异常后,重新启动应用 Intent intent = new Intent(softApp,ActOccurError.class); // 如果<span >没有NEW_TASK标识且</span>是UI线程抛的异常则界面卡死直到ANR intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); softApp.startActivity(intent); // write 2 /data/data/<app_package>/files/error.log write2ErrorLog(fileErrorLog,info); // kill App Progress androID.os.Process.killProcess(androID.os.Process.myPID()); } } private voID write2ErrorLog(file file,String content) { fileOutputStream fos = null; try { if (file.exists()) { // 清空之前的记录 file.delete(); } else { file.getParentfile().mkdirs(); } file.createNewfile(); fos = new fileOutputStream(file); fos.write(content.getBytes()); } catch (Exception e) { e.printstacktrace(); } finally { try { if (fos != null) { fos.close(); } } catch (Exception e) { e.printstacktrace(); } } }}
/res/layout/main.xml
<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:orIEntation="vertical" androID:layout_wIDth="fill_parent" androID:layout_height="fill_parent" > <TextVIEw androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:text="@string/hello" /> <button androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:text="Throws Exception By Main Thread" androID:ID="@+ID/btnThrowMain" ></button> <button androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:text="Throws Exception By Child Thread" androID:ID="@+ID/btnThrowChild" ></button></linearLayout>
/res/layout/report.xml
<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:orIEntation="vertical" androID:layout_wIDth="fill_parent" androID:layout_height="fill_parent"> <TextVIEw androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:text="@string/errorHint" androID:ID="@+ID/txtErrorHint" /> <EditText androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:ID="@+ID/editErrorContent" androID:editable="false" androID:layout_weight="1"></EditText> <linearLayout androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:background="#96cdcd" androID:gravity="center" androID:orIEntation="horizontal"> <button androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:text="Report" androID:ID="@+ID/btnREPORT" androID:layout_weight="1"></button> <button androID:layout_wIDth="fill_parent" androID:layout_height="wrap_content" androID:text="Cancel" androID:ID="@+ID/btnCANCEL" androID:layout_weight="1"></button> </linearLayout></linearLayout>
用到的string.xml资源为:
复制代码 代码如下:<string name="errorHint">A error has happened %1$s.Please click <i><b>"REPORT"</b></i> to send the error information to us by email,Thanks!!!</string>
重要的一点是要在AndroIDManifest.xml中对<application>节点设置androID:name=".softApplication"
更多关于AndroID相关内容感兴趣的读者可查看本站专题:《Android调试技巧与常见问题解决方法汇总》、《Android开发入门与进阶教程》、《Android多媒体 *** 作技巧汇总(音频,视频,录音等)》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》
希望本文所述对大家AndroID程序设计有所帮助。
总结以上是内存溢出为你收集整理的Android实现将应用崩溃信息发送给开发者并重启应用的方法全部内容,希望文章能够帮你解决Android实现将应用崩溃信息发送给开发者并重启应用的方法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)