@onekeyfe/react-native-background-thread 1.1.47 → 1.1.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  package com.backgroundthread
2
2
 
3
+ import android.net.Uri
3
4
  import com.facebook.react.ReactPackage
4
5
  import com.facebook.proguard.annotations.DoNotStrip
5
6
  import com.facebook.react.ReactInstanceEventListener
@@ -14,6 +15,7 @@ import com.facebook.react.fabric.ComponentFactory
14
15
  import com.facebook.react.runtime.ReactHostImpl
15
16
  import com.facebook.react.runtime.hermes.HermesInstance
16
17
  import com.facebook.react.shell.MainReactPackage
18
+ import java.io.File
17
19
 
18
20
  /**
19
21
  * Singleton manager for the background React Native runtime.
@@ -92,13 +94,61 @@ class BackgroundThreadManager private constructor() {
92
94
 
93
95
  // ── Background runner lifecycle ─────────────────────────────────────────
94
96
 
97
+ private fun isRemoteBundleUrl(entryURL: String): Boolean {
98
+ return entryURL.startsWith("http://") || entryURL.startsWith("https://")
99
+ }
100
+
101
+ private fun resolveLocalBundlePath(entryURL: String): String? {
102
+ if (entryURL.startsWith("file://")) {
103
+ return Uri.parse(entryURL).path
104
+ }
105
+ if (entryURL.startsWith("/")) {
106
+ return entryURL
107
+ }
108
+ return null
109
+ }
110
+
111
+ private fun createDownloadedBundleLoader(appContext: android.content.Context, entryURL: String): JSBundleLoader {
112
+ return object : JSBundleLoader() {
113
+ override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
114
+ val tempFile = File(appContext.cacheDir, "background.bundle")
115
+ try {
116
+ java.net.URL(entryURL).openStream().use { input ->
117
+ tempFile.outputStream().use { output ->
118
+ input.copyTo(output)
119
+ }
120
+ }
121
+ BTLogger.info("Background bundle downloaded to ${tempFile.absolutePath}")
122
+ } catch (e: Exception) {
123
+ BTLogger.error("Failed to download background bundle: ${e.message}")
124
+ throw RuntimeException("Failed to download background bundle from $entryURL", e)
125
+ }
126
+ delegate.loadScriptFromFile(tempFile.absolutePath, entryURL, false)
127
+ return entryURL
128
+ }
129
+ }
130
+ }
131
+
132
+ private fun createLocalFileBundleLoader(localPath: String, sourceURL: String): JSBundleLoader {
133
+ return object : JSBundleLoader() {
134
+ override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
135
+ val bundleFile = File(localPath)
136
+ if (!bundleFile.exists()) {
137
+ BTLogger.error("Background bundle file does not exist: $localPath")
138
+ throw RuntimeException("Background bundle file does not exist: $localPath")
139
+ }
140
+ delegate.loadScriptFromFile(bundleFile.absolutePath, sourceURL, false)
141
+ return sourceURL
142
+ }
143
+ }
144
+ }
145
+
95
146
  @OptIn(UnstableReactNativeAPI::class)
96
147
  fun startBackgroundRunnerWithEntryURL(context: ReactApplicationContext, entryURL: String) {
97
148
  if (isStarted) {
98
149
  BTLogger.warn("Background runner already started")
99
150
  return
100
151
  }
101
- isStarted = true
102
152
  BTLogger.info("Starting background runner with entryURL: $entryURL")
103
153
 
104
154
  val appContext = context.applicationContext
@@ -106,34 +156,18 @@ class BackgroundThreadManager private constructor() {
106
156
  if (reactPackages.isNotEmpty()) {
107
157
  reactPackages
108
158
  } else {
109
- BTLogger.warn("No ReactPackages registered for background runtime; falling back to MainReactPackage only")
159
+ BTLogger.warn("No ReactPackages registered for background runtime; call setReactPackages(...) from host before start. Falling back to MainReactPackage only.")
110
160
  listOf(MainReactPackage())
111
161
  }
112
162
 
113
- val bundleLoader = if (entryURL.startsWith("http")) {
114
- // Dev server: download bundle to temp file first, then load from file.
115
- // loadScriptFromFile only accepts local file paths, not HTTP URLs.
116
- object : JSBundleLoader() {
117
- override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
118
- val tempFile = java.io.File(appContext.cacheDir, "background.bundle")
119
- try {
120
- java.net.URL(entryURL).openStream().use { input ->
121
- tempFile.outputStream().use { output ->
122
- input.copyTo(output)
123
- }
124
- }
125
- BTLogger.info("Background bundle downloaded to ${tempFile.absolutePath}")
126
- } catch (e: Exception) {
127
- BTLogger.error("Failed to download background bundle: ${e.message}")
128
- throw RuntimeException("Failed to download background bundle from $entryURL", e)
129
- }
130
- delegate.loadScriptFromFile(tempFile.absolutePath, entryURL, false)
131
- return entryURL
132
- }
163
+ val localBundlePath = resolveLocalBundlePath(entryURL)
164
+ val bundleLoader =
165
+ when {
166
+ isRemoteBundleUrl(entryURL) -> createDownloadedBundleLoader(appContext, entryURL)
167
+ localBundlePath != null -> createLocalFileBundleLoader(localBundlePath, entryURL)
168
+ entryURL.startsWith("assets://") -> JSBundleLoader.createAssetLoader(appContext, entryURL, true)
169
+ else -> JSBundleLoader.createAssetLoader(appContext, "assets://$entryURL", true)
133
170
  }
134
- } else {
135
- JSBundleLoader.createAssetLoader(appContext, "assets://$entryURL", true)
136
- }
137
171
 
138
172
  val delegate = DefaultReactHostDelegate(
139
173
  jsMainModulePath = MODULE_NAME,
@@ -177,6 +211,7 @@ class BackgroundThreadManager private constructor() {
177
211
  })
178
212
 
179
213
  host.start()
214
+ isStarted = true
180
215
  }
181
216
 
182
217
  /**
@@ -71,6 +71,51 @@ static void invokeOptionalGlobalFunction(jsi::Runtime &runtime, const char *name
71
71
  }
72
72
  }
73
73
 
74
+ static NSURL *resolveMainBundleResourceURL(NSString *resourceName)
75
+ {
76
+ if (resourceName.length == 0) {
77
+ return nil;
78
+ }
79
+
80
+ NSURL *directURL = [[NSBundle mainBundle] URLForResource:resourceName withExtension:nil];
81
+ if (directURL) {
82
+ return directURL;
83
+ }
84
+
85
+ NSString *normalizedName = [resourceName hasPrefix:@"/"]
86
+ ? resourceName.lastPathComponent
87
+ : resourceName;
88
+ NSString *extension = normalizedName.pathExtension;
89
+ NSString *baseName = normalizedName.stringByDeletingPathExtension;
90
+ if (baseName.length == 0) {
91
+ return nil;
92
+ }
93
+
94
+ return [[NSBundle mainBundle] URLForResource:baseName
95
+ withExtension:extension.length > 0 ? extension : nil];
96
+ }
97
+
98
+ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
99
+ {
100
+ if (jsBundleSourceNS.length == 0) {
101
+ return nil;
102
+ }
103
+
104
+ NSURL *parsedURL = [NSURL URLWithString:jsBundleSourceNS];
105
+ if (parsedURL.scheme.length > 0) {
106
+ if (parsedURL.isFileURL && parsedURL.path.length > 0) {
107
+ return [NSURL fileURLWithPath:parsedURL.path];
108
+ }
109
+ return parsedURL;
110
+ }
111
+
112
+ if ([jsBundleSourceNS hasPrefix:@"/"]) {
113
+ return [NSURL fileURLWithPath:jsBundleSourceNS];
114
+ }
115
+
116
+ return resolveMainBundleResourceURL(jsBundleSourceNS);
117
+ }
118
+
74
119
  @interface BackgroundReactNativeDelegate () {
75
120
  RCTInstance *_rctInstance;
76
121
  std::string _origin;
@@ -127,17 +172,19 @@ static void invokeOptionalGlobalFunction(jsi::Runtime &runtime, const char *name
127
172
  {
128
173
  if (!_jsBundleSource.empty()) {
129
174
  NSString *jsBundleSourceNS = [NSString stringWithUTF8String:_jsBundleSource.c_str()];
130
- NSURL *url = [NSURL URLWithString:jsBundleSourceNS];
131
- if (url && url.scheme) {
132
- return url;
175
+ NSURL *resolvedURL = resolveBundleSourceURL(jsBundleSourceNS);
176
+ if (resolvedURL) {
177
+ return resolvedURL;
133
178
  }
134
179
 
135
- if ([jsBundleSourceNS hasSuffix:@".jsbundle"]) {
136
- return [[NSBundle mainBundle] URLForResource:jsBundleSourceNS withExtension:nil];
137
- }
180
+ [BTLogger warn:[NSString stringWithFormat:@"Unable to resolve custom jsBundleSource=%@", jsBundleSourceNS]];
138
181
  }
139
182
 
140
- return [[NSBundle mainBundle] URLForResource: @"background" withExtension: @"bundle"];
183
+ NSURL *defaultBundleURL = resolveMainBundleResourceURL(@"background.bundle");
184
+ if (defaultBundleURL) {
185
+ return defaultBundleURL;
186
+ }
187
+ return [[NSBundle mainBundle] URLForResource:@"background" withExtension:@"bundle"];
141
188
  }
142
189
 
143
190
  - (void)hostDidStart:(RCTHost *)host
@@ -107,9 +107,7 @@ static NSString *const MODULE_DEBUG_URL = @"http://localhost:8082/apps/mobile/ba
107
107
  self.reactNativeFactoryDelegate = [[BackgroundReactNativeDelegate alloc] init];
108
108
  self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self.reactNativeFactoryDelegate];
109
109
 
110
- #if DEBUG
111
- [self.reactNativeFactoryDelegate setJsBundleSource:std::string([entryURL UTF8String])];
112
- #endif
110
+ [self.reactNativeFactoryDelegate setJsBundleSource:std::string([entryURL UTF8String])];
113
111
 
114
112
  [self.reactNativeFactory.rootViewFactory viewWithModuleName:MODULE_NAME
115
113
  initialProperties:initialProperties
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-background-thread",
3
- "version": "1.1.47",
3
+ "version": "1.1.48",
4
4
  "description": "react-native-background-thread",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",