Skip to content

feat(background-thread): add SharedBridge JSI HostObject#43

Open
huhuanming wants to merge 2 commits intomainfrom
feat/shared-bridge-hostobject
Open

feat(background-thread): add SharedBridge JSI HostObject#43
huhuanming wants to merge 2 commits intomainfrom
feat/shared-bridge-hostobject

Conversation

@huhuanming
Copy link
Contributor

@huhuanming huhuanming commented Mar 21, 2026

Summary

  • Add cross-platform C++ SharedBridge HostObject that is installed in both main and background JSI runtimes, sharing the same process-level queues
  • Provides pull-model data transfer (send/drain/hasMessages) alongside existing push-model (postHostMessage/onHostMessage)
  • iOS integration: installs in background runtime via hostDidStart, main runtime via +installSharedBridgeInMainRuntime: class method

Files

File Purpose
cpp/SharedBridge.{h,cpp} Cross-platform HostObject — thread-safe queues + atomic flags
src/SharedBridge.ts TypeScript ISharedBridge interface + getSharedBridge()
ios/BackgroundRunnerReactNativeDelegate.mm Install in background runtime
ios/BackgroundThreadManager.{h,mm} +installSharedBridgeInMainRuntime:
BackgroundThread.podspec Include cpp/ sources
example/AppDelegate.swift Override hostDidStart to install in main runtime
example/background.js Drain loop with ping/pong demo
example/BackgroundThreadTestPage.tsx Push + Pull model test UI

Test plan

  • TypeScript compilation passes (tsc --noEmit)
  • Android assembleDebug passes
  • iOS Xcode build
  • Run example app: test Push Model (send message button)
  • Run example app: test Pull Model (SharedBridge ping/pong)

Open with Devin

…ntime data transfer

Add a shared C++ HostObject that is installed in both the main and background
JSI runtimes, enabling direct memory-level communication without ObjC dispatch
overhead. Provides both push (existing postHostMessage/onHostMessage) and pull
(SharedBridge send/drain) communication models.

- cpp/SharedBridge.{h,cpp}: Cross-platform HostObject with thread-safe queues
- iOS: Install in background runtime via hostDidStart, main runtime via class method
- TypeScript: ISharedBridge type and getSharedBridge() helper
- Example: Updated test page with ping/pong demo and drain loop
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

…SharedBridge

- CMakeLists.txt + cpp-adapter.cpp: JNI bridge for SharedBridge install,
  postHostMessage/onHostMessage JSI globals, and message passing
- BackgroundThreadModule.kt: Full implementation with ReactHostImpl for
  background JS runtime, JSI bindings install via ReactInstanceEventListener,
  SharedBridge in both main and background runtimes
- build.gradle: Add externalNativeBuild with CMake, prefab for fbjni/jsi
- SharedBridge.h: Make constructor public for std::make_shared compatibility
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 8 additional findings in Devin Review.

Open in Devin Review

Comment on lines +92 to +97
object : JSBundleLoader() {
override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
delegate.loadScriptFromFile(entryURL, entryURL, false)
return entryURL
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Android dev-mode bundle loading passes HTTP URL as local file path to loadScriptFromFile

In BackgroundThreadModule.kt:93-94, when the entryURL starts with "http", the code calls delegate.loadScriptFromFile(entryURL, entryURL, false). The first parameter of loadScriptFromFile is the local filesystem path to read the script from, not a URL. Passing an HTTP URL (e.g. http://localhost:8082/...) as the file path will cause the native layer to attempt to open it as a local file, which will fail. React Native's own createRemoteDebuggerBundleLoader passes null as the file-name parameter for remote loading, not the URL. This breaks starting the background runtime in development mode on Android.

Prompt for agents
In native-modules/react-native-background-thread/android/src/main/java/com/backgroundthread/BackgroundThreadModule.kt, lines 90-97, the custom JSBundleLoader for HTTP URLs incorrectly passes the URL as the first argument (file path) to delegate.loadScriptFromFile(). The first parameter should be a local file path or null, not an HTTP URL. Replace the custom JSBundleLoader with JSBundleLoader.createRemoteDebuggerBundleLoader(entryURL, entryURL) which correctly passes null as the file name for remote loading, or use a network-aware loader that first downloads the bundle to a cache file and then loads from there.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant