我们写程序的时候都希望能写出一个没有任何Bug的程序,期望在任何情况下都不会发生程序崩溃。不过理想是丰满的,现实是骨感的。没有一个程序员能保证自己写的程序绝对不会出现异常崩溃。特别是针对用户数达到几十万几百万的程序,当你用户数达到一定数量级后,就算你的程序出现个别异常崩溃情况也不用惊讶。

  既然我们写的程序都有可能发生异常崩溃,如果是还没发布的程序,我们可以通过测试抓取Log来分析。不过针对已经发布的程序,我们没法重现现象,所以让用户反馈程序异常信息就很重要。下面我们说说如何收集程序运行过程的异常信息。

(PS:新建的QQ群,有兴趣可以加入一起讨论:Android群:322599434)

1、Android异常捕获接口

//当线程因未捕获的异常而突然终止时,调用处理程序的接口
static interface UncaughtExceptionHandler

2、设置线程捕获异常

  从上面的接口我们可以看到,这个接口是针对线程来说,也就是说我们如果需要监控某个线程运行情况,只要把这个接口实现了,然后把监控方法设置到具体的线程里面即可。一般来说,我们最需要监控的就是我们的UI线程也就是主线程。

//设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

3、UncaughtExceptionHandler 实例


//Edited by mythou
//http://www.cnblogs.com/mythou/
public class MythouCrashHandler implements UncaughtExceptionHandler
{
private static final String TAG = "MythouCrashHandler---->";
private UncaughtExceptionHandler defaultUEH;

  //构造函数,获取默认的处理方法
public MythouCrashHandler()
{
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
}

  //这个接口必须重写,用来处理我们的异常信息
@Override
public void uncaughtException(Thread thread, Throwable ex)
{
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
     //获取跟踪的栈信息,除了系统栈信息,还把手机型号、系统版本、编译版本的唯一标示
StackTraceElement[] trace = ex.getStackTrace();
StackTraceElement[] trace2 = new StackTraceElement[trace.length+];
System.arraycopy(trace, , trace2, , trace.length);
trace2[trace.length+] = new StackTraceElement("Android", "MODEL", android.os.Build.MODEL, -);
trace2[trace.length+] = new StackTraceElement("Android", "VERSION", android.os.Build.VERSION.RELEASE, -);
trace2[trace.length+] = new StackTraceElement("Android", "FINGERPRINT", android.os.Build.FINGERPRINT, -);
    //追加信息,因为后面会回调默认的处理方法
ex.setStackTrace(trace2);
ex.printStackTrace(printWriter);
     //把上面获取的堆栈信息转为字符串,打印出来
String stacktrace = result.toString();
printWriter.close();
Log.e(TAG, stacktrace); //这里把刚才异常堆栈信息写入SD卡的Log日志里面
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
String sdcardPath = Environment.getExternalStorageDirectory().getPath();
writeLog(stacktrace, sdcardPath + "/mythou");
} defaultUEH.uncaughtException(thread, ex);
}

  //写入Log信息的方法,写入到SD卡里面
private void writeLog(String log, String name)
{
CharSequence timestamp = DateFormat.format("yyyyMMdd_kkmmss", System.currentTimeMillis());
String filename = name + "_" + timestamp + ".log"; try
{
FileOutputStream stream = new FileOutputStream(filename);
OutputStreamWriter output = new OutputStreamWriter(stream);
BufferedWriter bw = new BufferedWriter(output);
       //写入相关Log到文件
bw.write(log);
bw.newLine();
bw.close();
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

  上面就是实现了获取处理跟踪信息的方法,上面的方法是参照VLC的异常处理机制编写的。做了一些简单修改。不过上面只是获取了异常信息,如果程序安装到用户机器上,我们没法获取到这些信息,总不能让用户把机器拿过来给你,然后你把Log拷贝出来吧。(这个我以前做嵌入式的时候到试过,让客户把机器拿过来,拷贝里面的Log,那时候做的机器无法联网。现在想起来都纠结,O(∩_∩)O哈哈~) 为了不再纠结,我们需要一个可以把Log发送到我们服务器的功能,下面是把一个服务信息发送到我们指定服务器功能。

3、通过网络发送Log

//Edited by mythou
//http://www.cnblogs.com/mythou/
   public class SendCrashLog extends AsyncTask<String, String, Boolean>
{
public SendCrashLog() { } @Override
protected Boolean doInBackground(String... params)
{
if (params[].length() == )
return false;
HttpClient httpClient = new DefaultHttpClient();
       //你的服务器,这里只是举个例子。把异常信息当作http请求发送到服务器
HttpPost httpPost = new HttpPost("http://www.mythou/getlog.php");
       //这里把相关的异常信息转为http post请求的数据参数
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("model", params[]));
nameValuePairs.add(new BasicNameValuePair("device", params[]));
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
         //发送相关请求信息
httpClient.execute(httpPost);
} catch (ClientProtocolException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
Log.d(TAG, "Device model sent.");
return true;
} @Override
protected void onPostExecute(Boolean result) { }
}

  上面就是我上一篇文章讲的异步任务的使用,我们在异步任务里面编写了一个发送http请求的服务,用来把相关的异常信息发送到我们指定的服务器上面。这个需要你的服务器解析发送的http请求,这个难度不大,一般做个web的人都知道如何做。在上面的异常处理里面再调用这里的发送方法:

//Edited by mythou
//http://www.cnblogs.com/mythou/
SendCrashLogsendLog = new SendCrashLog();
//刚才的异常信息字符串
sendLog .execute(stacktrace);

通过上面的方法就可以把异常信息发送到指定的服务器,也就可以跟踪客户使用软件的情况,方便我们修改程序的问题。当然这个信息收集一般都隐私和后台流量问题,这个需要在程序里面做点提示,免得背上流氓软件的骂名。

Edited by mythou

原创博文,转载请标明出处:http://www.cnblogs.com/mythou/p/3194400.html 

Android 收集已发布程序的崩溃信息的更多相关文章

  1. 【转】Android 收集已发布程序的崩溃信息

    我们写程序的时候都希望能写出一个没有任何Bug的程序,期望在任何情况下都不会发生程序崩溃.不过理想是丰满的,现实是骨感的.没有一个程序员能保证自己写的程序绝对不会出现异常崩溃.特别是针对用户数达到几十 ...

  2. Android将应用程序的崩溃信息如何保存到本地文件,并上传服务器

    导语:最近实在是太忙了,没有怎么更新公众号,也没有怎么认真去写一些内容,在这里先给关注我的朋友说一声抱歉,可能在接下来的一段时间,还是很忙,但是我会争取抽空多分享一下技术文章,给大家看,共同进步,也希 ...

  3. Android中获取应用程序(包)的信息----PackageManager

    本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占用大小等.具体分为两个 部分,计划如下:   第一部分: 获取应用程序的packagena ...

  4. Android中获取应用程序(包)的信息-----PackageManager的使用(一)

    本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占用大小等.具体分为两个 部分,计划如下:  第一部分: 获取应用程序的packagenam ...

  5. Android中获取应用程序(包)的信息-----PackageManager的使用

    本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占用大小等.具体分为两个 部分,计划如下: 第一部分: 获取应用程序的packagename ...

  6. 【转】Android中获取应用程序(包)的信息-----PackageManager的使用(一)

    转载请注明出处:http://blog.csdn.net/qinjuning       本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占 ...

  7. Android中获取应用程序(包)的大小-----PackageManager的使用(二)

    通过第一部分<<Android中获取应用程序(包)的信息-----PackageManager的使用(一)>>的介绍,对PackageManager以及 AndroidMani ...

  8. 如何实现已发布app的自动更新

    要实现app的自动更新,做两件事情就可以搞定 1.获取当前手机中的app版本号 我们可以通过查询mainbundle中的获取CFBundleVersion NSDictionary *infoDict ...

  9. 构建一个Gods Eye Android应用程序:第1部分 – 收集已安装的Android应用程序

    首先问候一下我的黑客伙伴们,在之前的Introduction to Amunet 教程中,我们了解到Amunet可能是一个间谍Android应用程序. 我不浪费太多时间因而直入主题. 在本教程中,我们 ...

随机推荐

  1. KnockoutJs学习笔记

    1.缘由 新公司前端用的是Jquery+Knockout,Knockout只知道是MVVM,未曾接触.报到前,先学习下. 2.前置知识 MVVM模式介绍 MVVM:模型-视图-视图模型(Model-V ...

  2. Mac OS X各版本号的历史费用和升级关系

     Mac OS X各版本号的历史费用和升级关系 OS X 10.6 Snow Leopard 早在2009年10月,Mac OS X10.6雪豹是通过光盘发送.并在英国推出时.费用£25 OS X ...

  3. 最优化方法:共轭梯度法(Conjugate Gradient)

    http://blog.csdn.net/pipisorry/article/details/39891197 共轭梯度法(Conjugate Gradient) 共轭梯度法(英语:Conjugate ...

  4. 【转载】mysql 热备份

    原文: http://www.jb51.net/article/70967.htm 热备和冷备是两个相对的概念,冷备是把数据库服务,比如MySQL,Oracle停下来,然后使用拷贝.打包或者压缩命令对 ...

  5. App开发准备

    一. Android开发 二. IOS开发 1. 准备苹果电脑 Mac pro 一般比较贵,很少人或公司使用 替代的产品为 iMac 或 Mac mini 中配8G内存版 2. 准备苹果开发者账户,才 ...

  6. pycharm + selenium + python 提示 Unresolved reference 'webdriver' 解决办法

    尝试使用python + selenium + pycharm 做自动化测试, 命令行pip install selenium 安装了selenium.但是使用pycharm 新建一个测试项目后并新建 ...

  7. 小程序踩过的一个小坑---解析二维码decodeURIComponent() url解码

    因为我们需要用户扫码进入小程序,每一个货柜都有一个对应的二维码,当然每个二维码里的信息也不一样.用户扫码进入小程序之后,二维码的信息会以参数q带进去,而我们只能在onLoad事件中拿到这个参数, 但是 ...

  8. Mongo如何导出 CSV文件

    mongoexport /h localhost /port 37189 /u aa /p aa /authenticationDatabase admin /d testdb /c usertag ...

  9. react-navigation使用技巧

      因为react-navigation之前存在的问题相对较多,本文更新会稍慢,而且,我现在项目使用的是基于它封装的react-native-router-fluxV4版本,现在也推荐给大家使用.在下 ...

  10. Why do people integrate Spark with TensorFlow even if there is a distributed TensorFlow framework?

    https://www.quora.com/Why-do-people-integrate-Spark-with-TensorFlow-even-if-there-is-a-distributed-T ...