上篇写到,将程序中没有处理到的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. Dream------Hadoop--FSDataInputStream和FSDataOutputStream

    一.FSDataInputStream    FileSystem中的open()方法实际上返回的是一个FSDataInputStream,而不是标准的java.io类.这个类是java.io.Dat ...

  2. CentOS7最小化安装之后无法联网以及无法使用ifconfig以及无法使用yum安装软件

    无法联网解决办法,CentOS-7默认网卡未激活,进入BOOS设置修改,或者直接修改配置文件,然后重启服务 1 修改网卡配置文件: 激活: 2 重启服务即可: service network rest ...

  3. sleep允许休眠, delay不允许

    在Linux Driver开发中,经常要用到延迟函数:msleep,mdelay/udelay. 虽然msleep和mdelay都有延迟的作用,但他们是有区别的. 1.)对于模块本身 mdelay是忙 ...

  4. 电脑蓝屏提示unexpected store exception的解决方法

    在我们使用电脑的过程中常常会遇到许多问题,对于许多不熟悉电脑的用户常常摸不着头脑,而部分用户在使用电脑时,电脑常常出现蓝屏的情况,并提示你的电脑遇到问题需要重新启动,与此同时,其报告错误代码为“une ...

  5. [转]在C#程序设计中使用Win32类库

    http://blog.163.com/j_yd168/blog/static/496797282008611326218/     C# 用户经常提出两个问题:“我为什么要另外编写代码来使用内置于 ...

  6. shell函数-页面跳转练习->

    实现思维导图-> 实现思路-> 分析:1:先把三个页面的流程作为函数先写下来,定义在脚本的开头,方便下面的调用.2:先从一个流 程开始做,其他的流程类似,比如nginx3:整体实现思路是 ...

  7. pip install 升级时候 出现报asciii码错误的问题。

    原因是pip安装python包会加载我的用户目录,我的用户目录恰好是中文的,ascii不能编码.解决办法是: python目录 Python27\Lib\site-packages 建一个文件site ...

  8. Codeforces 948C Producing Snow(优先队列+思维)

    题目链接:http://codeforces.com/contest/948/problem/C 题目大意:给定长度n(n<=1e5),第一行v[i]表示表示第i堆雪的体积,第二行t[i]表示第 ...

  9. Ajax请求中的async:false/true

    Ajax请求中的async:false/trueasync. 默认是 true,即为异步方式,$.ajax执行后,会继续执行ajax后面的脚本,直到服务器端返回数据后,触发$.ajax里的succes ...

  10. class getConstructor newinstance

    public static void main(String[] args) throws Exception{ /**获取String对象指定的构造方法(通过方法的参数类型,传递 参数的 Class ...