热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)

转载请标明出处:http:www.jianshu.comp70a32892d134本文出自:【江清清博客-代号独狼】(一):写在前面的话接着上一篇继续更新,上一篇文章已经把Fast

转载请标明出处:
http://www.jianshu.com/p/70a32892d134
本文出自:【江清清博客-代号独狼】
(一):写在前面的话
接着上一篇继续更新,上一篇文章已经把FastDev4Android项目数据轻量级缓存ACache组件做了讲解和使用。今天项目更新客户端崩溃异常捕捉组件CustomCrash的讲解和使用。
本文章中的例子代码已经同步到FastDev4Android项目中,地址为:
https://github.com/jiangqqlmj/FastDev4Android
在平时我们都知道,Android系统的手机和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,设备比较多,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设备上导致崩溃的信息,同时可以获取设备的相关信息和用户信息,这对于下一个版本的 BUG 修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序。同时替换系统默认的崩溃弹框,提升应用的用户体验。
首先我们来看一下系统默认的崩溃弹框显示:

《【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)》 1.png

看上面的运行状态,一旦我们的应用出现了异常崩溃,立马会弹框,点击OK应用就退出了,这样用户也不知道发生了什么情况,一下子应用的用户体验下降了很多。更加严重的是,作为我们开发者还不知道云在用户手机的APP什么时候崩溃的,到底因为什么原因崩溃的。 OK下面我们来具体实现自定义的拦截崩溃异常的功能;

(二):具体实现

2.1:定义类 首先我们需要自定义一个实现Thead.UncaughtExceptionHandler的类,然后实现内部接口中定义的方法: void uncaughtException(Thread thread, Throwable ex); 具体如下:

public class CustomCrash implements Thread.UncaughtExceptionHandler

/*
* (non-Javadoc) 进行重写捕捉异常
*
* @see
* java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang
* .Thread, java.lang.Throwable)
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if(type_save==TYPE_SAVE_SDCARD){
// 1,保存信息到sdcard中
saveToSdcard(mContext, ex);
}else if(type_save==TYPE_SAVE_REMOTE){
// 2,异常崩溃信息投递到服务器
saveToServer(mContext,ex);
}
// 3,应用准备退出
showToast(mContext, "很抱歉,程序发生异常,即将推出.");
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
ManagerActivity.getInstance().finishActivity();
android.os.Process.killProcess(android.os.Process.myPid());
}

2.2.上下文注册 我们需要把当前应用的上下文注册到系统的异常处理器中。这样就让系统执行我们自定义的异常捕捉器。

public void setCustomCrashInfo(Context pContext) {
this.mCOntext= pContext;
Thread.setDefaultUncaughtExceptionHandler(this);
}

2.3:崩溃异常日志保存
①:数据保存到SDCard中,直接转换异常日志信息,写入SDCard文件中如下:

/**
* 保存异常信息到sdcard中
*
* @param pContext
* @param ex
* 异常信息对象
*/
private void saveToSdcard(Context pContext, Throwable ex) {
String fileName = null;
StringBuffer sBuffer = new StringBuffer();
// 添加异常信息
sBuffer.append(getExceptionInfo(ex));
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File file1 = new File(CRASH_SAVE_SDPATH);
if (!file1.exists()) {
file1.mkdir();
}
fileName = file1.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
File file2 = new File(fileName);
FileOutputStream fos;
try {
fos = new FileOutputStream(file2);
fos.write(sBuffer.toString().getBytes());
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}

②:数据POST投递到服务器中,进行接口存入相应的文件或者数据库中

/**
* 进行把数据投递至服务器
* @param pContext
* @param ex 崩溃异常
*/
private void saveToServer(Context pContext,Throwable ex){
final String carsh_log=getExceptionInfo(ex);
new Thread(new Runnable() {
@Override
public void run() {
HashMap params=new HashMap();
params.put("crash_log",carsh_log);
String result= IoUtils.responseFromServiceByGetNo(CARSH_LOG_DELIVER, params);
if (result.equals("1")){
Log.d(TAG,"崩溃日志投递成功...");
}else {
Log.d(TAG,"崩溃日志投递失败...");
}
}
}).start();
}

2.4:异常捕捉器具体使用: 我们需要在自定义的Application初始化方法进行初始化我们的捕捉器,然后设置改变系统捕捉器处理方式:

@Override
public void onCreate() {
super.onCreate();
this.instance=this;
//初始化崩溃日志收集器
CustomCrash mCustomCrash=CustomCrash.getInstance();
mCustomCrash.setCustomCrashInfo(this);
}

OK下面我们来具体来使用一下,我们故意制造一个空指针异常:结果分别如下:

《【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)》 2.png

《【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)》 3.png

《【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)》 4.png

《【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)》 5.png

《【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)》 6.png

2.5:由于上面对于类的核心方法进行了讲解,并且该类其他也没有多少行代码,方便大家阅读这边直接把整个类复制如下,并且为了大家测试使用,类中崩溃日志投递的地址也是可以正常使用的。

package com.chinaztt.fda.crash;
import android.content.Context;
import android.os.Environment;
import android.os.Looper;
import android.widget.Toast;
import com.chinaztt.fda.utils.IoUtils;
import com.chinaztt.fda.utils.Log;
import com.chinaztt.fda.utils.ManagerActivity;
import com.chinaztt.fda.utils.StrUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;
/**
* 当前类注释:客户端运行 异常崩溃数据扑捉异常保存SD卡或者实时投递服务器工具类
* 项目名:FastDev4Android
* 包名:com.chinaztt.fda.crash
* 作者:江清清 on 15/10/26 13:29
* 邮箱:jiangqqlmj@163.com
* QQ: 781931404
* 公司:江苏中天科技软件技术有限公司
*/
public class CustomCrash implements Thread.UncaughtExceptionHandler{
private static final String TAG="CustomCrash";
private static final int TYPE_SAVE_SDCARD=1; //崩溃日志保存本地SDCard --建议开发模式使用
private static final int TYPE_SAVE_REMOTE=2; //崩溃日志保存远端服务器 --建议生产模式使用
private int type_save=2; //崩溃保存日志模式 默认为2,采用保存Web服务器
private static final String CRASH_SAVE_SDPATH="sdcard/fda_cache/"; //崩溃日志SD卡保存路径
private static final String CARSH_LOG_DELIVER="http://img2.xxh.cc:8080/SalesWebTest/CrashDeliver";
private static CustomCrash instance = new CustomCrash();
private Context mContext;
private CustomCrash() {
}
/**
*
* @return
*/
public static CustomCrash getInstance() {
return instance;
}
/*
* (non-Javadoc) 进行重写捕捉异常
*
* @see
* java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang
* .Thread, java.lang.Throwable)
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if(type_save==TYPE_SAVE_SDCARD){
// 1,保存信息到sdcard中
saveToSdcard(mContext, ex);
}else if(type_save==TYPE_SAVE_REMOTE){
// 2,异常崩溃信息投递到服务器
saveToServer(mContext,ex);
}
// 3,应用准备退出
showToast(mContext, "很抱歉,程序发生异常,即将推出.");
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
ManagerActivity.getInstance().finishActivity();
android.os.Process.killProcess(android.os.Process.myPid());
}
/**
* 设置自定异常处理类
*
* @param pContext
*/
public void setCustomCrashInfo(Context pContext) {
this.mCOntext= pContext;
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 保存异常信息到sdcard中
*
* @param pContext
* @param ex
* 异常信息对象
*/
private void saveToSdcard(Context pContext, Throwable ex) {
String fileName = null;
StringBuffer sBuffer = new StringBuffer();
// 添加异常信息
sBuffer.append(getExceptionInfo(ex));
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File file1 = new File(CRASH_SAVE_SDPATH);
if (!file1.exists()) {
file1.mkdir();
}
fileName = file1.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
File file2 = new File(fileName);
FileOutputStream fos;
try {
fos = new FileOutputStream(file2);
fos.write(sBuffer.toString().getBytes());
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 进行把数据投递至服务器
* @param pContext
* @param ex 崩溃异常
*/
private void saveToServer(Context pContext,Throwable ex){
final String carsh_log=getExceptionInfo(ex);
new Thread(new Runnable() {
@Override
public void run() {
HashMap params=new HashMap();
params.put("crash_log",carsh_log);
String result= IoUtils.responseFromServiceByGetNo(CARSH_LOG_DELIVER, params);
if (result.equals("1")){
Log.d(TAG,"崩溃日志投递成功...");
}else {
Log.d(TAG,"崩溃日志投递失败...");
}
}
}).start();
}
/**
* 获取并且转化异常信息
* 同时可以进行投递相关的设备,用户信息
* @param ex
* @return 异常信息的字符串形式
*/
private String getExceptionInfo(Throwable ex) {
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append("---------Crash Log Begin---------\n");
//在这边可以进行相关设备信息投递--这边就稍微设置几个吧
//其他设备和用户信息大家可以自己去扩展收集上传投递
stringBuffer.append("SystemVersion:"+ StrUtils.getLocalSystemVersion()+"\n");
stringBuffer.append(sw.toString()+"\n");
stringBuffer.append("---------Crash Log End---------\n");
return stringBuffer.toString();
}
/**
* 进行弹出框提示
*
* @param pContext
* @param msg
*/
private void showToast(final Context pContext, final String msg) {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(pContext, msg, Toast.LENGTH_SHORT).show();
Looper.loop();
}
}).start();
}
/**
* 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式
* @param milliseconds
* @return
*/
private String paserTime(long milliseconds) {
System.setProperty("user.timezone", "Asia/Shanghai");
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
TimeZone.setDefault(tz);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String times = format.format(new Date(milliseconds));
return times;
}
}

到此为止我们今天自定义异常捕捉CustomCrash的讲解和使用结果,
详细代码项目地址: https://github.com/jiangqqlmj/FastDev4Android
同时欢迎大家star和fork整个开源快速开发框架项目~如果有什么意见和反馈,欢迎留言,必定第一时间回复。也欢迎有同样兴趣的童鞋加入到该项目中来,一起维护该项目。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
小就小-1980
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有