package com.edufound.reader.util; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import com.edufound.reader.listener.ActivityKillerV15_V20; import com.edufound.reader.listener.ActivityKillerV21_V23; import com.edufound.reader.listener.ActivityKillerV24_V25; import com.edufound.reader.listener.ActivityKillerV26; import com.edufound.reader.listener.ActivityKillerV28; import com.edufound.reader.listener.IActivityKiller; import java.lang.reflect.Field; import me.weishu.reflection.Reflection; /** * Created by wanjian on 2017/2/14. */ public final class Cockroach { private static IActivityKiller sActivityKiller; private static ExceptionHandler sExceptionHandler; private static boolean sInstalled = false;//标记位,避免重复安装卸载 private static boolean sIsSafeMode; private Cockroach() { } public static void install(Context ctx, ExceptionHandler exceptionHandler) { if (sInstalled) { return; } try { //解除 android P 反射限制 Reflection.unseal(ctx); } catch (Throwable throwable) { throwable.printStackTrace(); } sInstalled = true; sExceptionHandler = exceptionHandler; initActivityKiller(); Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { if (sExceptionHandler != null) { sExceptionHandler.uncaughtExceptionHappened(t, e); } if (t == Looper.getMainLooper().getThread()) { isChoreographerException(e); safeMode(); } } }); } /** * 替换ActivityThread.mH.mCallback,实现拦截Activity生命周期,直接忽略生命周期的异常的话会导致黑屏,目前 * 会调用ActivityManager的finishActivity结束掉生命周期抛出异常的Activity */ private static void initActivityKiller() { //各版本android的ActivityManager获取方式,finishActivity的参数,token(binder对象)的获取不一样 if (Build.VERSION.SDK_INT >= 28) { sActivityKiller = new ActivityKillerV28(); } else if (Build.VERSION.SDK_INT >= 26) { sActivityKiller = new ActivityKillerV26(); } else if (Build.VERSION.SDK_INT == 25 || Build.VERSION.SDK_INT == 24) { sActivityKiller = new ActivityKillerV24_V25(); } else if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT <= 23) { sActivityKiller = new ActivityKillerV21_V23(); } else if (Build.VERSION.SDK_INT >= 15 && Build.VERSION.SDK_INT <= 20) { sActivityKiller = new ActivityKillerV15_V20(); } else if (Build.VERSION.SDK_INT < 15) { sActivityKiller = new ActivityKillerV15_V20(); } try { hookmH(); } catch (Throwable e) { e.printStackTrace(); } } private static void hookmH() throws Exception { final int LAUNCH_ACTIVITY = 100; final int PAUSE_ACTIVITY = 101; final int PAUSE_ACTIVITY_FINISHING = 102; final int STOP_ACTIVITY_HIDE = 104; final int RESUME_ACTIVITY = 107; final int DESTROY_ACTIVITY = 109; final int NEW_INTENT = 112; final int RELAUNCH_ACTIVITY = 126; Class activityThreadClass = Class.forName("android.app.ActivityThread"); Object activityThread = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null); Field mhField = activityThreadClass.getDeclaredField("mH"); mhField.setAccessible(true); final Handler mhHandler = (Handler) mhField.get(activityThread); Field callbackField = Handler.class.getDeclaredField("mCallback"); callbackField.setAccessible(true); callbackField.set(mhHandler, new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (Build.VERSION.SDK_INT >= 28) {//android P 生命周期全部走这 final int EXECUTE_TRANSACTION = 159; if (msg.what == EXECUTE_TRANSACTION) { try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { sActivityKiller.finishLaunchActivity(msg); notifyException(throwable); } return true; } return false; } switch (msg.what) { case LAUNCH_ACTIVITY:// startActivity--> activity.attach activity.onCreate r.activity!=null activity.onStart activity.onResume try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { sActivityKiller.finishLaunchActivity(msg); notifyException(throwable); } return true; case RESUME_ACTIVITY://回到activity onRestart onStart onResume try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { sActivityKiller.finishResumeActivity(msg); notifyException(throwable); } return true; case PAUSE_ACTIVITY_FINISHING://按返回键 onPause try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { sActivityKiller.finishPauseActivity(msg); notifyException(throwable); } return true; case PAUSE_ACTIVITY://开启新页面时,旧页面执行 activity.onPause try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { sActivityKiller.finishPauseActivity(msg); notifyException(throwable); } return true; case STOP_ACTIVITY_HIDE://开启新页面时,旧页面执行 activity.onStop try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { sActivityKiller.finishStopActivity(msg); notifyException(throwable); } return true; case DESTROY_ACTIVITY:// 关闭activity onStop onDestroy try { mhHandler.handleMessage(msg); } catch (Throwable throwable) { notifyException(throwable); } return true; } return false; } }); } private static void notifyException(Throwable throwable) { if (sExceptionHandler == null) { return; } if (isSafeMode()) { sExceptionHandler.bandageExceptionHappened(throwable); } else { sExceptionHandler.uncaughtExceptionHappened(Looper.getMainLooper().getThread(), throwable); safeMode(); } } public static boolean isSafeMode() { return sIsSafeMode; } private static void safeMode() { sIsSafeMode = true; if (sExceptionHandler != null) { sExceptionHandler.enterSafeMode(); } while (true) { try { Looper.loop(); } catch (Throwable e) { isChoreographerException(e); if (sExceptionHandler != null) { sExceptionHandler.bandageExceptionHappened(e); } } } } /** * view measure layout draw时抛出异常会导致Choreographer挂掉 *

* 建议直接杀死app。以后的版本会只关闭黑屏的Activity * * @param e */ private static void isChoreographerException(Throwable e) { if (e == null || sExceptionHandler == null) { return; } StackTraceElement[] elements = e.getStackTrace(); if (elements == null) { return; } for (int i = elements.length - 1; i > -1; i--) { if (elements.length - i > 20) { return; } StackTraceElement element = elements[i]; if ("android.view.Choreographer".equals(element.getClassName()) && "Choreographer.java".equals(element.getFileName()) && "doFrame".equals(element.getMethodName())) { sExceptionHandler.mayBeBlackScreen(e); return; } } } }