相信在座的各位同学都遇到过程序崩溃的问题,在众多代码中,排查bug是让众多开发者比较头疼的一件事。特别是一些同学刚接手别人的项目时,如果出现bug,简直是一团乱麻,无从下手,加上市面上的机型众多,Android版本不同,出现的问题也不同,所以在下推荐腾讯旗下的bugly,一款可以轻松跟踪bug的在线平台。
首先你需要申请一个bugly账号,如果项目需要上架,建议使用公司的账号注册。
我们先来配置文件到项目中去吧!由于大部分项目都需要运用其它的sdk,所以推荐自定义配置(官方也是这样的哟)
打开app下面的build.gradel
在defaultConfig代码块中配置ndk
ndk {
// 设置支持的SO库架构
abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
在dependencies代码块中引入bugly版本
//其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9
implementation 'com.tencent.bugly:crashreport:2.1.9'
//其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0
implementation 'com.tencent.bugly:nativecrashreport:3.0'
点击sync now完成配置,当然也可以进行手动配置,详情请查看官方文档https://bugly.qq.com/docs/user-guide/instruction-manual-android/?v=20200622202242
现在我们开始写代码
首先定义一个全局配置的抓包工具类
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.Looper;
import android.text.TextUtils;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static CrashHandler sInstance = null;
private Thread.UncaughtExceptionHandler mDefaultHandler;
private Context mContext;
// 保存手机信息和异常信息
private Map<String, String> mMessage = new HashMap<>();
public static CrashHandler getInstance() {
if (sInstance == null) {
synchronized (CrashHandler.class) {
if (sInstance == null) {
synchronized (CrashHandler.class) {
sInstance = new CrashHandler();
}
}
}
}
return sInstance;
}
private CrashHandler() {
}
/**
* 初始化默认异常捕获
*
* @param context context
*/
public void init(Context context) {
mContext = context;
// 获取默认异常处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 将此类设为默认异常处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
if (!handleException(e)) {
// 未经过人为处理,则调用系统默认处理异常,弹出系统强制关闭的对话框
if (mDefaultHandler != null) {
mDefaultHandler.uncaughtException(t, e);
}
} else {
// 已经人为处理,系统自己退出
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.exit(1);
}
}
/**
* 是否人为捕获异常
*
* @param e Throwable
* @return true:已处理 false:未处理
*/
private boolean handleException(Throwable e) {
if (e == null) {// 异常是否为空
return false;
}
new Thread() {// 在主线程中弹出提示
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "捕获到异常", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
collectErrorMessages();
saveErrorMessages(e);
return false;
}
/**
* 1.收集错误信息
*/
private void collectErrorMessages() {
PackageManager pm = mContext.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = TextUtils.isEmpty(pi.versionName) ? "null" : pi.versionName;
String versionCode = "" + pi.versionCode;
mMessage.put("versionName", versionName);
mMessage.put("versionCode", versionCode);
}
// 通过反射拿到错误信息
Field[] fields = Build.class.getFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
field.setAccessible(true);
try {
mMessage.put(field.getName(), field.get(null).toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
/**
* 2.保存错误信息
*
* @param e Throwable
*/
private void saveErrorMessages(Throwable e) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : mMessage.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key).append("=").append(value).append("\n");
}
Writer writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
e.printStackTrace(pw);
pw.close();
String result = writer.toString();
sb.append(result);
// String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.CHINA).format(new Date());
String fileName = "crash-"+System.currentTimeMillis()+"-"+".log";
// 有无SD卡
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getPath()+"/crash/";
File dir = new File(path);
if (!dir.exists()) dir.mkdirs();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(path + fileName);
fos.write(sb.toString().getBytes());
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
}
自定义一个Application类,这个类可以用于各种SDK的初始化,可以大大减少代码的冗余。
import android.app.Application;
import com.tencent.bugly.crashreport.CrashReport;
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().init(this);
CrashReport.initCrashReport(getApplicationContext(), "自己申请的appID", false);
}
}
然后我们需要在AndroidManfest.xml中申请相关权限
<!-- App需要上传到google play store,需要将READ_PHONE_STATE权限屏蔽掉或者移除,否则可能会被下架 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
在application标签中我们需要添加name属性值,主要是把刚才自己写的application给加载到全局
android:name=".MyApp"
好了,现在就可以运行自己的apk了,当你程序出现崩溃时,就可以在bugly平台看见崩溃日志了哟!!!