1. 程式人生 > >React-Native開發十 react-navigation開發中的一些常見的坑

React-Native開發十 react-navigation開發中的一些常見的坑

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 [email protected]2:768 [email protected]
2:409 [email protected]2:262 <unknown>@435:301 [email protected]2:768 [email protected]2:409 [email protected]2:262 <unknown>@425:191 [email protected]2:768 [email protected]2:409 [email protected]2:262 <unknown>@307:191 [email protected]2:768 [email protected]2:409 [email protected]2:262 <unknown>@306:32 [email protected]2:768 [email protected]2:409 [email protected]2:262 <unknown>@12:42 [email protected]2:768 [email protected]2:339 [email protected]2:262 global [email protected]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是動態型別語言,很多型別及值在編譯器中不檢查,如果依賴型別及特殊值的,一定要養成良好的程式設計習慣,自己提早解決。