Cockroach.java 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package com.edufound.reader.util;
  2. import android.content.Context;
  3. import android.os.Build;
  4. import android.os.Handler;
  5. import android.os.Looper;
  6. import android.os.Message;
  7. import com.edufound.reader.listener.ActivityKillerV15_V20;
  8. import com.edufound.reader.listener.ActivityKillerV21_V23;
  9. import com.edufound.reader.listener.ActivityKillerV24_V25;
  10. import com.edufound.reader.listener.ActivityKillerV26;
  11. import com.edufound.reader.listener.ActivityKillerV28;
  12. import com.edufound.reader.listener.IActivityKiller;
  13. import java.lang.reflect.Field;
  14. import me.weishu.reflection.Reflection;
  15. /**
  16. * Created by wanjian on 2017/2/14.
  17. */
  18. public final class Cockroach {
  19. private static IActivityKiller sActivityKiller;
  20. private static ExceptionHandler sExceptionHandler;
  21. private static boolean sInstalled = false;//标记位,避免重复安装卸载
  22. private static boolean sIsSafeMode;
  23. private Cockroach() {
  24. }
  25. public static void install(Context ctx, ExceptionHandler exceptionHandler) {
  26. if (sInstalled) {
  27. return;
  28. }
  29. try {
  30. //解除 android P 反射限制
  31. Reflection.unseal(ctx);
  32. } catch (Throwable throwable) {
  33. throwable.printStackTrace();
  34. }
  35. sInstalled = true;
  36. sExceptionHandler = exceptionHandler;
  37. initActivityKiller();
  38. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  39. @Override
  40. public void uncaughtException(Thread t, Throwable e) {
  41. if (sExceptionHandler != null) {
  42. sExceptionHandler.uncaughtExceptionHappened(t, e);
  43. }
  44. if (t == Looper.getMainLooper().getThread()) {
  45. isChoreographerException(e);
  46. safeMode();
  47. }
  48. }
  49. });
  50. }
  51. /**
  52. * 替换ActivityThread.mH.mCallback,实现拦截Activity生命周期,直接忽略生命周期的异常的话会导致黑屏,目前
  53. * 会调用ActivityManager的finishActivity结束掉生命周期抛出异常的Activity
  54. */
  55. private static void initActivityKiller() {
  56. //各版本android的ActivityManager获取方式,finishActivity的参数,token(binder对象)的获取不一样
  57. if (Build.VERSION.SDK_INT >= 28) {
  58. sActivityKiller = new ActivityKillerV28();
  59. } else if (Build.VERSION.SDK_INT >= 26) {
  60. sActivityKiller = new ActivityKillerV26();
  61. } else if (Build.VERSION.SDK_INT == 25 || Build.VERSION.SDK_INT == 24) {
  62. sActivityKiller = new ActivityKillerV24_V25();
  63. } else if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT <= 23) {
  64. sActivityKiller = new ActivityKillerV21_V23();
  65. } else if (Build.VERSION.SDK_INT >= 15 && Build.VERSION.SDK_INT <= 20) {
  66. sActivityKiller = new ActivityKillerV15_V20();
  67. } else if (Build.VERSION.SDK_INT < 15) {
  68. sActivityKiller = new ActivityKillerV15_V20();
  69. }
  70. try {
  71. hookmH();
  72. } catch (Throwable e) {
  73. e.printStackTrace();
  74. }
  75. }
  76. private static void hookmH() throws Exception {
  77. final int LAUNCH_ACTIVITY = 100;
  78. final int PAUSE_ACTIVITY = 101;
  79. final int PAUSE_ACTIVITY_FINISHING = 102;
  80. final int STOP_ACTIVITY_HIDE = 104;
  81. final int RESUME_ACTIVITY = 107;
  82. final int DESTROY_ACTIVITY = 109;
  83. final int NEW_INTENT = 112;
  84. final int RELAUNCH_ACTIVITY = 126;
  85. Class activityThreadClass = Class.forName("android.app.ActivityThread");
  86. Object activityThread = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null);
  87. Field mhField = activityThreadClass.getDeclaredField("mH");
  88. mhField.setAccessible(true);
  89. final Handler mhHandler = (Handler) mhField.get(activityThread);
  90. Field callbackField = Handler.class.getDeclaredField("mCallback");
  91. callbackField.setAccessible(true);
  92. callbackField.set(mhHandler, new Handler.Callback() {
  93. @Override
  94. public boolean handleMessage(Message msg) {
  95. if (Build.VERSION.SDK_INT >= 28) {//android P 生命周期全部走这
  96. final int EXECUTE_TRANSACTION = 159;
  97. if (msg.what == EXECUTE_TRANSACTION) {
  98. try {
  99. mhHandler.handleMessage(msg);
  100. } catch (Throwable throwable) {
  101. sActivityKiller.finishLaunchActivity(msg);
  102. notifyException(throwable);
  103. }
  104. return true;
  105. }
  106. return false;
  107. }
  108. switch (msg.what) {
  109. case LAUNCH_ACTIVITY:// startActivity--> activity.attach activity.onCreate r.activity!=null activity.onStart activity.onResume
  110. try {
  111. mhHandler.handleMessage(msg);
  112. } catch (Throwable throwable) {
  113. sActivityKiller.finishLaunchActivity(msg);
  114. notifyException(throwable);
  115. }
  116. return true;
  117. case RESUME_ACTIVITY://回到activity onRestart onStart onResume
  118. try {
  119. mhHandler.handleMessage(msg);
  120. } catch (Throwable throwable) {
  121. sActivityKiller.finishResumeActivity(msg);
  122. notifyException(throwable);
  123. }
  124. return true;
  125. case PAUSE_ACTIVITY_FINISHING://按返回键 onPause
  126. try {
  127. mhHandler.handleMessage(msg);
  128. } catch (Throwable throwable) {
  129. sActivityKiller.finishPauseActivity(msg);
  130. notifyException(throwable);
  131. }
  132. return true;
  133. case PAUSE_ACTIVITY://开启新页面时,旧页面执行 activity.onPause
  134. try {
  135. mhHandler.handleMessage(msg);
  136. } catch (Throwable throwable) {
  137. sActivityKiller.finishPauseActivity(msg);
  138. notifyException(throwable);
  139. }
  140. return true;
  141. case STOP_ACTIVITY_HIDE://开启新页面时,旧页面执行 activity.onStop
  142. try {
  143. mhHandler.handleMessage(msg);
  144. } catch (Throwable throwable) {
  145. sActivityKiller.finishStopActivity(msg);
  146. notifyException(throwable);
  147. }
  148. return true;
  149. case DESTROY_ACTIVITY:// 关闭activity onStop onDestroy
  150. try {
  151. mhHandler.handleMessage(msg);
  152. } catch (Throwable throwable) {
  153. notifyException(throwable);
  154. }
  155. return true;
  156. }
  157. return false;
  158. }
  159. });
  160. }
  161. private static void notifyException(Throwable throwable) {
  162. if (sExceptionHandler == null) {
  163. return;
  164. }
  165. if (isSafeMode()) {
  166. sExceptionHandler.bandageExceptionHappened(throwable);
  167. } else {
  168. sExceptionHandler.uncaughtExceptionHappened(Looper.getMainLooper().getThread(), throwable);
  169. safeMode();
  170. }
  171. }
  172. public static boolean isSafeMode() {
  173. return sIsSafeMode;
  174. }
  175. private static void safeMode() {
  176. sIsSafeMode = true;
  177. if (sExceptionHandler != null) {
  178. sExceptionHandler.enterSafeMode();
  179. }
  180. while (true) {
  181. try {
  182. Looper.loop();
  183. } catch (Throwable e) {
  184. isChoreographerException(e);
  185. if (sExceptionHandler != null) {
  186. sExceptionHandler.bandageExceptionHappened(e);
  187. }
  188. }
  189. }
  190. }
  191. /**
  192. * view measure layout draw时抛出异常会导致Choreographer挂掉
  193. * <p>
  194. * 建议直接杀死app。以后的版本会只关闭黑屏的Activity
  195. *
  196. * @param e
  197. */
  198. private static void isChoreographerException(Throwable e) {
  199. if (e == null || sExceptionHandler == null) {
  200. return;
  201. }
  202. StackTraceElement[] elements = e.getStackTrace();
  203. if (elements == null) {
  204. return;
  205. }
  206. for (int i = elements.length - 1; i > -1; i--) {
  207. if (elements.length - i > 20) {
  208. return;
  209. }
  210. StackTraceElement element = elements[i];
  211. if ("android.view.Choreographer".equals(element.getClassName())
  212. && "Choreographer.java".equals(element.getFileName())
  213. && "doFrame".equals(element.getMethodName())) {
  214. sExceptionHandler.mayBeBlackScreen(e);
  215. return;
  216. }
  217. }
  218. }
  219. }