1 前言
都说RN开发效率高,一次学习随处编写。真的用RN开发了一个APP才知道,RN中坑真是太多,特别是很多坑只有在生产模式下才会出现,在平常的debug模式下,APP运行好好的,但是你一旦打正式包,就会发现各种报错,各种崩溃,如果在Android平台下,各种兼容性,各种奇葩的问题,加上js本身是动态语言,很多错误又无法在编译期间检查出来。因此用RN开发APP,在调试bug阶段消耗的时间和精力一点也不必开发的时间少。
下面说一说RN开发过程中的几个巨大的坑
2 常见的一些开发的坑
1 debug模式ok,但是生产环境打包出现了异常ReactNativeJS: undefined is not an object
08-20 20:03:55.491 2028-2050/com.github E/ReactNativeJS: undefined is not an object (evaluating 'a.View.propTypes.style')
08-20 20:03:55.494 2028-2050/com.github E/ReactNativeJS: Module AppRegistry is not a registered callable module (calling runApplication)
--------- beginning of crash
08-20 20:03:55.498 2028-2051/com.github E/AndroidRuntime: FATAL EXCEPTION: mqt_native_modules
Process: com.github, PID: 2028
com.facebook.react.common.JavascriptException: undefined is not an object (evaluating 'a.View.propTypes.style'), stack:
<unknown>@453:1186
d@2:768
n@2:409
t@2:262
<unknown>@435:301
d@2:768
n@2:409
t@2:262
<unknown>@425:191
d@2:768
n@2:409
t@2:262
<unknown>@307:191
d@2:768
n@2:409
t@2:262
<unknown>@306:32
d@2:768
n@2:409
t@2:262
<unknown>@12:42
d@2:768
n@2:339
t@2:262
global code@547:8
at com.facebook.react.modules.core.ExceptionsManagerModule.showOrThrowError(ExceptionsManagerModule.java:54)
at com.facebook.react.modules.core.ExceptionsManagerModule.reportFatalException(ExceptionsManagerModule.java:38)
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:160)
at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29)
at android.os.Looper.loop(Looper.java:164)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:192)
at java.lang.Thread.run(Thread.java:764)
08-20 20:03:55.580 2028-2050/com.github E/ReactNativeJS: Module AppRegistry is not a registered callable module (calling unmountApplicationComponentAtRootTag)
这个错误很悲催的是,在debug模式下是不会出现的,但是一旦在生成环境,在Android平台下就会出现。
出现这个错误后,一定要详细分析日志,详细分析日志,日志。。。
这里我把关键日志贴出来了undefined is not an object (evaluating ‘a.View.propTypes.style’)。现实的情况下你不一定会注意到这句话,这句话的意思是a.View.propTypes.style不是一个对象。怎么理解呢?google发现,是你js代码中propTypes使用方式错误了,网上很多资料提醒你,要导入import {PropTypes} from “prop-types”;之类的。但是我相信开发者一般不会犯这个错误。那么真实的错误是什么呢?真实的错误是代码中使用了View.propTypes.style来对某个属性的类型进行声明了。例如我犯错的代码如下:
//设置属性约束
static propTypes = {
style:View.propTypes.style,
title:PropTypes.string,
titleView:PropTypes.element,
hide:PropTypes.bool,
leftView:PropTypes.element,
rightView:PropTypes.element,
statusBar:PropTypes.shape(StatusBarShape),
};
可以看到style的属性约束和其他的属性约束不一样,由于js动态语言特性,不会报错。所以编译是没问题的。正确修改如下:
//设置属性约束
static propTypes = {
title:PropTypes.string,
titleView:PropTypes.element,
hide:PropTypes.bool,
leftView:PropTypes.element,
rightView:PropTypes.element,
statusBar:PropTypes.shape(StatusBarShape),
};
我直接去掉style的属性约束
这个bug坑爹的地方有以下两点
1 debug模式下,该bug不会出现的,导致无法定位和调试。
2 js是动态语言,这种类型的错误编译器也无法检测到,不会上报出来,导致只有在打release包之后才会出现,而且一出现就是崩溃。
2 生产环境Error while updating property ‘colors’ of a view managed by: AndroidSwipeRefreshLayout 错误
这个错误的崩溃信息如下:
08-20 20:54:36.105 3883-3883/com.github E/unknown:ViewManager: Error while updating prop colors
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:80)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:129)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:48)
at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:32)
at com.facebook.react.uimanager.NativeViewHierarchyManager.createView(NativeViewHierarchyManager.java:232)
at com.facebook.react.uimanager.UIViewOperationQueue$CreateViewOperation.execute(UIViewOperationQueue.java:152)
at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:815)
at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:928)
at com.facebook.react.uimanager.UIViewOperationQueue.access$2100(UIViewOperationQueue.java:46)
at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:988)
at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:134)
at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:105)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:909)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:655)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Double.intValue()' on a null object reference
at com.facebook.react.bridge.ReadableNativeArray.getInt(ReadableNativeArray.java:124)
at com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager.setColors(SwipeRefreshLayoutManager.java:58)
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:80)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:129)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:48)
at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:32)
at com.facebook.react.uimanager.NativeViewHierarchyManager.createView(NativeViewHierarchyManager.java:232)
at com.facebook.react.uimanager.UIViewOperationQueue$CreateViewOperation.execute(UIViewOperationQueue.java:152)
at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:815)
at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:928)
at com.facebook.react.uimanager.UIViewOperationQueue.access$2100(UIViewOperationQueue.java:46)
at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:988)
at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:134)
at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:105)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:909)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:655)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
08-20 20:54:36.106 3883-3883/com.github E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.github, PID: 3883
java.lang.RuntimeException: com.facebook.react.bridge.JSApplicationIllegalArgumentException: Error while updating property 'colors' of a view managed by: AndroidSwipeRefreshLayout
at com.facebook.react.bridge.ReactContext.handleException(ReactContext.java:311)
at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:31)
at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:134)
at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:105)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:909)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:655)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: com.facebook.react.bridge.JSApplicationIllegalArgumentException: Error while updating property 'colors' of a view managed by: AndroidSwipeRefreshLayout
at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:92)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:129)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:48)
at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:32)
at com.facebook.react.uimanager.NativeViewHierarchyManager.createView(NativeViewHierarchyManager.java:232)
at com.facebook.react.uimanager.UIViewOperationQueue$CreateViewOperation.execute(UIViewOperationQueue.java:152)
at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:815)
at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:928)
at com.facebook.react.uimanager.UIViewOperationQueue.access$2100(UIViewOperationQueue.java:46)
at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:988)
at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:134)
at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:105)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:909)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:655)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:80)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:129)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:48)
at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:32)
at com.facebook.react.uimanager.NativeViewHierarchyManager.createView(NativeViewHierarchyManager.java:232)
at com.facebook.react.uimanager.UIViewOperationQueue$CreateViewOperation.execute(UIViewOperationQueue.java:152)
at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:815)
at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:928)
at com.facebook.react.uimanager.UIViewOperationQueue.access$2100(UIViewOperationQueue.java:46)
at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:988)
at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:134)
at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:105)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:909)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:655)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Double.intValue()' on a null object reference
at com.facebook.react.bridge.ReadableNativeArray.getInt(ReadableNativeArray.java:124)
at com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager.setColors(SwipeRefreshLayoutManager.java:58)
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:80)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:129)
at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:48)
at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:32)
at com.facebook.react.uimanager.NativeViewHierarchyManager.createView(NativeViewHierarchyManager.java:232)
at com.facebook.react.uimanager.UIViewOperationQueue$CreateViewOperation.execute(UIViewOperationQueue.java:152)
at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:815)
at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:928)
at com.facebook.react.uimanager.UIViewOperationQueue.access$2100(UIViewOperationQueue.java:46)
at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:988)
at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:134)
at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:105)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:909)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:655)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
08-20 20:54:36.119 3883-3886/com.github I/zygote: Do partial code cache collection, code=30KB, data=23KB
08-20 20:54:36.120 3883-3886/com.github I/zygote: After code cache collection, code=30KB, data=23KB
Increasing code cache capacity to 128KB
这个错误初看起来是Android平台AndroidSwipeRefreshLayout的问题,但是我们知道RN都是将组件映射到原生控件的。因此要定位这个问题,你必须得知道什么控件会映射为AndroidSwipeRefreshLayout控件。
这里可以直接告诉你是RefreshControl,如果你不知道的话,那就只有一步一步的定位吧,对执行的代码加打点,一点一点的定位。
我们仔细看日志,发现是和colors有关系,那么检查代码中所有使用RefreshControl有关colors的地方吧,终于我发现了疑似出问题的地方
refreshControl={
<RefreshControl
//android
colors={[this.props.theme.colorPrimary]}
//ios
tintColor={this.props.theme.colorPrimary}
//ios
title="Loading"
titleColor={this.props.theme.colorPrimary}
refreshing={this.state.refreshing}
onRefresh={() => this.loadPopularData()}
/>
}
我们设置了Android平台下进度条颜色值,这里也是按照官方的使用说明设置成了一个数组。
那么为什么会报错呢,而且只是在release包中会报错呢?
答案就是this.props.theme.colorPrimary可能为null。如果为null,就会报错,并且这个报错只会在release中会出现。
所以解决办法也很简单,确保传入的值不会为null即可
坑爹的点
1 RefreshControl文档中未说明colors的属性要求的值不能为null
2 RN中对debug和release模式下处理的机制不一样,导致很多错误在release才会出现
先暂时写这两个坑吧,话说定位这两个错误真的花费了不少时间。后续有坑再继续补上!
3 总结
最后说一点RN开发的“忠告”
1 RN开发中不要太依赖debug模式,特别是Android平台,要不然你就等着哭吧
2 JS是动态类型语言,很多类型及值在编译器中不检查,如果依赖类型及特殊值的,一定要养成良好的编程习惯,自己提早解决。