Android应用不可避免的会发生各种各样的crash,也称之为闪退。无论你的程序写的多么完美,crash是不可避免的,可能是由于Android系统底层的bug,也可能是由于不充分的机型适配或者糟糕的网络,当然也可能是程序逻辑或者数据的问题,总之是,程序在上线之前,没有测试出的bug。当crash发生的时候,系统会kill掉正在执行的程序,现象就是闪退或者提示用户程序已经停止运行,这对用户来说是糟糕的,当然,对于开发人员更为糟糕,开发人员无法获取crash的信息,便无从下手进行异常补救。
那我们怎么补救呢,来看一个类Thread:
从字面意思我们可以知道是未被捕获异常的处理;
接下来我们就围绕这个类开始进行全局异常捕获处理。
第一步:新建类,实现接口 Thread.UncaughtExceptionHandler
我们采用单例模式进行,代码如下:
第二步:进行初始化操作
第三步:实现对线程异常的捕获记录
异常写入本地文件的方法:
获取异常手机信息的方法体:
上传到服务器的方法(未实现)(大家可以自己根据实际情况来写,毕竟不是什么文件传到后台都是安全的,所以上传的时候,需要和后台制定好策略):
第四步:在application中进行初始化
第五步:在Activity中进行测试工作
ok! 到这基本就结束了!当然别忘啦加sd卡的写权限哦!运行的结果如下图:
下面贴源码:
异常捕获类
package com.tian.bindertest.handlerException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.os.SystemClock;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by TCX on 2017/7/9.
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG="CrashHandler";
private static final boolean DEBUG=true;
//异常文件的路径
private static final String PATH= Environment.getExternalStorageDirectory().getPath()+"/CrashTest/log/";
private static final String FILE_NAME="crash";
private static final String FILE_NAME_SUFFIX=".trace";
private static CrashHandler sInstance=new CrashHandler();
private Thread.UncaughtExceptionHandler mUncaughtExceptionHandler;
private Context mContext;
private CrashHandler(){
}
public static CrashHandler getsInstance() {
return sInstance;
}
/**
* 初始化操作
* @param context
*/
public void init(Context context){
mUncaughtExceptionHandler=Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext=context.getApplicationContext();
}
/**
* 这个方法是最为关键的,当程序中未有被捕获的异常的时候,会调用 uncaughtException
* @param thread 未捕获异常的线程
* @param throwable 未捕获的异常
*/
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
//将捕获的异常写到SD卡中去
write2SD(throwable);
//将异常提交到服务器端
upload2Server();
throwable.printStackTrace();
//如果系统自己提供的处理异常的机制就交给系统自己来处理,否则自己处理
if(mUncaughtExceptionHandler!=null){
mUncaughtExceptionHandler.uncaughtException(thread,throwable);
}else{
Process.killProcess(Process.myPid());
}
}
private void write2SD(Throwable throwable) {
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
if (DEBUG){
Log.w(TAG,"sdcard unmounted, skip dump exception!");
}
}
File dir=new File(PATH);
if(!dir.exists()){
dir.mkdirs();
}
long current= SystemClock.currentThreadTimeMillis();
String currentTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
File file=new File(PATH+FILE_NAME+FILE_NAME_SUFFIX);
try {
PrintWriter writer=new PrintWriter(new BufferedWriter(new FileWriter(file)));
writer.println(currentTime);
writer.println("===================================================");
writer.println(getPhoneInfo(writer));
writer.println(throwable.getMessage());
writer.println("===================================================");
throwable.printStackTrace(writer);
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取手机的硬件信息
* @return
*/
private String getPhoneInfo(PrintWriter writer) {
PackageManager packageManager=mContext.getPackageManager();
try {
PackageInfo packageInfo=packageManager.getPackageInfo(mContext.getPackageName(),PackageManager.GET_ACTIVITIES);
writer.println("App Vision:"+packageInfo.versionName);//当前APP的版本号
writer.println("OS Vision:"+ Build.VERSION.RELEASE+"__SDK INT:"+Build.VERSION.SDK_INT);//当前操作系统版本
writer.println("Vendor:"+Build.MANUFACTURER);//手机制造厂商
writer.println("Moudle:"+Build.MODEL);//手机型号
writer.println("CUP ABI:"+Build.CPU_ABI);//手CPU型号
writer.println("===================================================");
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 上传服务器端
*/
private void upload2Server() {
upload2Server();
}
}
application类
package com.tian.bindertest;
import android.app.Application;
import com.tian.bindertest.handlerException.CrashHandler;
/**
* Created by TCX on 2017/7/9.
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler crashHandler=CrashHandler.getsInstance();
crashHandler.init(this);
}
}
activity类
package com.tian.bindertest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
/**
* 初始化页面控件
*/
private void initView() {
findViewById(R.id.btn_crash).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
throw new RuntimeException("这是我TCX抛出的异常!");
}
});
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tian.bindertest.MainActivity">
<Button
android:id="@+id/btn_crash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Crash"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
每天进步一点点,时间会让你成为巨人!加油!