@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;
|
|
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
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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 *
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
175
|
+
NSURL *resolvedURL = resolveBundleSourceURL(jsBundleSourceNS);
|
|
176
|
+
if (resolvedURL) {
|
|
177
|
+
return resolvedURL;
|
|
133
178
|
}
|
|
134
179
|
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|