在我们开发的过程中,再厉害的程序员也无法保证写的代码没有错误,而这里面最严重的错误,对于android开发来说,毫无疑问就是app闪退了.特别是在开发第三方SDK的时候,假设因为SDK里面报的问题,导致对方App崩溃了,这对SDK而言的打击是非常严重的,有的时候我们甚至希望SDK即使无法很好的工作,也不要引发对接方App的崩溃.
下面我就来介绍两种,可以根据代码判断,只会代码所在线程死掉,但不会引发崩溃的方法:
- hook Thread.UncaughtExceptionHandler 的方式
- 代理 Thread.UncaughtExceptionHandler 的方式
hook Thread.UncaughtExceptionHandler 的方式
第一种方式就是,我们在程序启动的时候,先使用Thread.getDefaultUncaughtExceptionHandler();
拿到系统的错误处理Handler对象,再次之上,我们自己创建一个 Thread.UncaughtExceptionHandler 的继承类,包裹住系统的错误处理Handler对象之后,把我们自己创建的Thread.UncaughtExceptionHandler 传递回去.
系统的Thread.UncaughtExceptionHandler的实现类是com.android.internal.os.RuntimeInit$UncaughtHandler
hook代码如下:
public void hook() {
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if (ueh == null) {
mSystemUeh = null;
mOtherUeh = null;
Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
return;
}
String androidUeh = "com.android.internal.os.RuntimeInit$UncaughtHandler";
String selfUeh = getClass().getName();
String currUeh = ueh.getClass().getName();
//系统的
if (androidUeh.equals(currUeh)) {
mSystemUeh = ueh;
mOtherUeh = null;
Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
} else if (selfUeh.equals(currUeh)) {
//自己人 不处理
} else {
// bugly的 -> currUeh.contains("com.tencent.bugly.crashreport.crash")
mSystemUeh = null;
mOtherUeh = ueh;
Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
}
return;
}
其中,CrashHookImpl2是我们继承自Thread.UncaughtExceptionHandler 自己的实现类, 其中uncaughtException
实现如下:
@Override
public void uncaughtException(Thread t, Throwable e) {
//-----------自己的处理,拦截除0异常-------------------------------
StackTraceElement[] elements = e.getStackTrace();
for (int i = 0; i < elements.length; i++) {
if (e.getClass().getName().equals("java.lang.ArithmeticException")) {
return;
}
}
//-----------下面的系统的uncaughtException不走,既不会崩溃---------
if (mOtherUeh != null) {
mOtherUeh.uncaughtException(t, e);
}
if (mSystemUeh != null) {
mSystemUeh.uncaughtException(t, e);
}
}
这时候, 我们可以在程序中写一个除以0 的异常,则是不会引发崩溃的,只会引发线程的停止:
CrashHookImpl.getInstance().hook();
CrashHookImpl.getInstance().addCutOffExceptionHandler(new CrashHook.CutOffExceptionHandler() {
@Override
public boolean onCutOffException(Thread t, Throwable e) {
StackTraceElement[] elements = e.getStackTrace();
for (int i = 0; i < elements.length; i++) {
if (e.getClass().getName().equals("java.lang.ArithmeticException")) {
return true;
}
}
return false;
}
});
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(100 / 0 + "hook");
String nullp = null;
int a = nullp.length();
}
}
}.start();
实现类全部代码:
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import java.util.ArrayList;
import java.util.List;
public class CrashHookImpl2 implements Thread.UncaughtExceptionHandler, CrashHook {
private static volatile CrashHookImpl2 instance = null;
private Thread.UncaughtExceptionHandler mSystemUeh;
private Thread.UncaughtExceptionHandler mOtherUeh;
private List<CutOffExceptionHandler> exceptionHandlers;
private CrashHookImpl2() {
exceptionHandlers = new ArrayList<>();
}
@Override
public void hook() {
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if (ueh == null) {
mSystemUeh = null;
mOtherUeh = null;
Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
return;
}
String androidUeh = "com.android.internal.os.RuntimeInit$UncaughtHandler";
String selfUeh = getClass().getName();
String currUeh = ueh.getClass().getName();
//系统的
if (androidUeh.equals(currUeh)) {
mSystemUeh = ueh;
mOtherUeh = null;
Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
} else if (selfUeh.equals(currUeh)) {
//自己人 不处理
} else {
// bugly的 -> currUeh.contains("com.tencent.bugly.crashreport.crash")
mSystemUeh = null;
mOtherUeh = ueh;
Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
}
return;
}
public static CrashHookImpl2 getInstance() {
if (instance == null) {
synchronized (CrashHookImpl2.class) {
if (instance == null) {
instance = new CrashHookImpl2();
}
}
}
return instance;
}
@Override
public void unHook() {
if (mSystemUeh != null) {
Thread.setDefaultUncaughtExceptionHandler(mSystemUeh);
return;
}
if (mOtherUeh != null) {
Thread.setDefaultUncaughtExceptionHandler(mSystemUeh);
return;
}
Thread.setDefaultUncaughtExceptionHandler(null);
return;
}
@Override
public void addCutOffExceptionHandler(CrashHook.CutOffExceptionHandler cutOffExceptionHandler) {
if (cutOffExceptionHandler == null) {
return;
}
exceptionHandlers.add(cutOffExceptionHandler);
}
@Override
public void removeCutOffExceptionHandler(CrashHook.CutOffExceptionHandler cutOffExceptionHandler) {
if (cutOffExceptionHandler == null) {
return;
}
exceptionHandlers.remove(cutOffExceptionHandler);
}
public void tryHook(final long time) {
if (time < 0) {
return;
}
final HandlerThread thread = new HandlerThread("hook_ch");
thread.start();
final Handler handler = new Handler(thread.getLooper());
handler.post(new Runnable() {
private int size = 0;
@Override
public void run() {
if (size <= (time / 200)) {
size++;
hook();
handler.postDelayed(this, 200);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
thread.quitSafely();
} else {
thread.quit();
}
}
}
});
return;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
for (int i = 0; i < exceptionHandlers.size(); i++) {
boolean consumed = exceptionHandlers.get(i).onCutOffException(t, e);
if (consumed) {
return;
}
}
if (mOtherUeh != null) {
mOtherUeh.uncaughtException(t, e);
}
if (mSystemUeh != null) {
mSystemUeh.uncaughtException(t, e);
}
}
}
代理 Thread.UncaughtExceptionHandler 的方式
实际上也是Hook默认的 Thread.UncaughtExceptionHandler , 但这种方式的好处是,我们自己无需创建实现Thread.UncaughtExceptionHandler接口的实现类,而只需要使用Proxy.newProxyInstance
代理系统默认的Thread.UncaughtExceptionHandler
就好了,这种方式比起上面一种来说,除了效果一致以外,好处可能就是可以避免一些代码的检查.
代理方式的hook代码:
public void hook() {
if (hooked) {
return;
}
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if (ueh == null) {
return;
}
//代理逻辑
class Px implements InvocationHandler {
private Thread.UncaughtExceptionHandler handler;
private Method uncaughtExceptionMethod;
private Px(Thread.UncaughtExceptionHandler handler) {
this.handler = handler;
try {
uncaughtExceptionMethod = Thread.UncaughtExceptionHandler.class.getMethod("uncaughtException", Thread.class, Throwable.class);
} catch (Throwable e) {
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
//先走自己的
try {
if (uncaughtExceptionMethod != null && uncaughtExceptionMethod.equals(method)) {
if (args != null && args.length == 2) {
Thread thread = (Thread) args[0];
Throwable throwable = (Throwable) args[1];
for (int i = 0; i < exceptionHandlers.size(); i++) {
boolean cutoff = exceptionHandlers.get(i).onCutOffException(thread, throwable);
if (cutoff) {
//截断
return null;
}
}
}
}
} catch (Throwable ignored) {
}
try {
//原来的
method.invoke(handler, args);
} catch (Throwable ignored) {
}
return null;
}
}
Thread.UncaughtExceptionHandler hookedUeh = (Thread.UncaughtExceptionHandler) Proxy.newProxyInstance(
ueh.getClass().getClassLoader(),
ueh.getClass().getInterfaces(),
new Px(ueh));
Thread.setDefaultUncaughtExceptionHandler(hookedUeh);
hooked = true;
}
本种方式的全部实现代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class CrashHookImpl implements CrashHook {
private List<CutOffExceptionHandler> exceptionHandlers;
private static volatile CrashHookImpl instance = null;
private boolean hooked = false;
private CrashHookImpl() {
exceptionHandlers = new ArrayList<>();
}
public static CrashHookImpl getInstance() {
if (instance == null) {
synchronized (CrashHookImpl.class) {
if (instance == null) {
instance = new CrashHookImpl();
}
}
}
return instance;
}
@Override
public void hook() {
if (hooked) {
return;
}
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if (ueh == null) {
return;
}
class Px implements InvocationHandler {
private Thread.UncaughtExceptionHandler handler;
private Method uncaughtExceptionMethod;
private Px(Thread.UncaughtExceptionHandler handler) {
this.handler = handler;
try {
uncaughtExceptionMethod = Thread.UncaughtExceptionHandler.class.getMethod("uncaughtException", Thread.class, Throwable.class);
} catch (Throwable e) {
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
try {
if (uncaughtExceptionMethod != null && uncaughtExceptionMethod.equals(method)) {
if (args != null && args.length == 2) {
Thread thread = (Thread) args[0];
Throwable throwable = (Throwable) args[1];
for (int i = 0; i < exceptionHandlers.size(); i++) {
boolean cutoff = exceptionHandlers.get(i).onCutOffException(thread, throwable);
if (cutoff) {
//截断
return null;
}
}
}
}
} catch (Throwable ignored) {
}
try {
//原来的
method.invoke(handler, args);
} catch (Throwable ignored) {
}
return null;
}
}
Thread.UncaughtExceptionHandler hookedUeh = (Thread.UncaughtExceptionHandler) Proxy.newProxyInstance(
ueh.getClass().getClassLoader(),
ueh.getClass().getInterfaces(),
new Px(ueh));
Thread.setDefaultUncaughtExceptionHandler(hookedUeh);
hooked = true;
}
@Override
public void unHook() {
if (!hooked) {
return;
}
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if (ueh == null) {
return;
}
Thread.setDefaultUncaughtExceptionHandler(ueh);
hooked = false;
}
@Override
public void addCutOffExceptionHandler(CutOffExceptionHandler cutOffExceptionHandler) {
if (cutOffExceptionHandler == null) {
return;
}
exceptionHandlers.add(cutOffExceptionHandler);
}
@Override
public void removeCutOffExceptionHandler(CutOffExceptionHandler cutOffExceptionHandler) {
if (cutOffExceptionHandler == null) {
return;
}
exceptionHandlers.remove(cutOffExceptionHandler);
}
}
结语
对于崩溃,这两种方法只是避险的手段,而在我们的开发中,通过严谨的代码审查和测试,在上线之前解决问题,才是最好的选择.以上方法除了保护崩溃意外,还可以统计错误上报:
使用方法:
CrashHookImpl.getInstance().hook();
//保护所有的除零崩溃
CrashHookImpl.getInstance().addCutOffExceptionHandler(new CrashHook.CutOffExceptionHandler() {
@Override
public boolean onCutOffException(Thread t, Throwable e) {
if (e.getClass().getName().equals("java.lang.ArithmeticException")) {
//返回true,截断崩溃
return true;
}
return false;
}
});
//统计上报
CrashHookImpl.getInstance().addCutOffExceptionHandler(new CrashHook.CutOffExceptionHandler() {
@Override
public boolean onCutOffException(Thread t, Throwable e) {
StackTraceElement[] elements = e.getStackTrace();
for (int i = 0; i < elements.length; i++) {
String msg= elements[i].getClassName()+elements[i].getMethodName()+elements[i].getLineNumber();
//上报错误
}
//返回false 不截断
return false;
}
});