0x00 React Native Default Exception Handler
React Native官方支持在生成ReactInstanceManager
时使用NativeModuleCallExceptionHandler
接口来设置自己的异常处理器,防止js/react-native代码异常导致native端直接崩溃,样例代码如下:
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
.setApplication(context.getApplicationContext())
.addPackage(new MainReactPackage())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.setNativeModuleCallExceptionHandler(new NativeModuleCallExceptionHandler {
@Override
public void handleException(Exception e) {
// 自己处理react-native异常.
}
});
0x01 com.facebook.react.uimanager.IllegalViewOperationException
app发布到线上之后,仍然出现了少部分未能捕获的异常,堆栈信息如下:
com.facebook.react.uimanager.IllegalViewOperationException
Trying to add unknown view tag: 500 detail: View tag:496 children(2): [ 497,498, ], viewsToAdd(1): [ [2,500], ],
1 com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:387)
2 com.facebook.react.uimanager.UIViewOperationQueue$ManageChildrenOperation.execute(UIViewOperationQueue.java:179)
3 com.facebook.react.uimanager.UIViewOperationQueue$2.run(UIViewOperationQueue.java:787)
4 com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:843)
5 com.facebook.react.uimanager.UIViewOperationQueue.access$1600(UIViewOperationQueue.java:48)
6 com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:889)
7 com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:31)
8 com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:129)
9 com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:107)
10 android.view.Choreographer$CallbackRecord.run(Choreographer.java:798)
11 android.view.Choreographer.doCallbacks(Choreographer.java:603)
12 android.view.Choreographer.doFrame(Choreographer.java:571)
13 android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
14 android.os.Handler.handleCallback(Handler.java:815)
从日志中可以看出,抛出的异常为IllegalViewOperationException
,该异常的继承链如下,可以发现该异常继承自RuntimeException
,
IllegalViewOperationException -> JSApplicationCausedNativeException -> RuntimeException
对比GuardedFrameCallback
的源代码,可以发现在GuardedFrameCallback.doFrame
方法中捕获了全部的RuntimeException
。
package com.facebook.react.uimanager;
public abstract class GuardedFrameCallback extends ChoreographerCompat.FrameCallback {
@Override
public final void doFrame(long frameTimeNanos) {
try {
doFrameGuarded(frameTimeNanos);
} catch (RuntimeException e) {
mReactContext.handleException(e);
}
}
}
进一步往下追踪,ReactContext
的handleException()
方法的源代码如下:
public void handleException(RuntimeException e) {
if (mCatalystInstance != null &&
!mCatalystInstance.isDestroyed() &&
mNativeModuleCallExceptionHandler != null) {
mNativeModuleCallExceptionHandler.handleException(e);
} else {
throw e;
}
}
由于我们先前已经设置了NativeModuleCallExceptionHandler
处理器;同时异常并没有被捕获到;可知出现异常时mCatalystInstance
要么为null
,要么已经销毁了。
Catch IllegalViewOperationException
为了捕获这个异常,我们的做法大致如下:
@Aspect
public class ReactContextAspect {
@Around("execution (* com.facebook.react.bridge.ReactContext.handleException(..))")
public Object injectHandleException(ProceedingJoinPoint joinPoint) throws Throwable {
try {
joinPoint.proceed(joinPoint.getArgs());
} catch (Throwable e) {
LOGGER.d("ReactContextAspect", "handleException", e);
// 上报错误
}
return null;
}
private static class ReactContextHandleException extends Throwable {
public ReactContextHandleException(Throwable cause) {
super(cause);
}
}
}