Android进阶——Crash异常捕获并发送到服务器

本篇文章包含以下内容:

Crash异常捕获的简单使用 Crash异常捕获并发送到服务器

在项目中,我们常常会遇到Crash的现象,也就是程序崩溃的时候,这个时候最常看到的就是这个界面

\

如果你的项目已经发布到市场上了,这样的崩溃对于开发人员是看不到的,所以我们得想方法将崩溃信息发送到服务器,交给我们的程序员查看,Google考虑到这一点,也提供了Thread.UncaughtExceptionHandler接口来实现这一问题

一、Crash异常捕获的简单使用

创建Crash异常捕获很简单,主要的步骤有:

创建BaseApplication继承Application并实现Thread.UncaughtExceptionHandler 通过Thread.setDefaultUncaughtExceptionHandler(this)设置默认的异常捕获 最后在manifests中注册创建的BaseApplication
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BaseApplication extends Application implements Thread.UncaughtExceptionHandler {
 
     @Override
     public void onCreate() {
         super .onCreate();
         //设置异常捕获
         Thread.setDefaultUncaughtExceptionHandler( this );
     }
 
     @Override
     public void uncaughtException( final Thread thread, final Throwable ex) {
         //当有异常产生时执行该方法
     }
}

我们可以在uncaughtException()方法中输出异常信息,并让它隔两秒杀死自己进程,这样就不会弹出崩溃的弹窗,让它直接退出程序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
  * 当有异常产生时执行该方法
  * @param thread 当前线程
  * @param ex 异常信息
  */
@Override
public void uncaughtException( final Thread thread, final Throwable ex) {
     new Thread( new Runnable() {
         @Override
         public void run() {
             Log.e( "TAG" , "currentThread:" +Thread.currentThread()+ "---thread:" +thread.getId()+ "---ex:" +ex.toString());
         }
     }).start();
     SystemClock.sleep( 2000 );
     android.os.Process.killProcess(android.os.Process.myPid());
}

最后一步,别忘了在manifests中注册BaseApplication

?
1
</application>

我们通过运行这个方法,来测试我们的程序

?
1
2
3
private void testError() {
     int a = 10 / 0 ;
}

查看Log信息,验证我们的错误信息

?
1
2
3
12 - 18 11 : 40 : 02.074 7510 - 7555 /com.handsome.ap E/TAG: currentThread:Thread[Thread- 325 , 5 ,main]---thread: 1 ---ex:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.handsome.ap/com.handsome.ap.Activity.MainActivity}:
java.lang.ArithmeticException: pide by zero

完整代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BaseApplication extends Application implements Thread.UncaughtExceptionHandler {
 
     @Override
     public void onCreate() {
         super .onCreate();
         //设置异常捕获
         Thread.setDefaultUncaughtExceptionHandler( this );
     }
 
     /**
      * 当有异常产生时执行该方法
      * @param thread 当前线程
      * @param ex 异常信息
      */
     @Override
     public void uncaughtException( final Thread thread, final Throwable ex) {
         new Thread( new Runnable() {
             @Override
             public void run() {
                 Log.e( "TAG" , "currentThread:" +Thread.currentThread()+ "---thread:" +thread.getId()+ "---ex:" +ex.toString());
             }
         }).start();
         SystemClock.sleep( 2000 );
         android.os.Process.killProcess(android.os.Process.myPid());
     }
}

二、Crash异常捕获并发送到服务器

其实这里就是将上面的简单使用进行封装,在一个类中处理相关的逻辑,主要步骤和上面是一样的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CrashHandler implements Thread.UncaughtExceptionHandler {
 
     /**
      * 捕获异常回掉
      *
      * @param thread 当前线程
      * @param ex     异常信息
      */
     @Override
     public void uncaughtException(Thread thread, Throwable ex) {
         //导出异常信息到SD卡
         dumpExceptionToSDCard(ex);
         //上传异常信息到服务器
         uploadExceptionToServer(ex);
         //延时1秒杀死进程
         SystemClock.sleep( 2000 );
         Process.killProcess(Process.myPid());
     }
}

我们为下面的信息保存先提供一些成员变量

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//文件夹目录
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/crash_log/" ;
//文件名
private static final String FILE_NAME = "crash" ;
//文件名后缀
private static final String FILE_NAME_SUFFIX = ".trace" ;
//上下文
private Context mContext;
 
//单例模式
private static CrashHandler sInstance = new CrashHandler();
private CrashHandler() {}
public static CrashHandler getInstance() {
     return sInstance;
}

提供一个初始化的方法,记得调用Thread.setDefaultUncaughtExceptionHandler(this)这个方法

?
1
2
3
4
5
6
7
8
9
10
11
/**
  * 初始化方法
  *
  * @param context
  */
public void init(Context context) {
     //将当前实例设为系统默认的异常处理器
     Thread.setDefaultUncaughtExceptionHandler( this );
     //获取Context,方便内部使用
     mContext = context.getApplicationContext();
}

剩下的就是保存异常信息了,这里发送到服务端采用的是Bmob第三方后端云

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
  * 导出异常信息到SD卡
  *
  * @param ex
  */
private void dumpExceptionToSDCard(Throwable ex) {
     if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
         return ;
     }
     //创建文件夹
     File dir = new File(PATH);
     if (!dir.exists()) {
         dir.mkdirs();
     }
     //获取当前时间
     long current = System.currentTimeMillis();
     String time = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( new Date(current));
     //以当前时间创建log文件
     File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
     try {
         //输出流操作
         PrintWriter pw = new PrintWriter( new BufferedWriter( new FileWriter(file)));
         //导出手机信息和异常信息
         PackageManager pm = mContext.getPackageManager();
         PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
         pw.println( "发生异常时间:" + time);
         pw.println( "应用版本:" + pi.versionName);
         pw.println( "应用版本号:" + pi.versionCode);
         pw.println( "android版本号:" + Build.VERSION.RELEASE);
         pw.println( "android版本号API:" + Build.VERSION.SDK_INT);
         pw.println( "手机制造商:" + Build.MANUFACTURER);
         pw.println( "手机型号:" + Build.MODEL);
         ex.printStackTrace(pw);
         //关闭输出流
         pw.close();
     } catch (Exception e) {
 
     }
}
 
/**
  * 上传异常信息到服务器
  *
  * @param ex
  */
private void uploadExceptionToServer(Throwable ex) {
     Error error = new Error(ex.getMessage());
     error.save( new SaveListener<string>() {
         @Override
         public void done(String objectId, BmobException e) {
 
         }
     });
}</string>

在我们的Application中创建该异常捕获

?
1
2
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init( this );

我们同样按照上面的方法来测试这个异常捕获,运行程序,在文件夹中找到我们创建的目录

\

找到对应文件

\

查看对应信息

\

后台数据库的信息

\

完整代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class CrashHandler implements Thread.UncaughtExceptionHandler {
 
     //文件夹目录
     private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/crash_log/" ;
     //文件名
     private static final String FILE_NAME = "crash" ;
     //文件名后缀
     private static final String FILE_NAME_SUFFIX = ".trace" ;
     //上下文
     private Context mContext;
 
     //单例模式
     private static CrashHandler sInstance = new CrashHandler();
     private CrashHandler() {}
     public static CrashHandler getInstance() {
         return sInstance;
     }
 
     /**
      * 初始化方法
      *
      * @param context
      */
     public void init(Context context) {
         //将当前实例设为系统默认的异常处理器
         Thread.setDefaultUncaughtExceptionHandler( this );
         //获取Context,方便内部使用
         mContext = context.getApplicationContext();
     }
 
     /**
      * 捕获异常回掉
      *
      * @param thread 当前线程
      * @param ex     异常信息
      */
     @Override
     public void uncaughtException(Thread thread, Throwable ex) {
         //导出异常信息到SD卡
         dumpExceptionToSDCard(ex);
         //上传异常信息到服务器
         uploadExceptionToServer(ex);
         //延时1秒杀死进程
         SystemClock.sleep( 2000 );
         Process.killProcess(Process.myPid());
     }
 
     /**
      * 导出异常信息到SD卡
      *
      * @param ex
      */
     private void dumpExceptionToSDCard(Throwable ex) {
         if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
             return ;
         }
         //创建文件夹
         File dir = new File(PATH);
         if (!dir.exists()) {
             dir.mkdirs();
         }
         //获取当前时间
         long current = System.currentTimeMillis();
         String time = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( new Date(current));
         //以当前时间创建log文件
         File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
         try {
             //输出流操作
             PrintWriter pw = new PrintWriter( new BufferedWriter( new FileWriter(file)));
             //导出手机信息和异常信息
             PackageManager pm = mContext.getPackageManager();
             PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
             pw.println( "发生异常时间:" + time);
             pw.println( "应用版本:" + pi.versionName);
             pw.println( "应用版本号:" + pi.versionCode);
             pw.println( "android版本号:" + Build.VERSION.RELEASE);
             pw.println( "android版本号API:" + Build.VERSION.SDK_INT);
             pw.println( "手机制造商:" + Build.MANUFACTURER);
             pw.println( "手机型号:" + Build.MODEL);
             ex.printStackTrace(pw);
             //关闭输出流
             pw.close();
         } catch (Exception e) {
 
         }
     }
 
     /**
      * 上传异常信息到服务器
      *
      * @param ex
      */
     private void uploadExceptionToServer(Throwable ex) {
         Error error = new Error(ex.getMessage());
         error.save( new SaveListener<string>() {
             @Override
             public void done(String objectId, BmobException e) {
 
             }
         });
     }
}
</string>

到这里我们的Crash异常捕获就结束了,很简单的一段代码就可以解决你缺少的项目经验


转自:https://www.2cto.com/kf/201612/578595.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值