Androidapp全局异常统⼀处理
异常处理需求
Android app 出现 crash 时,会出现 “程序异常退出” 的提⽰并关闭,体验不好,另外主要是⽆法知道哪⾥出现的崩溃,需要知道哪⾥造成的异常,就需要⼀个全局抓取异常的处理⽅式,可以把异常保存到⼿机或者上传到指定的服务器上,这样有利于 bug 的解决。通过的⽂章发现了⼀个全局处理该⽅式的接⼝UncaughtExceptionHandler。
接⼝UncaughtExceptionHandler
类位于:java.lang.Thread.UncaughtExceptionHandler
接⼝源码:
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
主要是通过实现接⼝的uncaughtException(Thread t, Throwable e)⽅法,实现 crash 的捕获和保存到 sd 卡,然后联⽹的情况下再上传到服务器。
具体实现
1. 创建⼀个类,实现接⼝UncaughtExceptionHandler
public class CrashHandler implements UncaughtExceptionHandler
2. 实现接⼝的⽅法uncaughtException(Thread t, Throwable e),异常的处理就在这⾥,⽐如保存异常信息到 SD 卡,⾃定义错误弹出信息,⾃动
退出。
@Override
public void uncaughtException(Thread t, Throwable e) {
//收集错误信息,保存到 sd 卡上
errorInfo2SD();
//弹出⾃定义的错误提醒
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "UnCrashException", Toast.LENGTH_SHORT).show();
Looper.loop();
}
});
//杀掉进程,退出应⽤
Process.Pid());
}
3. CrashHandler采⽤单例模式。
public class CrashHandler implements UncaughtExceptionHandler {
private static CrashHandler mInstance;
private CrashHandler() {
}
// 单例模式-懒汉
public static CrashHandler getInstance() {
if (mInstance == null) {
synchronized (CrashHandler.class) {
if (mInstance == null) {
mInstance = new CrashHandler();
}
}
}
return mInstance;
}
4. 定义⼀个⽅法,⽤于把当前应⽤注册到系统的异常处理中,让系统知道由⾃定义的异常捕获器处理。
public void register(Context context) {
mContext = context;
Thread.setDefaultUncaughtExceptionHandler(this);
}
5. 在 Application 中注册。
public class CrashHandlerApplication extends Application {
@Override
public void onCreate() {
}
}
6. 收集错误信息并保存到 SD 卡上。
//⽤于存储设备信息
private Map<String, String> mInfo = new HashMap<>();
//格式化时间,作为Log⽂件名
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
private void errorInfo2SD(Throwable e) {
PackageManager pm = PackageManager();
try {
PackageInfo info = pm.PackageName(), PackageManager.GET_ACTIVITIES);
// 获取版本信息
if (info != null) {
String versionName = TextUtils.isEmpty(info.versionName) ? "未设置版本名称" : info.versionName;
String versionCode = info.versionCode + "";
mInfo.put("versionName", versionName);
mInfo.put("versionCode", versionCode);
}
// 获取设备信息
Field[] fields = Fields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
field.setAccessible(true);
mInfo.Name(), (null).toString());
unknown怎么处理}
}
// 存储信息到 sd 卡指定⽬录
saveErrorInfo(e);
} catch (NameNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void saveErrorInfo(Throwable e) {
StringBuffer stringBuffer = new StringBuffer();
for (Map.Entry<String, String> entry : Set()) {
String keyName = Key();
String value = Value();
stringBuffer.append(keyName + "=" + value + "\n");
}
stringBuffer.append("\n-----Crash Log Begin-----\n");
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
e.printStackTrace(writer);
Throwable cause = e.getCause();
while (cause != null) {
cause.printStackTrace(writer);
cause = e.getCause();
}
writer.close();
String string = String();
stringBuffer.append(string);
stringBuffer.append("\n-----Crash Log End-----");
String format = dateFormat.format(new Date());
String fileName = "crash-" + format + ".log";
if (ExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = FilesDir() + File.separator + "crash";
File dir = new File(path);
if (!ists()) {
dir.mkdirs();
FileOutputStream fou = null;
try {
fou = new FileOutputStream(new File(path, fileName));
fou.String().getBytes());
fou.flush();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if (fou != null) {
fou.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
7. 以上完成了异常的捕获并保存到 sd 卡上,等待 app 再次启动的时候,上传异常信息到服务器上。
8. 保存到 sd 卡上的位置
9. SD 卡中拿到的 Log 信息
SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@58ff504
versionCode=1
BOARD=msm8939
BOOTLOADER=3.19.0.0000
TYPE=user
ID=MMB29M
TIME=1461124295000
BRAND=htc
SERIAL=HC4AZYC00984
HARDWARE=qcom
SUPPORTED_ABIS=[Ljava.lang.String;@abaed
CPU_ABI=arm64-v8a
RADIO=unknown
IS_DEBUGGABLE=false
MANUFACTURER=HTC
SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@c11fd17
TAGS=release-keys
CPU_ABI2=
UNKNOWN=unknown
USER=buildteam
FINGERPRINT=htc/htccn_chs_2/htc_a51dtul:6.0.1/MMB29M/738098.4:user/release-keys
HOST=ABM110
PRODUCT=htccn_chs_2
versionName=1.0
DISPLAY=MMB29M release-keys
MODEL=HTC D820u
DEVICE=htc_a51dtul
-----Crash Log Begin-----
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.CharSequence android.Text()' on a null object reference  at ashhandler.Click(MainActivity.java:26)
at android.view.View.performClick(View.java:5232)
at android.view.View$PerformClick.run(View.java:21289)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:168)
at android.app.ActivityThread.main(ActivityThread.java:5885)
at flect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
-----Crash Log End-----
10. 通过以上 log ,可以看到因为NullPointerException造成的退出。原因是MainActivity.java的第26⾏造成的。
11. ⼯程的MainActivity类的源码,可以看到获取tv2的时候因为没有实例化,造成的空指针
public class MainActivity extends AppCompatActivity {
private TextView tv1;
private TextView tv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(R.id.tv1);
tv1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, Text().toString(), Toast.LENGTH_SHORT).show();
}
});
}
}
12.

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。