上篇写到,将程序中没有处理到的crash信息保存到本地文件夹下。但是实际的情况是,你不可能总是将用户的设备拿过来。所以一般性的处理是,将crash reports发送到服务器或者邮箱。所以针对上篇的代码,改动如下:
 
CrashHandler.java
当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告

package com.amanda.crash2file;
 
import java.io.File;
import java.io.FileOutputStream;
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.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
 
/**
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
*
* @author user
*
*/
public class CrashHandler implements UncaughtExceptionHandler {
 
    private static final String TAG = "CrashHandler";
    private static final String CRASH_FLOD_NAME = "crash";
 
    //系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static CrashHandler INSTANCE = new CrashHandler();
    //程序的Context对象
    private Context mContext;
 
    //用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyyMMdd_kkmmss");
 
    /** 保证只有一个CrashHandler实例 */
    private CrashHandler() {
    }
 
    /** 获取CrashHandler实例 ,单例模式 */
    public static CrashHandler getInstance() {
        return INSTANCE;
    }
 
    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        //获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
 
    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            //退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }
 
    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
 
        //异步工作:获取版本信息、写入文件、发送邮件
        ProgressTask mTask = new ProgressTask(mContext);
        mTask.execute(ex);
 
 
        //使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();
 
        return true;
    }
 
 
    private class ProgressTask extends AsyncTask<Throwable, Void, Boolean> {
        private Context mContext;
 
        public ProgressTask(Context vContext){
            mContext = vContext;
        }
 
        protected void onPreExecute() {
        }
 
 
        protected void onPostExecute(Boolean result) {
        }
 
        @Override
        protected Boolean doInBackground(Throwable... params) {
            boolean mResult = false;
 
            //保存日志文件
            String filePath = saveCrashInfo2File(mContext,params[0]);       
            Log.d("test","filePath: "+filePath);
 
            //发送邮件
            if(filePath != null)
            {
                boolean isSuccessMail = sendMailByJavaMail(filePath);
                Log.d("test","isSuccessMail: "+isSuccessMail);
 
                //如果发送成功,则删除本地的crash report
                if(isSuccessMail){
                    deleteFile(filePath);
                }
 
                mResult = isSuccessMail;
            }
 
            return mResult;
        }
 
 
        /**
         * 收集设备参数信息
         * @param ctx
         */
        private Map<String, String> collectDeviceInfo(Context ctx) {
            Map<String, String> infos = new HashMap<String, String>();
            try {
                PackageManager pm = ctx.getPackageManager();
                PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
                if (pi != null) {
                    infos.put("packageName", pi.packageName);
                    String versionName = pi.versionName == null ? "null" : pi.versionName;
                    String versionCode = pi.versionCode + "";               
                    infos.put("versionName", versionName);
                    infos.put("versionCode", versionCode);               
                }
            } catch (NameNotFoundException e) {
                Log.e(TAG, "an error occured when collect package info", e);
            }
            Field[] fields = Build.class.getDeclaredFields();
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    infos.put(field.getName(), field.get(null).toString());
                    Log.d(TAG, field.getName() + " : " + field.get(null));
                } catch (Exception e) {
                    Log.e(TAG, "an error occured when collect crash info", e);
                }
            }
            return infos;
        }
 
        /**
         * 保存错误信息到文件中
         *
         * @param ex
         * @return    返回文件名称,便于将文件传送到服务器
         */
        private String saveCrashInfo2File(Context ctx,Throwable ex) {
            Map<String, String> infos = collectDeviceInfo(ctx);
 
            StringBuffer sb = new StringBuffer();
            for (Map.Entry<String, String> entry : infos.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key + "=" + value + "\n");
            }
 
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            ex.printStackTrace(printWriter);
            Throwable cause = ex.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.close();
            String result = writer.toString();
            sb.append(result);
            try {
                long timestamp = System.currentTimeMillis();
                String time = formatter.format(new Date());
                String fileName = "crash_" + time + "_" + timestamp + ".log";
                String fileAbsPath = "";
                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) &&
                        !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
                    fileAbsPath = Environment.getExternalStorageDirectory().getPath()+File.separator+CRASH_FLOD_NAME+File.separator;
                }
                else{
                    fileAbsPath = mContext.getFilesDir().getPath()+File.separator+CRASH_FLOD_NAME+File.separator;
                }
 
                File dir = new File(fileAbsPath);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
 
                FileOutputStream fos = new FileOutputStream(fileAbsPath + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
                return fileAbsPath + fileName;
            } catch (Exception e) {
                Log.e(TAG, "an error occured while writing file...", e);
            }
            return null;
        }
 
 
        private  boolean sendMailByJavaMail(String filePath) {
            Mail m = new Mail("xx@126.com", "xxxx");
            m.set_debuggable(false);
            String[] toArr = {"xx@126.com"};
            m.set_to(toArr);
            m.set_from("xx@126.com");
            m.set_subject("CrashReport");
            m.setBody("Email body. test by Java Mail final");
            try {
                m.addAttachment(filePath);
 
                if(m.send()) {
                    Log.i("test","Email was sent successfully.");
                    return true;
 
                } else {
                    Log.i("test","Email was sent failed.");
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e("test", "Could not send email", e);
            }
 
            return false;
        }
 
 
        private void deleteFile(String filePath)
        {
            File file = new File(filePath);
            if(file!= null && file.exists()){
                file.delete();
            }
        }
    }
}

 
Mail.java
发送邮件。该部分是参考了网上的相关资料。这里需要引入三个jar包。已打包放在附件。

package com.amanda.crash2file;
 
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.BodyPart;
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;
 
 
public class Mail extends javax.mail.Authenticator {  
    private String _user;  
    private String _pass;   
    private String[] _to;  
    private String _from;   
    private String _port;  
    private String _sport;   
    private String _host;   
    private String _subject;  
    private String _body;   
    private boolean _auth;     
    private boolean _debuggable;   
    private Multipart _multipart;    
    public Mail() {    
        _host = "smtp.126.com"; // default smtp server   
        _port = "465"; // default smtp port    
        _sport = "465"; // default socketfactory port    
        _user = ""; // username     _pass = ""; // password   
        _from = ""; // email sent from  
        _subject = ""; // email subject
        _body = ""; // email body 
        _debuggable = false; // debug mode on or off - default off 
        _auth = true; // smtp authentication - default on 
        _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);   }  
    public String get_user() {
        return _user;
    }
    public void set_user(String _user) {
        this._user = _user;
    }
    public String get_pass() {
        return _pass;
    }
    public void set_pass(String _pass) {
        this._pass = _pass;
    }
    public String[] get_to() {
        return _to;
    }
    public void set_to(String[] _to) {
        this._to = _to;
    }
    public String get_from() {
        return _from;
    }
    public void set_from(String _from) {
        this._from = _from;
    }
    public String get_port() {
        return _port;
    }
    public void set_port(String _port) {
        this._port = _port;
    }
    public String get_sport() {
        return _sport;
    }
    public void set_sport(String _sport) {
        this._sport = _sport;
    }
    public String get_host() {
        return _host;
    }
    public void set_host(String _host) {
        this._host = _host;
    }
    public String get_subject() {
        return _subject;
    }
    public void set_subject(String _subject) {
        this._subject = _subject;
    }
    public String get_body() {
        return _body;
    }
    public void set_body(String _body) {
        this._body = _body;
    }
    public boolean is_auth() {
        return _auth;
    }
    public void set_auth(boolean _auth) {
        this._auth = _auth;
    }
    public boolean is_debuggable() {
        return _debuggable;
    }
    public void set_debuggable(boolean _debuggable) {
        this._debuggable = _debuggable;
    }
    public Multipart get_multipart() {
        return _multipart;
    }
    public void set_multipart(Multipart _multipart) {
        this._multipart = _multipart;
    }
    public Mail(String user, String pass) {   
        this();  
        _user = user;   
        _pass = pass; 
        }   
    public boolean send() throws Exception {
        Properties props = _setProperties();
        if(!_user.equals("") && !_pass.equals("") && _to.length > 0 && !_from.equals("") && !_subject.equals("") && !_body.equals("")) {    
            Session session = Session.getInstance(props, this);     
            MimeMessage msg = new MimeMessage(session);     
            msg.setFrom(new InternetAddress(_from));       
            InternetAddress[] addressTo = new InternetAddress[_to.length];  
            for (int i = 0; i < _to.length; i++) {  
                addressTo[i] = new InternetAddress(_to[i]); 
                }       
            msg.setRecipients(MimeMessage.RecipientType.TO, addressTo);  
            msg.setSubject(_subject);  
            msg.setSentDate(new Date());        // setup message body  
            BodyPart messageBodyPart = new MimeBodyPart();   
            messageBodyPart.setText(_body);
            _multipart.addBodyPart(messageBodyPart);        // Put parts in message  
            msg.setContent(_multipart);        // send email 
            Transport.send(msg);  
            return true;  
            } else
                return false;     }
        }    public void addAttachment(String filename) throws Exception {  
            BodyPart messageBodyPart = new MimeBodyPart(); 
            DataSource source = new FileDataSource(filename); 
            messageBodyPart.setDataHandler(new DataHandler(source)); 
            messageBodyPart.setFileName(filename);   
            _multipart.addBodyPart(messageBodyPart);
            }  
        @Override   public PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(_user, _pass); 
            }
        private Properties _setProperties() { 
            Properties props = new Properties(); 
            props.put("mail.smtp.host", _host);    
            if(_debuggable) { 
                props.put("mail.debug", "true");     }  
            if(_auth) {    
                props.put("mail.smtp.auth", "true");  
                }      props.put("mail.smtp.port", _port);  
                props.put("mail.smtp.socketFactory.port", _sport); 
                props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");  
                props.put("mail.smtp.socketFactory.fallback", "false");  
                return props;   }    // the getters and setters 
        public String getBody() {   
            return _body;   }  
        public void setBody(String _body) {
 
            this._body = _body;
            }    // more of the getters and setters ….. }
    }

 
另外,需增加可以访问网络的权限
AndroidManifest.xml
<? xml   version = "1.0"   encoding = "utf-8" ?>
< manifest   xmlns:android = "http://schemas.android.com/apk/res/android"
     package = "com.amanda.crash2file"
     android:versionCode = "2"
     android:versionName = "1.1"   >
     < uses-sdk
         android:minSdkVersion = "15"
         android:targetSdkVersion = "15"   />
    
     < uses-permission   android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
     < uses-permission   android:name = "android.permission.INTERNET" />
     < application
         android:name = ".CrashApplication"         
         android:allowBackup = "true"
         android:icon = "@drawable/ic_launcher"
         android:label = "@string/app_name"
         android:theme = "@style/AppTheme"   >
         < activity
             android:name = "com.amanda.crash2file.MainActivity"
             android:label = "@string/app_name"   >
             < intent-filter >
                 < action   android:name = "android.intent.action.MAIN"   />
                 < category   android:name = "android.intent.category.LAUNCHER"   />
             </ intent-filter >
         </ activity >
     </ application >
</ manifest >

 
实现功能:当出现UncaughtException时,将其相关信息保存到文件/sdcard/crash/xx.log或者/data/data/<package name>/file/crash/xx.log,并且将该文件以附件的形式发送到邮箱。如果发送成功,则将保存的本地crash report删除。
 
后续需要增强:
1、成功发送邮件的几率不高,需提高成功率
2、每次发送的邮件附件改成crash目录下的所有本地report,这样没有发送成功的report可以下次再尝试发送
3、本地目录文件管理&封装
4、在此文的基础上,实现用户行为report或者Log report等等

附件列表

保存全局Crash报告&发送邮件的更多相关文章

  1. 保存全局Crash报告

    CrashHandler.java UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告 package  com.amanda;imp ...

  2. Android应用如何反馈Crash报告

    转自:http://www.cnblogs.com/draem0507/archive/2013/05/25/3099461.html 一.为什么要Crash crash可以理解成堕落,垮台.按照我们 ...

  3. ios如何生成crash报告

    #include <signal.h> #include <execinfo.h> void OnProcessExceptionHandler(int sigl) { do ...

  4. BFS(广度优先搜索遍历保存全局状态,华容道翻版做法)--08--DFS--蓝桥杯青蛙跳杯子

    题目描述 X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色. X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去. 如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙 ...

  5. Breakpad Google的crash捕获、抓取开源库

    简介: Breadpad为google chrominum项目下用于处理dump的一套工具:内部采用跨平台方式实现捕获.生成.解析与平台无关的dump,便于统一处理:支持进程内与进程外捕获,当为进程外 ...

  6. iOS应用的crash日志的分析基础

        Outline如何获得crash日志如何解析crash日志如何分析crash日志     1. iOS策略相关     2. 常见错误标识     3. 代码bug 一.如何获得crash日志 ...

  7. Oracle AWR报告生成和性能分析

    目录 一.AWE报告生成步骤 1.1 工具选择 1.2 自动创建快照 1.3 手工创建快照 1.4 生成AWR报告 二.AWR报告分析 2.1 AWR之DB Time 2.2 AWR之load_pro ...

  8. 定时执行自动化脚本-(二)ant发送邮件及邮件中添加附件

    发送邮件及邮件添加附件均需要用java来实现 1.idea创建一个maven的java项目,目录结构如下 2.pom.xml文件添加依赖的javax.mail <dependencies> ...

  9. 了解和分析iOS Crash

    WeTest 导读 北京时间凌晨一点,苹果一年一度的发布会如期而至.新机型的发布又会让适配相关的同学忙上一阵子啦,并且iOS Crash的问题始终伴随着移动开发者.本文将从三个阶段,由浅入深的介绍如何 ...

随机推荐

  1. 安卓微信、QQ自带浏览器 UXSS 漏洞

    安卓微信.QQ自带浏览器 UXSS 漏洞 注:PDF报告原文下载链接 Author: hei@knownsec.com Date: 2016-02-29 一.漏洞描述 在安卓平台上的微信及QQ自带浏览 ...

  2. 推荐一本springBoot学习书籍---深入浅出springBoot2.x

    花了几周时间读完了这本书,确实是一本特别详细全面的书,而且不单单只是springBoot, 书中还介绍了许多工作中常用的技术与springBoot的整合使用,当然,也有一些小bug, 因为在代码实践过 ...

  3. 【四校联考】【比赛题解】FJ NOIP 四校联考 2017 Round 7

    此次比赛为厦门一中出题.都是聚劳,不敢恭维. 莫名爆了个0,究其原因,竟然是快读炸了……很狗,很难受. 话不多说,来看看题: [T1] 题意: 样例: PS:1<=h[i]<=100000 ...

  4. Awk基础

    Awk文本处理 awk是一种编程语言,用于在linux/unix下对文本和数据进行处理.awk数据可以来自标准输入.一个或多个文件,或其它命令的输出.awk通常是配合脚本进行使用, 是一个强大的文本处 ...

  5. STL容器 vector,list,deque 性能比较

    C++的STL模板库中提供了3种容器类:vector,list,deque对于这三种容器,在觉得好用的同时,经常会让我们困惑应该选择哪一种来实现我们的逻辑.在少量数据操作的程序中随便哪一种用起来感觉差 ...

  6. Web 2.0应用客户端性能问题十大根源《转载》

    前言 Web 2.0应用的推广为用户带来了全新的体验,同时也让开发人员更加关注客户端性能问题.最近,资深Web性能诊断专家.知名工具dynatrace的创始人之一Andreas Grabner根据自己 ...

  7. NopCommerce 执行计划任务不同Services协调操作导致更新数据失败的问题!

    问题描述: 在Nop的计划任务里需要两个任务协调操作 _shipmentService.InsertShipment(shipment); _orderProcessingService.Ship(s ...

  8. hdu 5137 去掉一个点 使得最短路最大(2014广州现场赛 K题)

    题意:从2~n-1这几个点中任意去掉一个点,使得从1到n的最短路径最大,如果任意去掉一个点1~n无通路输出Inf. Sample Input4 51 2 31 3 71 4 502 3 43 4 23 ...

  9. 【社区公益】送《Web前端开发最佳实践》给需要的人

    算起来至今,我进入软件开发行业已经有11年之久.从最初的研究人工智能,到后来的Web开发,控件开发,直到现在纯粹的Web前端开发.虽然没有大的作品问世,但也是勤勤恳恳,踏实做事,低调做人.从来不吹牛逼 ...

  10. Mysql Window 下安装

    http://blog.csdn.net/u013235478/article/details/50623693