废话不多说,源码献上!
package com.anrongbc.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
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.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.anrongbc.login.LoginActivity;
import java.io.File;
import java.io.FilenameFilter;
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.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 全局捕获异常
* 当程序发生Uncaught异常的时候,有该类来接管程序,并记录错误日志
* 文件地址:内置sd卡路径下“一键核Error" + 日期时间 + ".log”
*/
@SuppressLint("SimpleDateFormat")
public class CrashHandler_Ma {
public static final String DEFAULT_FORMAT_DATE = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
//分隔符.
public final static String FILE_EXTENSION_SEPARATOR = ".";
public static String TAG = "MyCrash";
// 系统默认的UncaughtException处理类
private UncaughtExceptionHandler mDefaultHandler;
private static CrashHandler_Ma instance = new CrashHandler_Ma();
private Context mContext;
// 用来存储设备信息和异常信息
private Map infos = new HashMap();
// 用于格式化日期,作为日志文件名的一部分
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
/**
* 保证只有一个CrashHandler实例
*/
private CrashHandler_Ma() {
}
/**
* 获取CrashHandler实例 ,单例模式
*/
public static CrashHandler_Ma getInstance() {
return instance;
}
public void init(Context context) {
mContext = context;
//文件保存天数
autoClear(10);
// 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "uncaughtException: ", ex);
try {
// 使用Toast来显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "很抱歉,程序出现异常,即将重启.", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
// 收集设备参数信息
collectDeviceInfo(mContext);
// 保存日志文件
saveCrashInfoFile(ex);
// 重启应用(按需要添加是否重启应用)
SystemClock.sleep(1000);
restartApp();
} catch (Exception e) {
SystemClock.sleep(1000);
// 退出程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
});
}
/**
* 重启AppMyActivityManager
*/
public void restartApp() {
Intent intent = new Intent(mContext, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
MyActivityManager.finishAllActivity();
//结束进程之前可以把你程序的注销或者退出代码放在这段代码之前
android.os.Process.killProcess(android.os.Process.myPid());
}
/**
* 收集设备参数信息
*
* @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 + "";
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
} catch (Exception e) {
Log.e(TAG, "an error occured when collect crash info", e);
}
}
}
/**
* 保存错误信息到文件中
*
* @return 返回文件名称, 便于将文件传送到服务器
*/
private String saveCrashInfoFile(Throwable ex) {
StringBuffer sb = new StringBuffer();
try {
SimpleDateFormat sDateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
String date = sDateFormat.format(new Date());
sb.append("\r\n" + date + "\n");
for (Map.Entry entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
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.flush();
printWriter.close();
String result = writer.toString();
sb.append(result);
String fileName = writeFile(sb.toString());
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
sb.append("an error occured while writing file...\r\n");
writeFile(sb.toString());
}
return null;
}
private String writeFile(String sb) {
String time = formatter.format(new Date());
String fileName = "卡扣核查Error" + time + ".log";
FileUtils.saveFile(sb, fileName, true);
return fileName;
}
public static String getGlobalpath() {
return Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "crash" + File.separator;
}
public static void setTag(String tag) {
TAG = tag;
}
/**
* 文件删除
*
* @param autoClearDay 文件保存天数
*/
public void autoClear(final int autoClearDay) {
delete(getGlobalpath(), new FilenameFilter() {
@Override
public boolean accept(File file, String filename) {
String s = getFileNameWithoutExtension(filename);
int day = autoClearDay < 0 ? autoClearDay : -1 * autoClearDay;
String date = "crash-" + getOtherDay(day);
return date.compareTo(s) >= 0;
}
});
}
/**
* 获得不带扩展名的文件名称
*
* @param filePath 文件路径
* @return
*/
public String getFileNameWithoutExtension(String filePath) {
if (TextUtils.isEmpty(filePath)) {
return filePath;
}
int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
int filePosi = filePath.lastIndexOf(File.separator);
if (filePosi == -1) {
return (extenPosi == -1 ? filePath : filePath.substring(0,
extenPosi));
}
if (extenPosi == -1) {
return filePath.substring(filePosi + 1);
}
return (filePosi < extenPosi ? filePath.substring(filePosi + 1,
extenPosi) : filePath.substring(filePosi + 1));
}
/**
* 判断SD卡是否可用
*
* @return SD卡可用返回true
*/
public boolean hasSdcard() {
String status = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(status);
}
/**
* 删除指定目录中特定的文件
*
* @param dir
* @param filter
*/
public static void delete(String dir, FilenameFilter filter) {
if (TextUtils.isEmpty(dir)) {
return;
}
File file = new File(dir);
if (!file.exists()) {
return;
}
if (file.isFile()) {
file.delete();
}
if (!file.isDirectory()) {
return;
}
File[] lists = null;
if (filter != null) {
lists = file.listFiles(filter);
} else {
lists = file.listFiles();
}
if (lists == null) {
return;
}
for (File f : lists) {
if (f.isFile()) {
f.delete();
}
}
}
/**
* 获得几天之前或者几天之后的日期
*
* @param diff 差值:正的往后推,负的往前推
* @return
*/
public String getOtherDay(int diff) {
Calendar mCalendar = Calendar.getInstance();
mCalendar.add(Calendar.DATE, diff);
return getDateFormat(mCalendar.getTime());
}
/**
* 将date转成yyyy-MM-dd字符串
*
* @param date Date对象
* @return yyyy-MM-dd
*/
public String getDateFormat(Date date) {
return dateSimpleFormat(date, defaultDateFormat.get());
}
/**
* 将date转成字符串
*
* @param date Date
* @param format SimpleDateFormat
*
* 注: SimpleDateFormat为空时,采用默认的yyyy-MM-dd HH:mm:ss格式
* @return yyyy-MM-dd HH:mm:ss
*/
public static String dateSimpleFormat(Date date, SimpleDateFormat format) {
if (format == null) {
format = defaultDateTimeFormat.get();
}
return (date == null ? "" : format.format(date));
}
/**
* yyyy-MM-dd HH:mm:ss格式
*/
public static final ThreadLocal defaultDateTimeFormat = new ThreadLocal() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
}
};
/**
* yyyy-MM-dd格式
*/
public static final ThreadLocal defaultDateFormat = new ThreadLocal() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(DEFAULT_FORMAT_DATE);
}
};
}
到这里就完事了,别忘了在“Application”里面onCreate进行注册;
注册代码:CrashHandler_Ma.getInstance().init(this);
不过有些人反馈说找不到FileUtils工具类,那个只是本地写入文件的一个工具类,自己封装的,那我贴一下好了:
package com.anrongtec.zcpt.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* 文件工具类
*
* @author mazhanzhu 2018年8月19日
*/
public class FileUtils {
/**
* 分隔符.
*/
public final static String FILE_EXTENSION_SEPARATOR = ".";
/**
* 判断手机是否安装某个应用
*
* @param context
* @param appPackageName 应用包名
* @return true:安装,false:未安装
*/
public static boolean isApplicationAvilible(Context context, String appPackageName) {
try {
// 获取packagemanager
PackageManager packageManager = context.getPackageManager();
// 获取所有已安装程序的包信息
List pinfo = packageManager.getInstalledPackages(0);
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if (appPackageName.equals(pn)) {
return true;
}
}
}
return false;
} catch (Exception e) {
//出现异常,直接打开应用,避免停留在这个页面不动
return true;
}
}
/**
* 格式转换文件大小
*
* @param context
* @param fileSize
* @return
*/
@SuppressLint("NewApi")
public static String formatFileSize(Context context, long fileSize) {
return Formatter.formatFileSize(context, fileSize);
}
/**
* 判断该文件是否存在
*
* @return
*/
public static boolean isExist(String path) {
try {
File f = new File(path);
if (f.exists()) {
return true;
}
} catch (Exception e) {
return false;
}
return false;
}
/**
* 保存异常信息到文件
*
* @param data
* @param file_name
* @param isAppend 是否是追加, true为追加。 false为覆盖
*/
public static void saveFile(String data, String file_name, boolean isAppend) {
//内置sd卡路径
File sdPath = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
if (!sdPath.exists()) {
sdPath.mkdirs();
}
Log.e("fd", "saveFile: " + sdPath.toString());
File file = new File(sdPath, file_name);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file, isAppend);
fos.write(data.getBytes("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void saveFile(String data, String file_name) {
saveFile(data, file_name, false);
}
public static void saveLog(String data, String file_name) {
String time = new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(new Date());
saveFile("\n" + time + "\n" + data, file_name, true);
}
/**
* 根据路径删除文件
*/
public static boolean deleteFile(File file) {
if (file == null) {
throw new NullPointerException("file is null");
}
if (!file.exists()) {
return true;
}
if (file.isFile()) {
return file.delete();
}
if (!file.isDirectory()) {
return false;
}
File[] files = file.listFiles();
if (files == null) {
return true;
}
for (File f : files) {
if (f.isFile()) {
f.delete();
} else if (f.isDirectory()) {
deleteFile(f.getAbsolutePath());
}
}
return file.delete();
}
/**
* 判断SD卡是否可用
*
* @return SD卡可用返回true
*/
public static boolean hasSdcard() {
String status = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(status);
}
/**
* 删除指定文件或指定目录内的所有文件
*
* @param path 文件或目录的绝对路径
* @return 路径为空或空白字符串,返回true;文件不存在,返回true;文件删除返回true;
* 文件删除异常返回false
*/
public static boolean deleteFile(String path) {
if (TextUtils.isEmpty(path)) {
return true;
}
return deleteFile(new File(path));
}
/**
* 删除指定目录中特定的文件
*
* @param dir
* @param filter
*/
public static void delete(String dir, FilenameFilter filter) {
if (TextUtils.isEmpty(dir)) {
return;
}
File file = new File(dir);
if (!file.exists()) {
return;
}
if (file.isFile()) {
file.delete();
}
if (!file.isDirectory()) {
return;
}
File[] lists = null;
if (filter != null) {
lists = file.listFiles(filter);
} else {
lists = file.listFiles();
}
if (lists == null) {
return;
}
for (File f : lists) {
if (f.isFile()) {
f.delete();
}
}
}
/**
* 获得不带扩展名的文件名称
*
* @param filePath 文件路径
* @return
*/
public static String getFileNameWithoutExtension(String filePath) {
if (TextUtils.isEmpty(filePath)) {
return filePath;
}
int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
int filePosi = filePath.lastIndexOf(File.separator);
if (filePosi == -1) {
return (extenPosi == -1 ? filePath : filePath.substring(0,
extenPosi));
}
if (extenPosi == -1) {
return filePath.substring(filePosi + 1);
}
return (filePosi < extenPosi ? filePath.substring(filePosi + 1,
extenPosi) : filePath.substring(filePosi + 1));
}
}
当程序发生Uncaught异常的时候,记录错误日志;文件地址:内置sd卡路径下“crash-" + 日期时间 + ".log”