Android 收集奔溃信息
1. ExceptionCrashHandler implements Thread.UncaughtExceptionHandler 继承
// 当App 奔溃的时候回调用这个方法
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "到拦截闪退信息");
// 手机信息+应用信息+ 捕获异常写入 sd 卡
// 捕获到异常了,写入到本地,让系统默认处理
String crashFileName = saveInfoToSD(ex);
Log.e(TAG, "fileName --> " + crashFileName);
// 缓存崩溃日志文件, 下次启动App把奔溃信息上次到服务器
cacheCrashFile(crashFileName);
// 系统默认处理
// mDefaultHandler.uncaughtException(thread, ex);
// 让系统默认处理
// 奔溃,关闭当前activity返回上一个activity
mDefaultHandler.uncaughtException(thread,ex);
}
package com.example.andfixmodel;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
public class ExceptionCrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = "ExceptionCrashHandler";
// 单例设计模式
private static ExceptionCrashHandler mInstance;
// 留下原来的,便于开发的时候调试
private Thread.UncaughtExceptionHandler mDefaultHandler;
// 上下文 获取版本信息和手机信息
private Context mContext;
public static ExceptionCrashHandler getInstance() {
if (mInstance == null) {
synchronized (ExceptionCrashHandler.class) {
if (mInstance == null) {
mInstance = new ExceptionCrashHandler();
}
}
}
return mInstance;
}
private ExceptionCrashHandler() {
}
public void init(Context context) {
// 设置全局的异常类为本类,通过本类来捕获异常
Thread.currentThread().setUncaughtExceptionHandler(this);
// 获取系统默认的UncaughtException处理器,系统默认的处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
this.mContext = context;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "到拦截闪退信息");
// 捕获到异常了,写入到本地,让系统默认处理
String crashFileName = saveInfoToSD(ex);
Log.e(TAG, "fileName --> " + crashFileName);
// 3. 缓存崩溃日志文件
cacheCrashFile(crashFileName);
// 系统默认处理
// mDefaultHandler.uncaughtException(thread, ex);
// 让系统默认处理
// 奔溃,关闭当前activity返回上一个activity
mDefaultHandler.uncaughtException(thread,ex);
}
private void cacheCrashFile(String fileName) {
SharedPreferences sp = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE);
sp.edit().putString("CRASH_FILE_NAME", fileName).commit();
}
public File getCrashFile() {
String crashFileName = mContext.getSharedPreferences("crash",
Context.MODE_PRIVATE).getString("CRASH_FILE_NAME", "");
return new File(crashFileName);
}
private String saveInfoToSD(Throwable ex) {
String fileName = null;
StringBuffer sb = new StringBuffer();
// 手机信息+应用信息
for (Map.Entry<String, String> entry : obtainSimpleInfo(mContext)
.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key).append(" = ").append(value).append("\n");
}
// 奔溃信息
sb.append(obtainExceptionInfo(ex));
// 这里是手机的应用目录
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
// // /data/data/packageName/file 目录
File dir = new File(mContext.getFilesDir() + File.separator + "crash"
+ File.separator);
// 先删除之前的异常信息
if (dir.exists()) {
deleteDir(dir);
}
// 再从新创建文件夹
if (!dir.exists()) {
dir.mkdir();
}
try {
fileName = dir.toString()
+ File.separator
+ getAssignTime("yyyy_MM_dd_HH_mm") + ".txt";
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(sb.toString().getBytes());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return fileName;
}
private String getAssignTime(String dateFormatStr) {
DateFormat dataFormat = new SimpleDateFormat(dateFormatStr);
long currentTime = System.currentTimeMillis();
return dataFormat.format(currentTime);
}
private HashMap<String, String> obtainSimpleInfo(Context context) {
HashMap<String, String> map = new HashMap<>();
PackageManager mPackageManager = context.getPackageManager();
PackageInfo mPackageInfo = null;
try {
mPackageInfo = mPackageManager.getPackageInfo(
context.getPackageName(), PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
map.put("versionName", mPackageInfo.versionName);
map.put("versionCode", "" + mPackageInfo.versionCode);
map.put("MODEL", "" + Build.MODEL);
map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
map.put("PRODUCT", "" + Build.PRODUCT);
map.put("MOBLE_INFO", getMobileInfo());
return map;
}
public static String getMobileInfo() {
StringBuffer sb = new StringBuffer();
try {
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
String value = field.get(null).toString();
sb.append(name + "=" + value);
sb.append("\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
private String obtainExceptionInfo(Throwable throwable) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
throwable.printStackTrace(printWriter);
printWriter.close();
return stringWriter.toString();
}
private boolean deleteDir(File dir) {
if (dir.isDirectory()) {
File[] children = dir.listFiles();
// 递归删除目录中的子目录下
for (File child: children){
child.delete();
}
}
// 目录此时为空,可以删除
return true;
}
}
2. MyApplication初始化 ExceptionCrashHandler
public class MyApplication extends Application {
public static PatchManager patchManager;
@Override
public void onCreate() {
super.onCreate();
// 设置全异常捕捉类
ExceptionCrashHandler.getInstance().init(this);
}
}
3、 App再次启动,读取奔溃信息上次到服务器中
public class MainActivity extends Activity {
private final int SDK_PERMISSION_REQUEST = 127;
String Tag= "denganzhi";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> permissions = new ArrayList<String>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 定位精确位置
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
// 用户点击拒绝,每一次申请都会回调
if (permissions.size() > 0) {
requestPermissions(permissions.toArray(new String[permissions.size()]), SDK_PERMISSION_REQUEST);
}
}
// 获取异常上次奔溃信息
File crashFile = ExceptionCrashHandler.getInstance().getCrashFile();
if(crashFile.exists()){
//文件存在上传服务器
String resultText = readToString(crashFile.getAbsolutePath());
Log.e("denganzhi1","奔溃信息是:"+resultText);
}
}
// 读取奔溃信息
public String readToString(String fileName) {
String encoding = "UTF-8";
File file = new File(fileName);
Long filelength = file.length();
byte[] filecontent = new byte[filelength.intValue()];
try {
FileInputStream in = new FileInputStream(file);
in.read(filecontent);
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
return new String(filecontent, encoding);
} catch (UnsupportedEncodingException e) {
System.err.println("The OS does not support " + encoding);
e.printStackTrace();
return null;
}
}
@TargetApi(23)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// TODO Auto-generated method stub
if (requestCode == SDK_PERMISSION_REQUEST)
{
if (grantResults[0] == PackageManager.PERMISSION_GRANTED )
{
// callPhone();
Toast.makeText(MainActivity.this,"权限通过:"+grantResults.length,Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this,"用户点击拒绝:"+grantResults.length,Toast.LENGTH_SHORT).show();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}