最近在写Android程序崩溃异常处理,完成之后,稍加封装与大家分享。

我的思路是这样的,在程序崩溃之后,将异常信息保存到一个日志文件中,然后对该文件进行处理,比如发送到邮箱,或发送到服务器。

所以,第一步是先定义一个接口,用于在保存好日志之后的回调。代码如下:

/** @(#)CrashListener.java             Project: crash* Date:2014-5-27** Copyright (c) 2014 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.githang.android.crash;import java.io.File;/*** @author Geek_Soledad <a target="_blank" href=*         "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=XTAuOSVzPDM5LzI0OR0sLHM_MjA"*         style="text-decoration:none;"><img src=*         "http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_01.png"*         /></a>*/
public interface CrashListener {/*** 保存异常的日志。* * @param file*/public void afterSaveCrash(File file);
}

接下来是用于处理崩溃异常的类,它要实现UncaughtExceptionHandler接口。实现它之后,将它设为默认的线程异常的处理者,这样程序崩溃之后,就会调用它了。但是在调用它之前,还需要先获取保存之前默认的handler,用于在我们收集了异常之后对程序进行处理,比如默认的弹出“程序已停止运行”的对话框(当然你也可以自己实现一个),终止程序,打印LOG。

我的实现如下:

/** @(#)CrashHandler.java              Project: crash* Date:2014-5-26** Copyright (c) 2014 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.githang.android.crash;import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;/*** @author Geek_Soledad <a target="_blank" href=*         "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=XTAuOSVzPDM5LzI0OR0sLHM_MjA"*         style="text-decoration:none;"><img src=*         "http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_01.png"*         /></a>*/
public class CrashHandler implements UncaughtExceptionHandler {private static final CrashHandler sHandler = new CrashHandler();private static final UncaughtExceptionHandler sDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();private static final ExecutorService THREAD_POOL = Executors.newSingleThreadExecutor();private Future<?> future;private CrashListener mListener;private File mLogFile;public static CrashHandler getInstance() {return sHandler;}@Overridepublic void uncaughtException(Thread thread, Throwable ex) {CrashLogUtil.writeLog(mLogFile, "CrashHandler", ex.getMessage(), ex);future = THREAD_POOL.submit(new Runnable() {public void run() {if (mListener != null) {mListener.afterSaveCrash(mLogFile);}};});if (!future.isDone()) {try {future.get();} catch (Exception e) {e.printStackTrace();}}sDefaultHandler.uncaughtException(thread, ex);}public void init(File logFile, CrashListener listener) {mLogFile = logFile;mListener = listener;}}

这个类很简单,就是在发生未能捕获的异常之后,保存LOG到文件,然后 调用前面定义的接口,对日志文件进行处理。其中CrashLogUtil是我实现的保存LOG到文件的类。代码如下:

/** @(#)LogUtil.java               Project: crash* Date:2014-5-27** Copyright (c) 2014 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.githang.android.crash;import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;/*** @author Geek_Soledad <a target="_blank" href=*         "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=XTAuOSVzPDM5LzI0OR0sLHM_MjA"*         style="text-decoration:none;"><img src=*         "http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_01.png"*         /></a>*/
public class CrashLogUtil {private static final SimpleDateFormat timeFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS",Locale.getDefault());/*** 将日志写入文件。* * @param tag* @param message* @param tr*/public static synchronized void writeLog(File logFile, String tag, String message, Throwable tr) {logFile.getParentFile().mkdirs();if (!logFile.exists()) {try {logFile.createNewFile();} catch (IOException e) {e.printStackTrace();}}String time = timeFormat.format(Calendar.getInstance().getTime());synchronized (logFile) {FileWriter fileWriter = null;BufferedWriter bufdWriter = null;PrintWriter printWriter = null;try {fileWriter = new FileWriter(logFile, true);bufdWriter = new BufferedWriter(fileWriter);printWriter = new PrintWriter(fileWriter);bufdWriter.append(time).append(" ").append("E").append('/').append(tag).append(" ").append(message).append('\n');bufdWriter.flush();tr.printStackTrace(printWriter);printWriter.flush();fileWriter.flush();} catch (IOException e) {closeQuietly(fileWriter);closeQuietly(bufdWriter);closeQuietly(printWriter);}}}public static void closeQuietly(Closeable closeable) {if (closeable != null) {try {closeable.close();} catch (IOException ioe) {// ignore}}}
}

在日志保存之后,我们还需要生成一个报告,并发送给服务器。报告的方法,可以是发送到邮箱,或者http请求发送给服务器。所以这里写了一个抽象类,实现了生成标题和内容,设置日志路径等。代码如下:

/** @(#)AbstractReportHandler.java             Project: crash* Date:2014-5-27** Copyright (c) 2014 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.githang.android.crash;import java.io.File;import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;/*** @author Geek_Soledad <a target="_blank" href=*         "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=XTAuOSVzPDM5LzI0OR0sLHM_MjA"*         style="text-decoration:none;"><img src=*         "http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_01.png"*         /></a>*/
public abstract class AbstractCrashReportHandler implements CrashListener {private Context mContext;public AbstractCrashReportHandler(Context context) {mContext = context;CrashHandler handler = CrashHandler.getInstance();handler.init(getLogDir(context), this);Thread.setDefaultUncaughtExceptionHandler(handler);}protected File getLogDir(Context context) {return new File(context.getFilesDir(), "crash.log");}protected abstract void sendReport(String title, String body, File file);@Overridepublic void afterSaveCrash(File file) {sendReport(buildTitle(mContext), buildBody(mContext), file);}public String buildTitle(Context context) {return "Crash Log: "+ context.getPackageManager().getApplicationLabel(context.getApplicationInfo());}public String buildBody(Context context) {StringBuilder sb = new StringBuilder();sb.append("APPLICATION INFORMATION").append('\n');PackageManager pm = context.getPackageManager();ApplicationInfo ai = context.getApplicationInfo();sb.append("Application : ").append(pm.getApplicationLabel(ai)).append('\n');try {PackageInfo pi = pm.getPackageInfo(ai.packageName, 0);sb.append("Version Code: ").append(pi.versionCode).append('\n');sb.append("Version Name: ").append(pi.versionName).append('\n');} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}sb.append('\n').append("DEVICE INFORMATION").append('\n');sb.append("Board: ").append(Build.BOARD).append('\n');sb.append("BOOTLOADER: ").append(Build.BOOTLOADER).append('\n');sb.append("BRAND: ").append(Build.BRAND).append('\n');sb.append("CPU_ABI: ").append(Build.CPU_ABI).append('\n');sb.append("CPU_ABI2: ").append(Build.CPU_ABI2).append('\n');sb.append("DEVICE: ").append(Build.DEVICE).append('\n');sb.append("DISPLAY: ").append(Build.DISPLAY).append('\n');sb.append("FINGERPRINT: ").append(Build.FINGERPRINT).append('\n');sb.append("HARDWARE: ").append(Build.HARDWARE).append('\n');sb.append("HOST: ").append(Build.HOST).append('\n');sb.append("ID: ").append(Build.ID).append('\n');sb.append("MANUFACTURER: ").append(Build.MANUFACTURER).append('\n');sb.append("PRODUCT: ").append(Build.PRODUCT).append('\n');sb.append("TAGS: ").append(Build.TAGS).append('\n');sb.append("TYPE: ").append(Build.TYPE).append('\n');sb.append("USER: ").append(Build.USER).append('\n');return sb.toString();}
}

这样一个框架就算基本完成了。

当然,下面我还给出了报告的一种实现,发送邮件。

如何发送邮箱,网上已有不少资料,这里不再简而言之。

首先需要用到三个jar包: activation.jar, additionnal.jar, mail.jar。

然后 写一个类,继承自Authenticator。代码如下:

/** @(#)Snippet.java               Project: CrashHandler* Date: 2014-5-27** Copyright (c) 2014 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.githang.android.crash;import android.util.Log;import java.util.Date;
import java.util.Properties;import javax.activation.CommandMap;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.activation.MailcapCommandMap;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;/*** Author: msdx (645079761@qq.com) Time: 14-5-27 上午9:07*/
public class LogMail extends Authenticator {private String host;private String port;private String user;private String pass;private String from;private String to;private String subject;private String body;private Multipart multipart;private Properties props;public LogMail() {}public LogMail(String user, String pass, String from, String to, String host, String port,String subject, String body) {this.host = host;this.port = port;this.user = user;this.pass = pass;this.from = from;this.to = to;this.subject = subject;this.body = body;}public LogMail setHost(String host) {this.host = host;return this;}public LogMail setPort(String port) {this.port = port;return this;}public LogMail setUser(String user) {this.user = user;return this;}public LogMail setPass(String pass) {this.pass = pass;return this;}public LogMail setFrom(String from) {this.from = from;return this;}public LogMail setTo(String to) {this.to = to;return this;}public LogMail setSubject(String subject) {this.subject = subject;return this;}public LogMail setBody(String body) {this.body = body;return this;}public void init() {multipart = new MimeMultipart();// There is something wrong with MailCap, javamail can not find a// handler for the multipart/mixed part, so this bit needs to be added.MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");CommandMap.setDefaultCommandMap(mc);props = new Properties();props.put("mail.smtp.host", host);props.put("mail.smtp.auth", "true");props.put("mail.smtp.port", port);props.put("mail.smtp.socketFactory.port", port);props.put("mail.transport.protocol", "smtp");props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");props.put("mail.smtp.socketFactory.fallback", "false");}public boolean send() throws MessagingException {if (!user.equals("") && !pass.equals("") && !to.equals("") && !from.equals("")) {Session session = Session.getDefaultInstance(props, this);Log.d("SendUtil", host + "..." + port + ".." + user + "..." + pass);MimeMessage msg = new MimeMessage(session);msg.setFrom(new InternetAddress(from));InternetAddress addressTo = new InternetAddress(to);msg.setRecipient(MimeMessage.RecipientType.TO, addressTo);msg.setSubject(subject);msg.setSentDate(new Date());// setup message bodyBodyPart messageBodyPart = new MimeBodyPart();messageBodyPart.setText(body);multipart.addBodyPart(messageBodyPart);// Put parts in messagemsg.setContent(multipart);// send emailTransport.send(msg);return true;} else {return false;}}public void addAttachment(String filePath, String fileName) throws Exception {BodyPart messageBodyPart = new MimeBodyPart();DataSource source = new FileDataSource(filePath);messageBodyPart.setDataHandler(new DataHandler(source));messageBodyPart.setFileName(fileName);multipart.addBodyPart(messageBodyPart);}@Overridepublic PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(user, pass);}
}

然后是发送报告邮件的类了,它继承自前面所写的AbstractCrashReportHandler,实现如下:

/** @(#)CrashEmailReport.java              Project: CrashHandler* Date: 2014-5-27** Copyright (c) 2014 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.githang.android.crash;import java.io.File;import android.content.Context;/*** @author Geek_Soledad <a target="_blank" href=*         "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=XTAuOSVzPDM5LzI0OR0sLHM_MjA"*         style="text-decoration:none;"><img src=*         "http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_01.png"*         /></a>*/
public class CrashEmailReport extends AbstractCrashReportHandler {private String mReceiveEmail;public CrashEmailReport(Context context) {super(context);}public void setReceiver(String receiveEmail) {mReceiveEmail = receiveEmail;}@Overrideprotected void sendReport(String title, String body, File file) {LogMail sender = new LogMail().setUser("irain_log@163.com").setPass("xxxxxxxx").setFrom("irain_log@163.com").setTo(mReceiveEmail).setHost("smtp.163.com").setPort("465").setSubject(title).setBody(body);sender.init();try {sender.addAttachment(file.getPath(), file.getName());sender.send();file.delete();} catch (Exception e) {e.printStackTrace();}}
}

这样,一个完整的程序崩溃异常框架就完成了。对于日志报告,可自己继承AbstractCrashReportHandler来扩展实现。

使用的时候,需要写一个继承自Application的类,在onCreate方法中加上如下代码,即设置接收邮箱。

  new CrashEmailReport(this).setReceiver("log@msdx.pw");

然后在AndroidManifest.xml中配置这个类。

项目已开源,见另一篇博客:http://blog.csdn.net/maosidiaoxian/article/details/27320815

Android程序崩溃异常收集框架相关推荐

  1. 安卓崩溃信息收集框架ACRA

    版权声明:本文为博主高远原创文章,转载请注明出处:http://blog.csdn.net/wgyscsf    https://blog.csdn.net/wgyscsf/article/detai ...

  2. android处理crash程序崩溃异常

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

  3. android 程序崩溃日记捕捉

    1.重新UncaughtExceptionHandler public class CrashHandler implements Thread.UncaughtExceptionHandler {p ...

  4. android 程序崩溃处理,Android应用崩溃的应急处理

    在我们开发Android应用程序的时候总是难免遇到程序崩溃的问题:(很抱歉,"××"已停止运行.) 每次看到这个问题心都是哇凉哇凉的 一般遇到这样问题,有两种可能: 1.自己的代码 ...

  5. android程序崩溃后重启

    有时候由于测试不充分或者程序潜在的问题而导致程序异常崩溃,这个是令人无法接受的,在android中怎样捕获程序的异常崩溃,然后进行一些必要的处理或重新启动 应用这个问题困恼了我很久,今天终于解决了该问 ...

  6. android查找邮件程序,Android 程序崩溃日志邮件获取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 在我们开发Android应用程序的时候,BUG的出现是难以避免的,时不时还会出现崩溃的情况,这个时候,我们急需知道造成问题的原因是什么,但是,在没 ...

  7. android+程序崩溃,Android平台程序崩溃的类型及原因列举

    Android平台程序崩溃大家都应该遇到过,force close和ANR应该是大家遇到较多的. 这里把Android平台程序崩溃的各种类型做一个简述和原因列举. 1.ANR(可见ANR): 发生场景 ...

  8. qt调试android程序崩溃,使用qt5开发的Android应用程序合并了AWS C++库崩溃

    我想开发一种 Android应用程序 它在行刑一开始就崩溃了. 我在用 第5.11节 我认为配置很好(我正在使用 上一个Android SDK和ndk10e )因为我可以运行一个简单的Android应 ...

  9. android程序崩溃后,在appuard混淆之后,Android应用程序崩溃了

    我在我的应用程序上运行了proguard工具来进行混淆.我知道,当混淆发生时,proguard会缩小并优化应用程序.因此,在混淆应用程序正确打开后,然后当我尝试登录时崩溃.我得到的最好的是这个日志,因 ...

  10. android 程序崩溃后自动重启,Android 应用Crash 后自动重启的方法小结

    前提 首先,我们肯定要在Application里面注册一个CrashHandler,监听应用crash public class TestApplication extends MultiDexApp ...

最新文章

  1. python matplotlib绘制等高线,plt.contour(),ax3.contour()和plt.contourf(),ax3.contour(), 同名函数
  2. 电影点评系统论文java_毕业设计(论文)-基于web的电影点评系统分析与设计.docx...
  3. windows环境下封装条件wait和signal
  4. 2018-11-02 在代码中进行中文命名实践的短期目标
  5. excel文件成绩处理python代码_Python处理Excel文件实例代码
  6. 5 Ways to Speed Up Your Rails App
  7. FPGA中数的表示方法
  8. 微信公众号“成为开发者”实践代码
  9. 如何为resin的jvm-default.log瘦身
  10. java出现com.lowagie.text。DocumentException:字体“C: \ Windows \ \ simsun字体。带有'Identity-H'的ttc'不被识别的错误
  11. IE-LAB网络实验室:华为认证 北京华为认证,思科ccie,sp ccie 思科ccnp CCNP需要学习多长时间
  12. 中级程序员还应该如何提高自己
  13. java 调用 yed 绘制 流程图_用 yEd Graph Editor 绘制流程图(2)
  14. 图像算法---白平衡AWB
  15. CAD“左手键”快捷命令表,大大提高绘图效率
  16. 四款35W 双C快充解决方案,功率支持35W、20W、15W等
  17. 无法安装 cloudera-manager-agent
  18. selenium闪退
  19. 学无止境,我爱python
  20. 全国邮编区号大全和从word中读取内容保存到msql中的源程序

热门文章

  1. 20190828——python模块
  2. 甘肃康县乡村“蝶变”:北方山沟引来“南方媳妇”
  3. 什么是Python爬虫?一篇文章带你全面了解爬虫
  4. 平台级SAAS架构——统一身份管理系统
  5. 2011广告联盟排名,最好的广告联盟推荐
  6. android Wifi热点启动流程,[android]WIFI热点启动流程分析
  7. ISA服务器安装设置全集
  8. 故障分析 | MySQL 8.0 解决连接满问题
  9. 终于解决 归递调用 警告,其实程序没有 归递调用*** WARNING L13: RECURSIVE CALL TO SEGMENT
  10. Processing鼠标键盘