microsoft / react-native-code-push

React Native module for CodePush
http://appcenter.ms
Other
8.99k stars 1.48k forks source link

App crashing on running updated bundle #1144

Closed marcmo closed 4 years ago

marcmo commented 6 years ago

My app is using RNN and uses codepush. When running the app with react-native run-android everything works and I can update using codepush. But when I install the app, the app crashes after the codepush update.

Expected Behavior

same behavior on installed app and app running with local packager

Actual Behavior

What actually happens?

01-15 14:02:44.006 14439 14608 D ReactNative: [CodePush] Applying full update.
01-15 14:02:44.028 14439 14485 I ReactNativeJS: [CodePush] Installing update.
01-15 14:02:44.101 14439 14486 D ReactNative: [CodePush] Loading JS bundle from "/data/user/0/com.esrlabs.csm4/files/CodePush/48e46b6353890ca25110f096c80afa3356c8d8c8b70eaef1c2e70a99333de075/CodePush/index.android.bundle"
01-15 14:02:44.103 14439 14439 D ReactNative: ReactInstanceManager.recreateReactContextInBackgroundInner()
01-15 14:02:44.103 14439 14439 D ReactNative: ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()
01-15 14:02:44.103 14439 14439 D ReactNative: ReactInstanceManager.recreateReactContextInBackground()

01-15 14:02:44.103 14439 14485 I ReactNativeJS: [CodePush] Restarting app
01-15 14:02:44.103 14439 14439 D ReactNative: ReactInstanceManager.runCreateReactContextOnNewThread()
01-15 14:02:44.103 14439 14439 D ReactNative: ReactInstanceManager.tearDownReactContext()
01-15 14:02:44.116 14439 14439 D ReactNative: CatalystInstanceImpl.destroy() start
01-15 14:02:44.118 14439 14613 D ReactNative: ReactInstanceManager.createReactContext()
01-15 14:02:44.135 14439 14613 W unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.airbnb.android.react.lottie.LottieAnimationViewManager
01-15 14:02:44.146 14439 14613 W unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.uimanager.LayoutShadowNode
...
01-15 14:02:44.205 14439 14439 D ReactNative: CatalystInstanceImpl.destroy() end
01-15 14:02:44.206 14439 14613 W unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.picker.ReactDialogPickerManager
...
01-15 14:02:44.269 14439 14613 D ReactNative: Initializing React Xplat Bridge.
01-15 14:02:44.271 14439 14613 D ReactNative: Initializing React Xplat Bridge before initializeBridge
01-15 14:02:44.279 14439 14613 D ReactNative: Initializing React Xplat Bridge after initializeBridge
01-15 14:02:44.279 14439 14613 D ReactNative: CatalystInstanceImpl.runJSBundle()
01-15 14:02:44.279 14439 14439 D ReactNative: ReactInstanceManager.setupReactContext()
01-15 14:02:44.280 14439 14439 D ReactNative: CatalystInstanceImpl.initialize()
01-15 14:02:44.281 14439 14439 D ReactNative: ReactInstanceManager.attachRootViewToInstance()
01-15 14:02:45.186 14439 14615 E ReactNativeJS: Module AppRegistry is not a registered callable module (calling runApplication)
01-15 14:02:45.210 14439 14616 E AndroidRuntime: FATAL EXCEPTION: mqt_native_modules
01-15 14:02:45.210 14439 14616 E AndroidRuntime: Process: com.esrlabs.csm4, PID: 14439
01-15 14:02:45.210 14439 14616 E AndroidRuntime: com.facebook.react.common.JavascriptException: Module AppRegistry is not a registered callable module (calling runApplication), stack:
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.modules.core.ExceptionsManagerModule.showOrThrowError(ExceptionsManagerModule.java:56)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.modules.core.ExceptionsManagerModule.reportFatalException(ExceptionsManagerModule.java:40)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:363)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:162)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at android.os.Handler.handleCallback(Handler.java:790)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:99)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:164)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:194)
01-15 14:02:45.210 14439 14616 E AndroidRuntime:    at java.lang.Thread.run(Thread.java:764)
01-15 14:02:45.545   766   778 I am_crash: [14439,0,com.esrlabs.csm4,950582854,com.facebook.react.common.JavascriptException,Module AppRegistry is not a registered callable module (calling runApplication), stack:
...

Environment

marcmo commented 6 years ago

what kind of clarifications can I provide?

sergey-akhalkov commented 6 years ago

Hi @marcmo, thanks for reaching us.

I've added clarification tag by mistake. As for your issue, could you please double check if your app has been configured correctly using this installation guide.

Please also take a look at this https://github.com/Microsoft/react-native-code-push/issues/790#issuecomment-294148452 - at the end of the comment you can find the configured app that should work.

marcmo commented 6 years ago

thanks for responding @sergey-akhalkov , I checked and my setup is almost exactly the same, only the name I use for my getJSBundleFile is different:

public class MainApplication extends NavigationApplication implements ReactInstanceHolder {

    @Override
    public boolean isDebug() {
        return BuildConfig.DEBUG;
    }

    @Nullable
    @Override
    public List<ReactPackage> createAdditionalReactPackages() {
        return getReactPackages();
    }

    @NonNull
    protected List<ReactPackage> getReactPackages() {
        return Arrays.<ReactPackage>asList(
                new CodePush(getResources().getString(R.string.reactNativeCodePush_androidDeploymentKey), getApplicationContext(), BuildConfig.DEBUG)
        );
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }

    @Override
    public String getJSBundleFile() {
        return CodePush.getJSBundleFile();
    }

    @Override
    public ReactInstanceManager getReactInstanceManager() {
        return getReactNativeHost().getReactInstanceManager();
    }
}

I had a lot of different issues and was able to resolve them but now what baffles me is that my setup works perfectly fine when running during development, but the error shows up in the installed release version. (this error: Module AppRegistry is not a registered callable module (calling runApplication))

sergey-akhalkov commented 6 years ago

@marcmo, could you please share which way you are registering your callable module? As for the sample app above, please pay attention to the following code:

import CodePush from 'react-native-code-push';

// register all screens of the app (including internal ones)
export function registerScreens() {
  Navigation.registerComponent('example.FirstTabScreen', () => CodePush(FirstTabScreen));
  ...
}
marcmo commented 6 years ago

are you sure about the call to CodePush? For me it seems CodePush is a function that takes an optional config as parameter and when called returns a function that takes the screen as parameter.

this is how I use codepush:

// module registration
export const CodePushConfig = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
};

export const registerScreens = (store: Redux.Store<RootState>, provider) => {
  Navigation.registerComponent('xyz.IntroScreen', () => IntroScreen, store, provider);
  ...
  Navigation.registerComponent('xyz.DevOpts', () => CodePush(CodePushConfig)(DevOpts), store, provider);
  ...
};

// in DevOpts:
componentWillMount() {
  CodePush.sync({
  updateDialog: CodePush.DEFAULT_UPDATE_DIALOG,
  installMode: CodePush.InstallMode.IMMEDIATE,
  deploymentKey: Platform.select({
    android: '***',
    ios: '***',
    }),
  });
}

I have to admit I don't yet understand the RN app lifecycle implications on codepush. e.g. if it is possible/advisable to ask for a codepush sync like this:

AppState.addEventListener('change', handleAppStateChange);

const handleAppStateChange = (nextAppState: AppStateStatus) => {
  switch (nextAppState) {
    case 'active':
      CodePush.sync(AppConfig.codePushConfig);
      break;
    default:
  }
};
sergey-akhalkov commented 6 years ago

are you sure about the call to CodePush?

Yeah, I'm sure, please take a look at codePush(..) usage here.

this is how I use codepush:

Looks good, it's really weird why CodePush doesn't work for you. Could you please share the minimum working version of you App so I'll be able to take a look at it closer? As of now I don't sure what exact root cause of your issue is, but I'm sure that demo app I've provided here https://github.com/Microsoft/react-native-code-push/issues/790#issuecomment-294148452 worked well for me - could you please also download it and confirm if it works for you?

marcmo commented 6 years ago

@sergey-akhalkov I will try to prepare a minimal app version but that will take me some time... I was hoping you already experienced situations where code-push was working when running in development mode, but not in an release-build. That indicates to me that all my setup can actually work, my keys are correct and my configuration is OK. but currently I'm really lost on this error:

Module AppRegistry is not a registered callable module (calling runApplication)

anyway...thanks for looking into this. I will try to setup a sharable example to demonstrate this behavior.

sergey-akhalkov commented 6 years ago

Hi @marcmo, got it, will wait for the demo!

I was hoping you already experienced situations where code-push was working when running in development mode, but not in an release-build.

Please note, that running RN app in the development mode is not actually the same as running in release mode, please see official docs:

NOTE: CodePush updates should be tested in modes other than Debug mode. In Debug mode, React Native app always downloads JS bundle generated by packager, so JS bundle downloaded by CodePush does not apply.

Loading JS bundle via packager is the main reason why you have no issue in debug mode. CodePush uses locally installed JS bundle instead.

marcmo commented 6 years ago

ok..I prepared a repository (android only) that can demonstrate this behavior: https://github.com/marcmo/code-push-minimal I would be grateful if you could take a look at this @sergey-akhalkov the README has instructions on how to build and how to trigger codepush. I guess you will have no problems with the codepush keys...keystore with everything that is needed is also in the repository.

sergey-akhalkov commented 6 years ago

@marcmo, great, thank you! I'm going to investigate it and let you know if I have any questions or If I'll found the root cause of the issue.

marcmo commented 6 years ago

@ruslan-bikkinin let me know if you need anything else from my side...I'm really stuck on this one

ruslan-bikkinin commented 6 years ago

Hi @marcmo, I was able to reproduce your issue using demo project you sent us. After further investigation I am suspecting that the root of the problem is somewhere in react-native. Actually, when you are using any install modes but ON_NEXT_RESTART, CodePush is using soft restart of app without killing it. This mechanism uses react-native method instanceManager.recreateReactContextInBackground(); that does the trick.

Everything works pretty good when you are starting the app from scratch but something goes wrong after restarting the app using this method because probably several context parts aren't cleared completely in case of registering components asynchronously.

We have similar issue here. I will prepare a clean demo and ask the react-native team on github about possible solutions. I will post the results here.

As for now, you can switch install mode to ON_NEXT_RESTART as a workaround.

marcmo commented 6 years ago

thanks so much @ruslan-bikkinin and @sergey-akhalkov ! I just verified that using ON_NEXT_RESTART did the trick! feel free to close the issue or leave it open until the IMMEDIATE option also works.

zkrige commented 6 years ago

Is there any progress on this? IMMEDIATE works fine in debug mode, but crashes the app and rolls back the update for release builds

ericketts commented 6 years ago

fwiw I believe I've somewhat nailed down why this is a problem in wix/react-native-navigation#2331 and a hacky "fix", but I'm afraid I don't yet have a solid generalized solution figured out ☹️

henrycity commented 6 years ago

The app doesn't crash for me but navigation is broken. These are the logs I get

06-07 10:30:51.359 9800-9862/? E/unknown:ReactNative: Invariant Violation: Minified React error #143; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=143 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

    This error is located at:
        in r
        in t
        in RCTView
        in t
        in Connect(t)
        in RCTView
        in t
        in Connect(t)
        in RCTView
        in t
        in Connect(t)
        in r
        in n
        in s
        in RCTView
        in RCTView
        in t, stack:
    only@101:4233
    value@659:1338
    l@44:54398
    beginWork@44:56240
    n@44:82237
    i@44:82566
    o@44:82907
    C@44:87082
    b@44:86624
    m@44:85850
    d@44:85131
    f@44:84822
    t@44:50910
    updateContainer@44:101734
    render@44:69237
    exports@283:730
    run@279:615
    runApplication@279:2046
    value@21:3582
    <unknown>@21:1067
    value@21:3009
    value@21:1039
06-07 10:30:51.359 9800-9861/? E/ReactNativeJS: 'Unhandled promise rejection', { [ReferenceError: Can't find variable: AsyncStorage]
      line: 1199,
      column: 895,
      sourceURL: '/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle' }
06-07 10:30:51.361 9800-9861/? E/ReactNativeJS: 'Unhandled promise rejection', { [ReferenceError: Can't find variable: AsyncStorage]
      line: 1199,
      column: 895,
      sourceURL: '/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle' }
06-07 10:30:51.362 9800-9862/? E/unknown:ReactNative: console.error: "Unhandled promise rejection", {"line":1199,"column":895,"sourceURL":"/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle"}, stack:
    <unknown>@1078:1147
    exports@1085:66
    <unknown>@1078:1018
    <unknown>@26:1779
    u@26:513
    c@26:877
    callImmediates@26:3180
    value@21:3183
    <unknown>@21:1516
    value@21:3009
    value@21:1486
    value@21:1094
06-07 10:30:51.363 9800-9862/? E/unknown:ReactNative: console.error: "Unhandled promise rejection", {"line":1199,"column":895,"sourceURL":"/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle"}, stack:
    <unknown>@1078:1147
    exports@1085:66
    <unknown>@1078:1018
    <unknown>@26:1779
    u@26:513
    c@26:877
    callImmediates@26:3180
    value@21:3183
    <unknown>@21:1516
    value@21:3009
    value@21:1486
    value@21:1094
06-07 10:30:51.558 9800-9800/? E/zygote: The String#value field is not present on Android versions >= 6.0
06-07 10:30:51.878 9800-9861/? E/ReactNativeJS: 'Unhandled promise rejection', { [ReferenceError: Can't find variable: AsyncStorage]
      line: 1199,
      column: 895,
      sourceURL: '/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle' }
06-07 10:30:51.880 9800-9861/? E/ReactNativeJS: 'Unhandled promise rejection', { [ReferenceError: Can't find variable: AsyncStorage]
      line: 1199,
      column: 895,
      sourceURL: '/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle' }
06-07 10:30:51.881 9800-9862/? E/unknown:ReactNative: console.error: "Unhandled promise rejection", {"line":1199,"column":895,"sourceURL":"/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle"}, stack:
    <unknown>@1078:1147
    exports@1085:66
    <unknown>@1078:1018
    <unknown>@26:1779
    u@26:513
    c@26:877
    callImmediates@26:3180
    value@21:3183
    <unknown>@21:1516
    value@21:3009
    value@21:1486
    value@21:1094
06-07 10:30:51.882 9800-9862/? E/unknown:ReactNative: console.error: "Unhandled promise rejection", {"line":1199,"column":895,"sourceURL":"/data/user/0/fi.taskuparkki.app/files/CodePush/a23158e284ff200eda3120985783f8bf574ee5ab2743218be6b2f32caeb5c7bc/CodePush/index.android.bundle"}, stack:
    <unknown>@1078:1147
    exports@1085:66
    <unknown>@1078:1018
    <unknown>@26:1779
    u@26:513
    c@26:877
    callImmediates@26:3180
    value@21:3183
    <unknown>@21:1516
    value@21:3009
    value@21:1486
    value@21:1094
ericketts commented 6 years ago

fwiw the PR I submitted that's linked above seems to fix this bug for me... I'm a bit worried about potential race condition but so far I have yet to see one arise.

Also I doubt it will be merged since its for V1 and they've said they're not accepting V1 PRs any more, but I wanted to get it out there for others that are having this issue to fork & patch themselves without having to spend the time I did figuring out what was going wrong.

ericketts commented 6 years ago

exciting news, despite the announcement that V1 is closed for PRs wix actually merged my fix PR, so @sergey-akhalkov (sorry if you're the wrong person to tag) you might be able to close this issue?

edit: cc @ruslan-bikkinin

ruslan-bikkinin commented 6 years ago

Hi @ericketts, I am not responsible for codepush stuff anymore, I believe the right person for it is @alexandergoncharov.

alexandergoncharov-zz commented 6 years ago

Hi all, Yeah, I'll look at this as soon as possible. It is in queue. Sorry for this delay.

Thanks, Alexander

nighttiger1990 commented 4 years ago

Is there any progress on this? I hope IMMEDIATE option works soon.

JB-CHAUVIN commented 4 years ago

Same error for me... restarting the app with react-native-navigation on Android causes this error.

JB-CHAUVIN commented 4 years ago

Any news about this ? How can we get the IMMEDIATE option working ?

micheleb commented 4 years ago

In my case the problem seemed to be that the onCatalystInstanceDestroy() method was not blocking the instanceManager.recreateReactContextInBackground() call here, so the context would be recreated before onCatalystInstanceDestroy() had a chance to call navigator.destroyViews(), and the app would come up and find these views that weren't registered (I'm using a tab layout, and I was getting an Invariant Violation: "MyView" has not been registered. for each tab). Or something along those lines, an issue with synchronization anyway.

The solution that seems to be working well so far was to add a native restartApp() method to my app that would first call navigator.destroyViews() to clear RNN state, and use a promise to have the JS call CodePush.restartApp(false) once that was done.

In code, it's something like this:

/* MyNativeUtils.kt */
@ReactMethod
fun restartApp(promise: Promise) {
    val activity = currentActivity
    val lock = ReentrantLock()

    // MainActivity is my root activity extending RNN NavigationActivity
    if (activity == null || activity !is MainActivity) {
        Log.e(TAG, "activity is " + activity)
    } else {
        // show the splash screen while we're reloading
        SplashScreen.show(activity, true)

        val viewsDestroyed = lock.newCondition()
        val destroyed = AtomicBoolean(false)

        runOnUiThread(Runnable {
            activity.navigator.destroyViews()
            try {
                lock.lock()
                destroyed.set(true)
                viewsDestroyed.signalAll()
            } finally {
                lock.unlock()
            }
        })

        try {
            lock.lock()
            if (!destroyed.get()) {
                // waiting for views to be destroyed
                viewsDestroyed.await(3, TimeUnit.SECONDS)
                // wake up, JS!
                promise.resolve(true);
            }
        } catch (e: InterruptedException) {
            Log.w(TAG, "stopped waiting for views to be destroyed, see stack trace")
            e.printStackTrace()
            promise.resolve(false)
        } finally {
            lock.unlock()
        }
    }
}

I'm using reentrant locks just because, you can synchronize however you like, of course. On the JS side I have a snackbar with a restart button that calls this

onAppRestart: () => 
  MyNativeUtils.restartApp().then((ready) => {
    if (ready) {
      CodePush.restartApp(false);
    } else {
      MyNativeUtils.toast("couldn't install update");
    }
  })

The onCatalystInstanceDestroy() overrides added in this PR (and this one for RNN v2+) can stay, they simply won't do anything as they're called after everything's already been cleared.

Hope this helps somebody, it was really a pain to debug!

Krasavinigor commented 4 years ago

Hi @nighttiger1990, @JB-CHAUVIN! Sorry for the delay! I couldn't reproduce the issue. I created a demo project with the latest versions of RN, RNN, and react-native-code-push (link: https://github.com/Krasavinigor/DemoProjectWithRNN). Could you please provide a demo project with the issue?

Krasavinigor commented 4 years ago

Hi @nighttiger1990, @JB-CHAUVIN! Unfortunately, I'm going to close this issue for now as I haven't heard from you in a while. Please feel free to reopen it if you have any questions.