--------------------- CrashHandler ---------------------
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static CrashHandler mInstance;
private Thread.UncaughtExceptionHandler mDefautHandler;
private static final String TAG = "CrashHandlerTag";
private CrashHandler() {
}
public static CrashHandler getInstance() {
if (mInstance == null) {
synchronized (CrashHandler.class) {
if (mInstance == null) {
mInstance = new CrashHandler();
}
}
}
return mInstance;
}
public void init() {
mDefautHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (ex != null) {
// 收集设备信息、版本信息、异常信息
String info = collectDeviceInfo(BaseApplication.getInstance().getApplicationContext(), ex);
// 本地存储
saveInfo(info);
Log.d(TAG, "已捕获到异常 ");
//不进行延时处理的话kill不能执行完成
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 异常已经处理,结束进程
Process.killProcess(Process.myPid());
System.exit(1);
}
}, 100);
} else {
Log.d(TAG, "没有捕获异常 ");
//没有处理还交给系统默认的处理器
if (mDefautHandler != null) {
mDefautHandler.uncaughtException(thread, ex);
}
}
}
/**
* 收集设备信息
*
* @param c
* @param ex
*/
private String collectDeviceInfo(Context c, Throwable ex) {
Map infos = new HashMap<>();
//收集版本信息
try {
PackageManager pm = c.getPackageManager();
PackageInfo pi = pm.getPackageInfo(c.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionCode = pi.versionCode + "";
String versionName = TextUtils.isEmpty(pi.versionName) ? "没有版本名称" : pi.versionName;
infos.put("versionCode", versionCode);
infos.put("versionName", versionName);
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
//收集设备信息
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
} catch (Exception e) {
}
}
//收集异常信息
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();
// 转化为字符串
StringBuffer sb = new StringBuffer();
for (Map.Entry entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
sb.append(result);
return sb.toString();
}
/**
* 保存异常信息到本地
* @param infos
*/
private void saveInfo(String infos) {
Log.d(TAG, "输出log日志: " + infos);
// 把采集到的信息写入到本地文件
LogLocalUtils.writeLogToFile(infos);
}
}
--------------------- LogLocalUtils---------------------
public class LogLocalUtils {
private static final String TAG = "LogLocalUtils";
private static String basePath = Environment.getExternalStorageDirectory().getPath()
+ "/Android/data/com.example.app/log/";
/**
* 开启全局错误日志捕获
*/
public static void openErrorCatch() {
CrashHandler.getInstance().init();
}
/**
* 记录用户在什么时间启动的App
* 登录成功进入菜单界面记录一次
*/
public static void startAppRecord() {
writeLogToFile("----------> 登录成功,进入到菜单界面了... <----------");
}
/**
* 保存日志信息到log.txt文件中
* 每次调用往log.txt文件中添加信息
*
* @param logMessage 日志内容
*/
public static void writeLogToFile(String logMessage) {
try {
//新建多级目录
File sdCardDir = new File(basePath);
if (!sdCardDir.exists() && !sdCardDir.mkdirs()) {
try {
sdCardDir.createNewFile();
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "writeLogToFile-sdCardDir.createNewFile():" + e.getMessage());
}
}
//一个月创建一次日志文件
String yearMonth = new SimpleDateFormat("yyyyMM").format(new Date());
String fileName = "log-" + yearMonth + ".txt";
String logFilePath = basePath + fileName;
FileOutputStream outputStream = new FileOutputStream(new File(logFilePath), true);
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date());
String message =
"<---------------------------start---------------------------->"
+ "\n"
+ "\n"
+ time + "\n"
+ "日志信息:" + logMessage + "\n"
+ "\n"
+ "<---------------------------end---------------------------->"
+ "\n"
+ "\n"
+ "\n"
+ "\n";
outputStream.write(message.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "writeLogToFile-error: " + e.getMessage());
}
}
/**
* 保存异常信息文件到sdcard中
* 每次调用创建新文件
*
* @param errorLog 日志内容
*/
public static void writeLogToSdCard(String errorLog) {
String sdPath = basePath + "/error_log";
//新建多级目录
File sdCardDir = new File(sdPath);
if (!sdCardDir.exists() && !sdCardDir.mkdirs()) {
try {
sdCardDir.createNewFile();
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "sdCardDir.createNewFile()-error:" + e.getMessage());
}
}
try {
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS")
.format(new Date());
String fileName = time + ".txt";
//新建文件
File saveFile = new File(sdCardDir, fileName);
if (!saveFile.exists()) {
saveFile.createNewFile();
}
final FileOutputStream outStream = new FileOutputStream(saveFile);
if (!TextUtils.isEmpty(errorLog)) {
String message =
"<---------------------------start---------------------------->"
+ "\n"
+ "\n"
+ time + "\n"
+ "日志信息:" + errorLog + "\n"
+ "\n"
+ "<---------------------------end---------------------------->"
+ "\n";
outStream.write(message.getBytes());
outStream.close();
}
Log.d(TAG, "日志输出成功! ");
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "流写入错误:" + e.toString());
}
}
}
启动全局日志捕获的事件(出现异常崩溃后,日志会输出到sdcard中):
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
//开启全局错误日志捕获
LogLocalUtils.openErrorCatch();
}
}
记录App启动时间的日志(看个人情况,也可以在Application中记录):
public class HomeActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
//登录成功进入Home界面后,记录一条App启动的日志
LogLocalUtils.startAppRecord();
}
}