Expo Fix: Improve New Architecture JS bundle reload on Android by prioritizing reactHost.reload() #20
Conversation
CHOIMINSEOK
left a comment
There was a problem hiding this comment.
Hello, @naveen-bitrise ! thank you for this contribution
While I read your pull request, I was wondering how expo-based React-Native app could be reloaded even the reflection is failed on ExpoReactHostDelegate. Can you explain more about this?
android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java
Outdated
Show resolved
Hide resolved
android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java
Outdated
Show resolved
Hide resolved
|
Before we run regression tests across various environments, could you add regarding Demo App on Examples directory? (so that we will run regression tests on your setup) @naveen-bitrise |
|
I have added an Example project CodePushExpoDemoApp in the Examples directory. For Expo there is a prebuild step Please note that in package.json in the example, I could not use |
| CodePushUtils.log("Could not get ReactHostDelegate from ReactHostImpl."); | ||
| } | ||
| } else { | ||
| CodePushUtils.log("ReactHost is not a direct ReactHostImpl instance (" + reactHost.getClass().getName() + "), skipping direct setJSBundle reflection attempt. This is expected with Expo."); |
There was a problem hiding this comment.
What about trying to typecast to ExpoReactHost implementation & update jsbundleFile on it using reflection?
@naveen-bitrise
There was a problem hiding this comment.
The exception being caught is NoSuchFieldException here
Field 'jsBundleLoader' NOT FOUND on expo.modules.ExpoReactHostFactory$ExpoReactHostDelegate.
It might have a differently named field, or likely that Expo delegate is designed to not have its bundle loader set this way.
There was a problem hiding this comment.
Yes They have a different implementation, so it must be failed. What I was telling you was we might be able to fix the reflection code working on ExpoReactHostDelegate
I think just replacing _jsBundleLoader backing field inside ExpoReactHostDelegate would be enough? ( It seems it's for dev mode, but i think we could use it for code push too)
https://github.com/expo/expo/blob/54d745bed821a8054a52237739a7b4571448bf89/packages/expo/android/src/rn78/main/expo/modules/ExpoReactHostFactory.kt#L39
Anyway, I'm running regression test for your PR and leave the result on it. It takes 2 hours or so.
@naveen-bitrise
|
I'll do regression tests & leave a comment about the result here. In the mean time, I'll add github action script for test. |
There was a problem hiding this comment.
👍👍👍
Note: Maybe we could use this as official code-push expo plugin ?
@kmsbernard @naveen-bitrise
|
All regression tests are passed 🎉 New Architecture Android ✅ |
|
As current test code depends on bare react native app structure, we need to add new test code configuration for expo. Trying to setup this with ExpoDemoApp |
|
Note: To complete the Expo test setup, the following tasks remain:
Thanks for your patience 😅 Let’s aim to wrap this up by next Sunday! |
|
We added test case for expo codepush plugin, and it fails on 30 scenarios 🥲. We will start addressing this on next weekend |
|
@CHOIMINSEOK Is there anything I can do to help? |
|
@naveen-bitrise hey, I think we will address them this Sunday. Do you have time around 11:00 - 15:00 KST? Maybe we could share some failure cases |
|
@CHOIMINSEOK that would be Saturday night time 10pm - 2 am for me as I am in US East Cost. I may not be available then, but I can review on Sunday morning based on the comments here. |
|
I see. I’ll leave comments about the remaining regression issues tomorrow. What time works best for you, by the way? I might be able to adjust my schedule to match yours. @naveen-bitrise |
|
We identified the issue — it turns out expo export generates a different bundle format compared to react-native bundle, which causes the CodePush client to fail at runtime when trying to locate the JS bundle. Given that the CodePush CLI is currently tightly coupled with the React Native CLI, this behavior is understandable. Until we extend the CodePush CLI to support expo export, any Expo-based app will need to use react-native bundle (which is the default behavior of the CodePush CLI) to ensure compatibility. That said, I’m curious — could this impact Expo app runtime? Are you currently using CodePush on top of an Expo app? |
|
Yes, the CodePushExpoDemoApp app works with CodePush. When you run The plugin file has to be in the |
It doesn't have to be. We can install the plugin via npm if we place the file in the root package. For easier usage, it have to be shipped in root. @naveen-bitrise |
This is how MainApplication.kt will look like. |
…n app/build.gradle fixed 2. Added addtional debug looging to App.js 3. Update README
|
@CHOIMINSEOK I tested with CodePush to confirm that it works. Here are the steps that I did and the CodePush update was loaded successfully
|
|
@CHOIMINSEOK I can connect during the week, this week during the day and anytime till 10 pm (EDT). I am away, from July 11 to 21. |
|
Thanks! I'll add a additional guide about how to use plugin tomorrow. |
CHOIMINSEOK
left a comment
There was a problem hiding this comment.
Really appreciate for your contribution and patience! @naveen-bitrise
…oritizing reactHost.reload() (CodePushNext#20) * classCastException caught in loadBundle * removed debug comments * Added Expo Example Project * updated android bundle name to index.android.bundle * Added iOS instructions to Readme in CodePushExpoDemoApp * Updated iOS instructions in Readme in CodePushExpoDemoApp * Updated Readme in CodePushExpoDemoApp to mention codepush expo plugin * updated Readme to include zipping the updates * minor Readme corrections * add expo test-app setup-1 * refactored expo codepush plugin * expo setup for test - 2 * expo setup for test - 3 * override TestAppName * fix android test * fix bundling script * fix bundling script * using react-native bundle instead of expo export during expo test * moved expo plugin file to CodePushExpoDemoApp * moved expo.js to root * 1. Expo plugin refactor, and an issue in modifying buildTypes block in app/build.gradle fixed 2. Added addtional debug looging to App.js 3. Update README * expo plugin refactor, and README update * add expo plugin guide --------- Co-authored-by: CHOIMINSEOK <cms3718@gmail.com> Co-authored-by: Minsik Kim <kmsbernard@gmail.com> Co-authored-by: Minseok Choi <minseokchoi@Minseoks-MacBook-Pro.local>



Problem Description
This PR addresses an issue where CodePush updates fail to apply correctly on Android when using React Native's New Architecture, particularly in environments where the
ReactHostDelegateimplementation differs from what the library's reflection-basedsetJSBundlemethod assumes. (which is the case for Expo)Currently, the
CodePushNativeModule.javaattempts to directly set ajsBundleLoaderfield on theReactHostDelegateusing Java reflection. This approach is brittle and can lead to exceptions (e.g.,NoSuchFieldException) if theReactHostDelegateimplementation (such asExpoReactHostDelegatein Expo-managed projects, or potentially other custom setups) does not have this specific internal field.When this reflection fails, the exception can be caught by an outer error handler in the
loadBundlemethod. This often prevents the more standard New Architecture reload mechanism,reactHost.reload(), from being called or from completing effectively, leading to the CodePush update not being applied without a full Activity restart.Solution
This PR implements the following changes to
CodePushNativeModule.javato improve the reliability of applying updates on Android with the New Architecture:Made
setJSBundle(ReactHostDelegate, String)More Resilient:jsBundleLoaderonReactHostDelegatehas been modified.NoSuchFieldException(and potentially other reflection-related exceptions likeIllegalAccessExceptionorSecurityException) internally.Prioritized
ReactHost.reload()inloadBundle()for New Architecture:loadBundle()method has been refactored to ensure thatreactHost.reload("reason");is consistently called.setJSBundleis made, but its failure no longer preventsreactHost.reload()from executing.ReactHostDelegate.Benefits
ReactHostDelegateimplementations (e.g., Expo).How to Test (Example Scenario)
These changes were identified and tested in an environment using:
@code-push-next/react-native-code-pushversion10.0.1In this environment, the previous implementation failed to apply updates due to
NoSuchFieldExceptionwhen interacting withExpoReactHostDelegate. With these changes, CodePush updates are now applied correctly, with the JavaScript bundle reloading as expected.