示例代码下载 : http://download.csdn.net/detail/han1202012/8638801;

一. 崩溃日志本地存储

1. 保存原理解析

崩溃信息本地保存步骤 :

-- 1. 自定义类实现 UncaughtExceptionHandler : public class CrashHandler implements UncaughtExceptionHandler;

-- 2. 设置该自定义的 CrashHandler 类为单例模式 : 
// 单例模式
private static CrashHandler INSTANCE = new CrashHandler();
private CrashHandler() {
}
public static CrashHandler getInstance() {
return INSTANCE;
}
-- 重写 uncaughtException 方法 : 
@Override
public void uncaughtException(Thread thread, Throwable ex)
-- 自定义 handleException 方法处理异常信息 : 在该方法中进行设备信息收集, 以及将信息保存到文件中;

(1) UncaughtExceptionHandler 类解析

UncaughtExceptionHandler 作用 : 该类处理以下情况, 如果有未捕获的异常发生, 出现了程序崩溃闪退的情况, 此时会回调该类的 uncaughtException 方法;

(2) 线程相关

线程相关 : 每个线程都对应有响应的默认的未捕获异常处理器;

-- 获取线程默认的未捕获异常处理器 : Thread.getDefaultUncaughtExceptionHandler();

-- 设置线程默认的未捕获异常处理器 : Thread.setDefaultUncaughtExceptionHandler(this);

(3) uncaughtException 方法

uncaughtException 方法解析 :

-- 回调时机 : 出现未定义的异常时;

-- 回调参数 : 回调时会传入 线程对象 和 要抛出的异常信息, 我们可以在程序中拿到这两个信息;

public void uncaughtException(Thread thread, Throwable ex)

(4) 手机设备信息

手机设备信息手机步骤 :

-- 1. 获取包信息 :

//获取包管理器
PackageManager pm = ctx.getPackageManager();
//获取包信息
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
PackageManager.GET_ACTIVITIES);
-- 2. 获取版本号信息 :

if (pi != null) {
//版本号
String versionName = pi.versionName == null ? "null"
: pi.versionName;
//版本代码
String versionCode = pi.versionCode + "";
//将版本信息存放到 成员变量 Map<String, String> mInfos 中
this.mInfos.put("versionName", versionName);
this.mInfos.put("versionCode", versionCode);
}

-- 3. 使用反射获取 Build 类成员变量变量 , 并遍历获取这些变量内容:

//获取 Build 中定义的变量, 使用反射方式获取, 该类中定义了设备相关的变量信息
Field[] fields = Build.class.getDeclaredFields();
//遍历获取额变量, 将这些信息存放到成员变量 Map<String, String> mInfos 中
for (Field field : fields) {
try {
//设置 Build 成员变量可访问
field.setAccessible(true);
//将 设备相关的信息存放到 mInfos 成员变量中
mInfos.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);
}
}

(4) 保存崩溃信息到文件

保存文件步骤 : 这些步骤就很简单了, 使用 IO流即可;

-- 1. 将之前获取的 Build 设备信息, 版本信息, 崩溃信息转为字符串 :

//存储相关的字符串信息
StringBuffer sb = new StringBuffer();
//将成员变量 Map<String, String> mInfos  中的数据 存储到 StringBuffer sb 中
for (Map.Entry<String, String> entry : this.mInfos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
-- 2. 在 Logcat 中打印崩溃信息 : 之前的默认操作就是打印崩溃信息到 Logcat 中, 我们在这里继续执行完这个步骤, 否则Logcat 中没有数据的;

//将 StringBuffer sb 中的字符串写出到文件中
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();
-- 3. 写出数据到文件中 : IO 流知识点, 不再做过多赘述;

String result = writer.toString();
sb.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = formatter.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".txt";
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
//获取文件输出路径
String path = Environment.getExternalStorageDirectory()
+ "/crashinfo/";
//创建文件夹和文件
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
//创建输出流
FileOutputStream fos = new FileOutputStream(path + fileName);
//向文件中写出数据
fos.write(sb.toString().getBytes());
fos.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}

2. 代码及示例

(1) 相关代码示例

故意发生错误的代码 :

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

}

public void onClick(View view) {
int i = 3;
i = i / 0;
}

}

CrashHandler 注册代码 : 在 Activity 或者 Application 中注册该代码;

CrashHandler.getInstance().init(getApplicationContext());

CrashHandler 代码 :

package cn.org.octpus.crash;

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.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * UncaughtExceptionHanlder 作用 : 处理 线程被未捕获的异常终止 的情况, 一旦出现了未捕获异常崩溃, 系统就会回调该类的
 * uncaughtException 方法;
 */
public class CrashHandler implements UncaughtExceptionHandler {
// 用于打印日志的 TAG 标识符
public static final String TAG = "octopus.CrashHandler";

// 系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
// 程序的Context对象
private Context mContext;
// 用来存储设备信息和异常信息
private Map<String, String> mInfos = new HashMap<String, String>();
// 用于格式化日期,作为日志文件名的一部分
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

// 单例模式
private static CrashHandler INSTANCE = new CrashHandler();
private CrashHandler() {
}
public static CrashHandler getInstance() {
return INSTANCE;
}

/**
* 初始化该类, 向系统中注册
* @param context
*/
public void init(Context context) {
mContext = context;
// 获取系统默认的 UncaughtException 处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该 CrashHandler 为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}

/*
* 出现未捕获的异常时, 会自动回调该方法
* (non-Javadoc)
* @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable)
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
/*
* 调用 handleException() 方法处理该线程
* 如果返回 true 说明处理成功, 如果返回 false 则调用默认的异常处理器来处理
* 一般情况下该方法都会成功处理
*/
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;
}
/*
* 使用Toast来显示异常信息, 
* 由于在主线程会阻塞, 
* 不能实时出现 Toast 信息, 
* 这里我们在子线程中处理 Toast 信息
*/
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG)
.show();
Looper.loop();
}
}.start();
// 收集设备参数信息
collectDeviceInfo(mContext);
// 保存日志文件
saveCrashInfo2File(ex);
return true;
}

/**
* 收集设备参数信息, 将手机到的信息存储到
* @param ctx
* 上下文对象
*/
public void collectDeviceInfo(Context ctx) {
try {
//获取包管理器
PackageManager pm = ctx.getPackageManager();
//获取包信息
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
PackageManager.GET_ACTIVITIES);
if (pi != null) {
//版本号
String versionName = pi.versionName == null ? "null"
: pi.versionName;
//版本代码
String versionCode = pi.versionCode + "";
//将版本信息存放到 成员变量 Map<String, String> mInfos 中
this.mInfos.put("versionName", versionName);
this.mInfos.put("versionCode", versionCode);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}

//获取 Build 中定义的变量, 使用反射方式获取, 该类中定义了设备相关的变量信息
Field[] fields = Build.class.getDeclaredFields();
//遍历获取额变量, 将这些信息存放到成员变量 Map<String, String> mInfos 中
for (Field field : fields) {
try {
//设置 Build 成员变量可访问
field.setAccessible(true);
//将 设备相关的信息存放到 mInfos 成员变量中
mInfos.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);
}
}
}

/**
* 保存错误信息到文件中
* @param ex
* @return 返回文件名称,便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {
//存储相关的字符串信息
StringBuffer sb = new StringBuffer();
//将成员变量 Map<String, String> mInfos  中的数据 存储到 StringBuffer sb 中
for (Map.Entry<String, String> entry : this.mInfos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}

//将 StringBuffer sb 中的字符串写出到文件中
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 + ".txt";
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
//获取文件输出路径
String path = Environment.getExternalStorageDirectory()
+ "/crashinfo/";
//创建文件夹和文件
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
//创建输出流
FileOutputStream fos = new FileOutputStream(path + fileName);
//向文件中写出数据
fos.write(sb.toString().getBytes());
fos.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
return null;
}
}

(2) 结果示例

崩溃日志存放文件路径 : /storage/sdcard0/crashinfo/crash-2015-04-27-19-31-41-1430134301642.txt;

-- 说明 : 其中 /storage/sdcard0/ 是系统默认的 SD 卡路径, crashinfo/crash-2015-04-27-19-31-41-1430134301642.txt 是我们创建的文件;

崩溃日志内容 :

1430134301642.txt                                                             <
HARDWARE=pxa1088
RADIO=unknown
versionCode=1
HOST=SWDA2601
TAGS=release-keys
ID=JDQ39
MANUFACTURER=samsung
TYPE=user
IS_TRANSLATION_ASSISTANT_ENABLED=false
IS_SECURE=false
TIME=1416298944000
FINGERPRINT=samsung/wilcoxdszn/wilcoxds:4.2.2/JDQ39/G3812ZNUANK1:user/release-keys
UNKNOWN=unknown
BOARD=PXA1088
PRODUCT=wilcoxdszn
versionName=1.0
DISPLAY=JDQ39.G3812ZNUANK1
USER=se.infra
DEVICE=wilcoxds
MODEL=SM-G3812
BOOTLOADER=unknown
CPU_ABI=armeabi-v7a
CPU_ABI2=armeabi
IS_SYSTEM_SECURE=false
IS_DEBUGGABLE=false
SERIAL=5202889565301100
BRAND=samsung
java.lang.IllegalStateException: Could not execute method of the activity
at android.view.View$1.onClick(View.java:3804)
at android.view.View.performClick(View.java:4439)
at android.widget.Button.performClick(Button.java:142)
at android.view.View$PerformClick.run(View.java:18395)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5319)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at android.view.View$1.onClick(View.java:3799)
... 12 more
Caused by: java.lang.ArithmeticException: divide by zero
at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
... 15 more
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at android.view.View$1.onClick(View.java:3799)
at android.view.View.performClick(View.java:4439)
at android.widget.Button.performClick(Button.java:142)
at android.view.View$PerformClick.run(View.java:18395)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5319)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ArithmeticException: divide by zero
at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
... 15 more
java.lang.ArithmeticException: divide by zero
at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at android.view.View$1.onClick(View.java:3799)
at android.view.View.performClick(View.java:4439)
at android.widget.Button.performClick(Button.java:142)
at android.view.View$PerformClick.run(View.java:18395)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5319)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)

------转载自http://www.bkjia.com/Androidjc/992161.html

二、奔溃日志保存到远程服务器上

实现途径 :

-- 1. 自己开发服务器端 : 在上面的保存文件的基础上, 在开发一个服务器端, 使用网络编程接口将打印到文件的内容上传到服务器端;

-- 2. 使用第三方服务 : 使用 Testin 的崩溃大师, 地址 http://crash.testin.cn/app?scnavbar ;

Android之崩溃日志本地存储与远程保存相关推荐

  1. 【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存

    示例代码下载 : http://download.csdn.net/detail/han1202012/8638801; 一. 崩溃日志本地存储 1. 保存原理解析 崩溃信息本地保存步骤 : -- 1 ...

  2. Android 异常崩溃日志,捕捉并保存到本地

    Android 异常崩溃日志,捕捉并保存到本地: 前几天因为在省公安厅做一个通讯类之类的应用:碰到个问题,就是download人员信息将信息保存到本地数据库完成的时候,菊花转还没有dismission ...

  3. Android捕捉崩溃日志并输出日志文件

    Android捕捉崩溃日志并输出日志文件 当程序与运行时发生崩溃,可以捕捉到当前崩溃的日志信息并写入文件保存到指定的目录下.这里还做了最大文件数量限制,超过数量即删除旧日志文件. import jav ...

  4. Android 应用崩溃日志的收集和上传

    如何将应用崩溃日志收集起来? Android 应用难以避免的会 crash ,也称为崩溃,无论你的程序多完美,总是无法避免 crash 的发生.这对用户来说是很不友好的,也是开发者所不愿意看到的.更糟 ...

  5. android拷贝文件到内部存储,将文件保存在内部存储android中

    我是Android的新手,当我尝试将文件保存到内部存储器时,我遇到了问题,新的示例适用于我的sdk,但不适用于我的手机. 我正试图在一个索尼爱立信xperia中运行de示例,顺便说一下android ...

  6. android xlog崩溃日志,腾讯Xlog接入指南与踩过的坑

    一 为什么要接入日志打印系统? 相信大家在开发应用的时候,总会遇到bug,这个时候,如果bug是在我们本地开发的过程中发现的,那么我们把手机插入android studio进行联调,就可以马上定位到出 ...

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

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

  8. linux日志本地存储和网络存储,文件系统和本地存储管理 1

    Linux系统管理 1.存储管理 传统的磁盘分区 RAID技术,软RAID的实现 LVM 文件系统管理 ext,xfs,btrfs(了解) 2.程序包管理 rpm.yum.dnf 3.sed.gawk ...

  9. android 记录崩溃日志

    2019独角兽企业重金招聘Python工程师标准>>> 每个android应用都是由一个Application和多个activity或者server构成.应用启动时,会首先启动App ...

  10. android xlog崩溃日志,Android第三方log库:xlog使用记录

    第一步:由于xlog发布在jitpack 仓库,所以如果android studio没有在project目下的的build.gradle下配置jitpack仓库的话需要配置下 allprojects ...

最新文章

  1. windows10下使用wget命令(安装失败,请大家提意见)
  2. [转载] 大道至简:软件工程实践者的思想——第六章 谁是解结的人
  3. linux查找文件里面的内容
  4. java浮点数原理,浮点型数据存储原理
  5. Python基础语法-三种函数特殊形参定义以及使用方式
  6. mysql---mysql查看数据库操作记录
  7. “根本就不需要 Kafka 这样的大型分布式系统!”
  8. 警惕“***性社工”现象
  9. EXCEL 两列名字 如何快速查看缺少了哪些人名
  10. 计算机科学与技术专接本的历年真题,10年计算机专业专接本真题
  11. NAND Flash 芯片测试
  12. spss 描述性分析
  13. RTA PAVIA CSD ET04-1 ET04
  14. 计算机组成原理编码键盘实验报告,计算机组成原理实验报告–杨睿.doc
  15. EasyPusher安卓Android手机直播推送之RTSP流媒体协议流程
  16. kali局域网ARP攻击欺骗+图片获取
  17. 像素和分辨率是什么?
  18. 小米8Lite刷机教程(大多数手机操作基本通用)
  19. Java输出字符串出现次数最多的字母
  20. 天地飞9设置Pixhawk/PX4六段开关

热门文章

  1. DPDK框架原理简介(0003转)
  2. 使用systemtap调试Linux内核
  3. python property 与get/set方法详解
  4. cocos2d-x 之 简单数据存储——Userdefault
  5. python中用于返回元组中元素最小值的是_第5章习题
  6. html4如何插背景音乐,HTML插入背景音乐方法【全】
  7. 计算机不显示用户组密码设置,手把手教你设置win10系统开机时跳过锁屏画面直接显示密码框登录界面-系统操作与应用 -亦是美网络...
  8. thinkphp5 两个控制器传参数_PID参数整定:看大神是如何整定参数
  9. php entity,PHP html_entity_decode() 函数_程序员人生
  10. Netty中的Channel之数据冲刷与线程安全(writeAndFlush)