开发过程中或多或少肯定会出现异常问题,有的可以百分百复现,可以很快的定位到问题,但有的只是偶尔出现一次,定位问题会困难很多,可以使用CrashHandlerManager(主要定位会造成崩溃的异常)和ExceptionManager(主要定位不会造成崩溃的异常)来捕获异常,并打印日志文件到本地。
注意:Android 9以上的项目,需要适配才可以打印日志到本地。
以下代码在项目里,项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2
1.先申请权限,关于如何申请权限请查看RxPermissions的使用(简单实用)_ErwinNakajima的博客-CSDN博客
2.写一个获取手机唯一识别码的管理类。
package com.phone.common_library.manager;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.telephony.TelephonyManager;
import androidx.core.app.ActivityCompat;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.MessageDigest;
import java.util.UUID;
public class SystemIdManager {
private static final String TAG = SystemIdManager.class.getSimpleName();
//保存文件的路径
// private static final String CACHE_IMAGE_DIR = "aray/cache/devices";
// private static final String CACHE_IMAGE_DIR = "Android/data/systemId";
private static final String CACHE_IMAGE_DIR = "Android/systemId";
//保存的文件 采用隐藏文件的形式进行保存
private static final String DEVICES_FILE_NAME = ".DEVICES";
/**
* 获取设备唯一标识符
*
* @param context
* @return
*/
public static String getSystemId(Context context) {
//读取保存的在sd卡中的唯一标识符
String systemId = readSystemId(context);
//用于生成最终的唯一标识符
StringBuffer stringBuffer = new StringBuffer();
//判断是否已经生成过,
if (systemId != null && !"".equals(systemId)) {
LogManager.i(TAG, "已经生成过");
return systemId;
}
try {
//获取IMES(也就是常说的SystemId)
systemId = getIMIEStatus(context);
stringBuffer.append(systemId);
} catch (Exception e) {
e.printStackTrace();
}
try {
//获取设备的MACAddress地址 去掉中间相隔的冒号
systemId = getLocalMac(context).replace(":", "");
stringBuffer.append(systemId);
} catch (Exception e) {
e.printStackTrace();
}
//如果以上搜没有获取相应的则自己生成相应的UUID作为相应设备唯一标识符
if (stringBuffer == null || stringBuffer.length() <= 0) {
UUID uuid = UUID.randomUUID();
systemId = uuid.toString().replace("-", "");
stringBuffer.append(systemId);
}
//为了统一格式对设备的唯一标识进行md5加密 最终生成32位字符串
String md5 = getMD5(stringBuffer.toString(), false);
if (stringBuffer.length() > 0) {
//持久化操作, 进行保存到SD卡中
saveSystemId(md5, context);
} else {
}
return md5;
}
/**
* 读取固定的文件中的内容,这里就是读取sd卡中保存的设备唯一标识符
*
* @param context
* @return
*/
public static String readSystemId(Context context) {
File file = getSystemIdDir(context);
if (file.exists()) {
LogManager.i(TAG, "file.exists()");
if (file.length() > 1) {
LogManager.i(TAG, "file.length() > 1");
StringBuffer buffer = new StringBuffer();
try {
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
Reader in = new BufferedReader(isr);
int i;
while ((i = in.read()) > -1) {
buffer.append((char) i);
}
in.close();
return buffer.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
} else {
return null;
}
} else {
LogManager.i(TAG, "xfile.exists()");
return null;
}
}
/**
* 获取手机IMEI(需要“android.permission.READ_PHONE_STATE”权限)
* 需要 READ_PHONE_STATE 权限
*
* @param context
* @return
*/
public static String getIMIEStatus(Context context) {
TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
String iMIEStatus = null;
if (Build.VERSION.SDK_INT >= 23) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return null;
}
iMIEStatus = telephonyManager.getDeviceId();
LogManager.i(TAG, "iMIEStatus*****" + iMIEStatus);
}
return iMIEStatus;
}
/**
* 获取设备MAC 地址 由于 6.0 以后 WifiManager 得到的 MacAddress得到都是 相同的没有意义的内容
* 所以采用以下方法获取Mac地址
*
* @param context
* @return
*/
private static String getLocalMac(Context context) {
// WifiManager wifi = (WifiManager) context
// .getSystemService(Context.WIFI_SERVICE);
// WifiInfo info = wifi.getConnectionInfo();
// return info.getMacAddress();
String macAddress = null;
StringBuffer buf = new StringBuffer();
NetworkInterface networkInterface = null;
try {
networkInterface = NetworkInterface.getByName("eth1");
if (networkInterface == null) {
networkInterface = NetworkInterface.getByName("wlan0");
}
if (networkInterface == null) {
return "";
}
byte[] addr = networkInterface.getHardwareAddress();
for (byte b : addr) {
buf.append(String.format("%02X:", b));
}
if (buf.length() > 0) {
buf.deleteCharAt(buf.length() - 1);
}
macAddress = buf.toString();
} catch (SocketException e) {
e.printStackTrace();
return "";
}
return macAddress;
}
/**
* 保存 内容到 SD卡中, 这里保存的就是 设备唯一标识符
*
* @param str
* @param context
*/
public static void saveSystemId(String str, Context context) {
File file = getSystemIdDir(context);
try {
FileOutputStream fos = new FileOutputStream(file);
Writer out = new OutputStreamWriter(fos, "UTF-8");
out.write(str);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 对挺特定的 内容进行 md5 加密
*
* @param message 加密明文
* @param upperCase 加密以后的字符串是是大写还是小写 true 大写 false 小写
* @return
*/
public static String getMD5(String message, boolean upperCase) {
String md5str = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] input = message.getBytes();
byte[] buff = md.digest(input);
md5str = bytesToHex(buff, upperCase);
} catch (Exception e) {
e.printStackTrace();
}
return md5str;
}
public static String bytesToHex(byte[] bytes, boolean upperCase) {
StringBuffer md5str = new StringBuffer();
int digital;
for (int i = 0; i < bytes.length; i++) {
digital = bytes[i];
if (digital < 0) {
digital += 256;
}
if (digital < 16) {
md5str.append("0");
}
md5str.append(Integer.toHexString(digital));
}
if (upperCase) {
return md5str.toString().toUpperCase();
}
return md5str.toString().toLowerCase();
}
/**
* 统一处理设备唯一标识 保存的文件的地址
*
* @param context
* @return
*/
private static File getSystemIdDir(Context context) {
File file = null;
File dirs = new File(Environment.getExternalStorageDirectory(), CACHE_IMAGE_DIR);
if (!dirs.exists()) {
LogManager.i(TAG, "!dir.exists()");
// dir.mkdirs();
//适配android 9
boolean isCreateDirsSuccess = DocumentManager.mkdirs(context, dirs);
LogManager.i(TAG, "getSystemIdDir isCreateDirsSuccess*****" + isCreateDirsSuccess);
}
file = new File(dirs, DEVICES_FILE_NAME); // 用DEVICES_FILE_NAME给文件命名
if (file.exists()) {
LogManager.i(TAG, "getSystemIdDir file.exists()");
} else {
LogManager.i(TAG, "getSystemIdDir !file.exists()");
// try {
// file.createNewFile();
// } catch (IOException e) {
// e.printStackTrace();
// }
//适配android 9
boolean isCreateFileSuccess = DocumentManager.canWrite(file);
LogManager.i(TAG, "getSystemIdDir isCreateFileSuccess*****" + isCreateFileSuccess);
}
return file;
}
}
3.写一个获取系统数据的管理类。
package com.phone.common_library.manager;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.telephony.TelephonyManager;
import androidx.core.app.ActivityCompat;
import com.blankj.utilcode.util.DeviceUtils;
import java.util.Locale;
import java.util.UUID;
public class SystemManager {
/**
* 获取当前手机系统语言
*
* @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN”
*/
public static String getSystemLanguage() {
return Locale.getDefault().getLanguage();
}
/**
* 获取当前系统上的语言列表(Locale列表)
*
* @return 语言列表
*/
public static Locale[] getSystemLanguageList() {
return Locale.getAvailableLocales();
}
/**
* 获取当前手机系统版本号
*
* @return 系统版本号
*/
public static String getSystemVersion() {
return android.os.Build.VERSION.RELEASE;
}
/**
* 获取手机型号
*
* @return 手机型号
*/
public static String getSystemModel() {
return android.os.Build.MODEL;
}
/**
* 获取手机厂商
*
* @return 手机厂商
*/
public static String getDeviceBrand() {
return android.os.Build.BRAND;
}
/**
* 获取手机IMEI(需要“android.permission.READ_PHONE_STATE”权限)
*
* @return 手机IMEI
*/
public static String getIMEI(Context context) {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Activity.TELEPHONY_SERVICE);
if (telephonyManager != null) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return null;
}
return telephonyManager.getDeviceId();
}
return null;
}
/**
* 获取手机CPU_ABI
*
* @return 手机CPU_ABI
*/
public static String getDeviceCpuAbi() {
return android.os.Build.CPU_ABI;
}
/**
* 获取手機唯一識別码(推薦使用,Android有很多雜牌手機,還有山寨機,这个方法可以統一獲取到)
*
* @return
*/
public static String getSystemId(Context context) {
return SystemIdManager.getSystemId(context);
}
/**
* 获取手機唯一識別码(不推薦使用,Android有很多雜牌手機,還有山寨機,根本不能統一獲取到deviceUuid)
*
* @return
*/
public static String getDeviceUUid() {
String androidId = DeviceUtils.getAndroidID();
UUID deviceUuid = new UUID(androidId.hashCode(), ((long) androidId.hashCode() << 32));
return deviceUuid.toString();
}
}
4.写一个造成App崩溃的异常管理类。
package com.phone.common_library.manager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.util.ArrayMap;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
/**
* 造成App崩潰的異常管理類(在Application裏初始化,只要一有崩潰的異常就會被捕獲)
*/
public class CrashHandlerManager implements Thread.UncaughtExceptionHandler {
public static final String TAG = CrashHandlerManager.class.getSimpleName();
//系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
private static CrashHandlerManager instance;
private Context mContext;
/*使用Properties 来保存设备的信息和错误堆栈信息*/
private Properties mDeviceCrashInfo = new Properties();
//存储设备信息
private Map mDevInfoMap = new ArrayMap<>();
private SimpleDateFormat formatdate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private CrashHandlerManager(Context context) {
mContext = context;
}
/**
* 保证只有一个实例
*
* @param context
* @return
*/
public static CrashHandlerManager getInstance(Context context) {
if (instance == null) {
synchronized (CrashHandlerManager.class) {
if (instance == null) {
instance = new CrashHandlerManager(context);
}
}
}
return instance;
}
public void init() {
//获得默认的handle
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
//重新设置handle 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
//如果開發人員没有处理则让系统默认的异常处理器来处理
if (!handleException(e) && mDefaultHandler != null) {
mDefaultHandler.uncaughtException(t, e);
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
LogManager.i(TAG, "error");
}
//结束程序
ActivityPageManager.getInstance().exitApp2();
}
}
/**
* Throwable 包含了其线程创建时线程执行堆栈的快照
* 收集設備信息和保存異常日誌到文件
*
* @param throwable
* @return
*/
public boolean handleException(Throwable throwable) {
if (throwable == null) {
return false;
}
// final String msg = throwable.getLocalizedMessage();
// new Thread() {
// public void run() {
// Looper.prepare();
// Toast.makeText(mContext, "msg" + msg, Toast.LENGTH_LONG).show();
// Looper.loop();
// }
// }.start();
//收集設備信息
collectDeviceInfo(mContext);
//保存異常日誌到文件
saveCrashInfoToFile(throwable);
//使用HTTP Post發送錯誤報告
sendCrashReportsToServer(mContext);
return true;
}
public void sendPreviousReportsToServer() {
//使用HTTP Post發送錯誤報告
sendCrashReportsToServer(mContext);
}
/**
* 使用HTTP Post發送錯誤報告
*
* @param mContext
*/
private void sendCrashReportsToServer(Context mContext) {
String[] crFiles = getCrashReportFiles(mContext);
if (crFiles != null && crFiles.length > 0) {
TreeSet sortedFiles = new TreeSet();
sortedFiles.addAll(Arrays.asList(crFiles));
for (String fileName : sortedFiles) {
File cr = new File(mContext.getFilesDir(), fileName);
postReport(cr);
//cr.delete();
}
}
}
private void postReport(File cr) {
// TODO 使用HTTP Post 发送错误报告到服务器
}
private static final String CRASH_REPORTER_EXTENSION = ".cr";
private String[] getCrashReportFiles(Context mContext) {
File filesDir = mContext.getFilesDir();
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String filename) {
return filename.endsWith(CRASH_REPORTER_EXTENSION);
}
};
return filesDir.list(filter);
}
/**
* 保存異常日誌到文件
*
* @param throwable
* @return
*/
private String saveCrashInfoToFile(Throwable throwable) {
StringBuffer buffer = new StringBuffer();
for (Map.Entry entry : mDevInfoMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
buffer.append(key + "=" + value + '\n');
}
//可以用其回收在字符串缓冲区中的输出来构造字符串
Writer writer = new StringWriter();
//向文本输出流打印对象的格式化表示形式
PrintWriter printWriter = new PrintWriter(writer);
//将此 throwable 及其追踪输出至标准错误流
throwable.printStackTrace(printWriter);
Throwable cause = throwable.getCause();
while (cause != null) {
//异常链
cause.printStackTrace();
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
buffer.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = formatdate.format(new Date(timestamp));
String fileName = "crash-" + time + "-" + timestamp + ".txt";
// 判断SD卡是否存在,并且是否具有读写权限
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/crash_xy/";// /sdcard/crash/crash-time-timestamp.log
File dirs = new File(path);
if (!dirs.exists()) {
dirs.mkdirs();
}
File file = new File(path, fileName);
if (!file.exists()) {
file.createNewFile();
}
// FileOutputStream trace = new FileOutputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
mDeviceCrashInfo.store(bufferedOutputStream, "");
bufferedOutputStream.flush();
bufferedOutputStream.close();
//output 针对内存来说的 output到file中
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(buffer.toString().getBytes());
bos.flush();
bos.close();
}
return fileName;
} catch (Exception e) {
LogManager.i(TAG, "an error occured while writing file...", e);
}
return null;
}
/**
* 保存內存不足日誌到文件(在即將殺死App之前保存)
*
* @param info
* @return
*/
public String saveTrimMemoryInfoToFile(String info) {
collectDeviceInfo(mContext);
LogManager.i(TAG, "saveTrimMemoryInfoToFile");
StringBuffer buffer = new StringBuffer();
for (Map.Entry entry : mDevInfoMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
buffer.append(key + "=" + value + '\n');
}
buffer.append(info);
try {
long timestamp = System.currentTimeMillis();
String time = formatdate.format(new Date(timestamp));
String fileName = "aCrash-" + time + "-" + timestamp + ".txt";
// 判断SD卡是否存在,并且是否具有读写权限
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/aCrash_xy/";// /sdcard/crash/crash-time-timestamp.log
File dirs = new File(path);
if (!dirs.exists()) {
dirs.mkdirs();
}
File file = new File(path, fileName);
if (!file.exists()) {
file.createNewFile();
}
//output 针对内存来说的 output到file中
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(buffer.toString().getBytes());
bos.flush();
bos.close();
}
return fileName;
} catch (Exception e) {
LogManager.i(TAG, "an error occured while writing file...", e);
}
return null;
}
/**
* 收集設備信息
*
* @param context
*/
private void collectDeviceInfo(Context context) {
try {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
if (packageInfo != null) {
String versionName = packageInfo.versionName == null ? "null" : packageInfo.versionName;
String versionCode = packageInfo.versionCode + "";
mDevInfoMap.put("versionName", versionName);
mDevInfoMap.put("versionCode", versionCode);
}
} catch (PackageManager.NameNotFoundException e) {
LogManager.i(TAG, "NameNotFoundException");
}
// //使用反射 获得Build类的所有类里的变量
// // Class 代表类在运行时的一个映射
// //在Build类中包含各种设备信息,
// // 例如: 系统版本号,设备生产商 等帮助调试程序的有用信息
// // 具体信息请参考后面的截图
// Field[] fields = Build.class.getDeclaredFields();
// for (Field field : fields) {
// try {
// field.setAccessible(true);
// //get方法返回指定对象上此 Field 表示的字段的值
// mDevInfoMap.put(field.getName(), field.get(null).toString());
// LogManager.i(TAG, field.getName() + ":" + field.get(null).toString());
// } catch (Exception e) {
// LogManager.i(TAG, "an error occured when collect crash info", e);
// }
// }
mDevInfoMap.put("手机厂商", SystemManager.getDeviceBrand());
mDevInfoMap.put("手机型号", SystemManager.getSystemModel());
mDevInfoMap.put("手机当前系统语言", SystemManager.getSystemLanguage());
mDevInfoMap.put("手机系统版本号", SystemManager.getSystemVersion());
mDevInfoMap.put("手机CPU架构", SystemManager.getDeviceCpuAbi());
// //手机厂商
// SystemManager.getDeviceBrand();
// //手机型号
// SystemManager.getSystemModel();
// //手机当前系统语言
// SystemManager.getSystemLanguage();
// //手机系统版本号
// SystemManager.getSystemVersion();
// //手机CPU架构
// SystemManager.getDeviceCpuAbi();
// //手機唯一識別碼
// SystemManager.getSystemId(mContext);
}
// /**
// * 使用HTTP服务之前,需要确定网络畅通,我们可以使用下面的方式判断网络是否可用
// *
// * @param context
// * @return
// */
// public static boolean isNetworkAvailable(Context context) {
// ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// NetworkInfo[] info = mgr.getAllNetworkInfo();
// if (info != null) {
// for (int i = 0; i < info.length; i++) {
// if (info[i].getState() == NetworkInfo.State.CONNECTED) {
// return true;
// }
// }
// }
// return false;
// }
}
5.在Application onCrate方法里初始化(只要一有崩溃的异常就会被捕获)。
CrashHandlerManager crashHandlerManager = CrashHandlerManager.getInstance(this);
crashHandlerManager.sendPreviousReportsToServer();
crashHandlerManager.init();
6.写三个类User、User2和User3。
(1)User类。
package com.phone.common_library.bean;
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)User2类。
package com.phone.common_library.bean;
public class User2 extends User {
private String userId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
@Override
public String toString() {
return "User2{" +
"userId='" + userId + '\'' +
'}';
}
}
(3)User3类。
package com.phone.common_library.bean;
public class User3 extends User {
private int userNumber;
public int getUserNumber() {
return userNumber;
}
public void setUserNumber(int userNumber) {
this.userNumber = userNumber;
}
@Override
public String toString() {
return "User3{" +
"userNumber=" + userNumber +
'}';
}
}
7.在Activity中onCreate里写一个会造成崩溃的异常(模拟开发中出现的异常),就会被CrashHandlerManager捕获到,并打印日志文件到本地。
//製造一個不會造成App崩潰的異常(类强制转换异常java.lang.ClassCastException)
User user = new User2();
User3 user3 = (User3) user;
LogManager.i(TAG, user3.toString());
8.写一个不会造成App崩溃的异常(被try(){}catch拋出来的异常)管理类。
package com.phone.common_library.manager;
import android.content.Context;
/**
* 不會造成App崩潰的異常(被try(){}catch拋出來的異常)管理類
*/
public class ExceptionManager {
private static final String TAG = ExceptionManager.class.getSimpleName();
private static ExceptionManager exceptionManager;
private ExceptionManager() {
}
public static ExceptionManager getInstance() {
if (exceptionManager == null) {
synchronized (ExceptionManager.class) {
if (exceptionManager == null) {
exceptionManager = new ExceptionManager();
}
}
}
return exceptionManager;
}
/**
* 打印異常的堆棧日誌
*
* @param throwable
*/
public void throwException(Context context, Throwable throwable) {
// //1.调试打印堆栈而不退出(推薦使用,開發人員用Log就可以把異常日誌打印出來)
// LogManager.i(TAG, Log.getStackTraceString(throwable));
//2.打印异常堆栈(推薦使用,讓系統把異常日誌打印出來)
throwable.printStackTrace();
// //3.获取当前线程的堆栈(不推薦使用,打印的不是很詳細,報錯具體哪行沒打印出來,故不要使用這種方法)
// for (StackTraceElement i : Thread.currentThread().getStackTrace()) {
// LogManager.i(TAG, i.toString());
// }
// //4.打印异常堆栈(不推薦使用,打印的不是很詳細,報錯具體哪行沒打印出來,故不要使用這種方法)
// throwable.fillInStackTrace();
// LogManager.i(TAG, "stackTrace", throwable);
CrashHandlerManager crashHandlerManager = CrashHandlerManager.getInstance(context.getApplicationContext());
//收集設備信息和保存異常日誌到文件
crashHandlerManager.handleException(throwable);
}
}
9.在Activity中onCreate里写一个不会造成崩溃的异常(模拟开发中出现的异常,这种try(){}catch拋出來的异常,就不会造成App崩溃),就会被ExceptionManager捕获到,并打印日志文件到本地。
try {
//製造一個不會造成App崩潰的異常(类强制转换异常java.lang.ClassCastException)
User user = new User2();
User3 user3 = (User3) user;
LogManager.i(TAG, user3.toString());
} catch (Exception e) {
ExceptionManager.getInstance().throwException(rxAppCompatActivity, e);
}
如对此有疑问,请联系qq1164688204。
推荐Android开源项目
项目功能介绍:RxJava2和Retrofit2项目,添加自动管理token功能,添加RxJava2生命周期管理,使用App架构设计是MVP模式和MVVM模式,同时使用组件化,部分代码使用Kotlin,此项目持续维护中。
项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2