react-native-update 10.40.1 → 10.41.0
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.
- package/README-CN.md +3 -3
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +58 -2
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +17 -3
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +25 -3
- package/package.json +1 -1
- package/react-native-update-10.40.2.tgz +0 -0
- package/react-native-update-10.40.0.tgz +0 -0
package/README-CN.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
### 区域服务说明
|
|
8
8
|
|
|
9
|
-
- 中国区服务使用 **Pushy**(<https://pushy.reactnative.cn
|
|
10
|
-
- 全球区服务使用 **Cresc**(<https://cresc.dev>),由 **CHARMLOT PTE. LTD.**
|
|
11
|
-
-
|
|
9
|
+
- 中国区服务使用 **Pushy**(<https://pushy.reactnative.cn>),由**武汉青罗网络科技有限公司**运营,服务器位于中国境内,也通过 cloudflare 智能分流,完全支持海外用户高速访问。**使用人民币支付订阅**。
|
|
10
|
+
- 全球区服务使用 **Cresc**(<https://cresc.dev>),由 **CHARMLOT PTE. LTD.** 运营,服务器位于新加坡。**使用美元支付订阅**。
|
|
11
|
+
- 中国区与全球区服务由不同公司实体独立运营,服务器、数据及控制台系统彼此隔离。如果可以使用网银和支付宝结算,建议使用 Pushy,否则建议使用 Cresc。
|
|
12
12
|
|
|
13
13
|
**现已支持鸿蒙以及新架构**
|
|
14
14
|
|
|
@@ -33,15 +33,36 @@ final class BundledResourceCopier {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Holds the exact archive a CRC32 match came from, so the fallback copy
|
|
37
|
+
// reads from that archive even if another APK exposes the same entry name
|
|
38
|
+
// with different bytes.
|
|
39
|
+
private static final class ZipSource {
|
|
40
|
+
final ZipEntry entry;
|
|
41
|
+
final SafeZipFile zipFile;
|
|
42
|
+
|
|
43
|
+
ZipSource(ZipEntry entry, SafeZipFile zipFile) {
|
|
44
|
+
this.entry = entry;
|
|
45
|
+
this.zipFile = zipFile;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
BundledResourceCopier(Context context) {
|
|
37
50
|
this.context = context.getApplicationContext();
|
|
38
51
|
}
|
|
39
52
|
|
|
40
|
-
void copyFromResource(
|
|
53
|
+
void copyFromResource(
|
|
54
|
+
HashMap<String, ArrayList<File>> resToCopy,
|
|
55
|
+
HashMap<String, Long> crcByFrom
|
|
56
|
+
) throws IOException {
|
|
41
57
|
ArrayList<String> apkPaths = collectApkPaths();
|
|
42
58
|
HashMap<String, ZipEntry> availableEntries = new HashMap<String, ZipEntry>();
|
|
43
59
|
HashMap<String, SafeZipFile> zipFileMap = new HashMap<String, SafeZipFile>();
|
|
44
60
|
HashMap<String, SafeZipFile> entryToZipFileMap = new HashMap<String, SafeZipFile>();
|
|
61
|
+
// Content checksum index: CRC32 -> matched archive source. Lets us
|
|
62
|
+
// locate a file by content when its origin path is not present verbatim
|
|
63
|
+
// on device (e.g. APK baseline diff applied on an AAB/split-apk install
|
|
64
|
+
// whose res/ paths were shortened). First entry for a given crc wins.
|
|
65
|
+
HashMap<Long, ZipSource> crcToEntry = new HashMap<Long, ZipSource>();
|
|
45
66
|
|
|
46
67
|
try {
|
|
47
68
|
for (String apkPath : apkPaths) {
|
|
@@ -55,6 +76,10 @@ final class BundledResourceCopier {
|
|
|
55
76
|
availableEntries.put(entryName, ze);
|
|
56
77
|
entryToZipFileMap.put(entryName, zipFile);
|
|
57
78
|
}
|
|
79
|
+
long crc = ze.getCrc();
|
|
80
|
+
if (crc != -1L && !crcToEntry.containsKey(crc)) {
|
|
81
|
+
crcToEntry.put(crc, new ZipSource(ze, zipFile));
|
|
82
|
+
}
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
85
|
|
|
@@ -76,6 +101,7 @@ final class BundledResourceCopier {
|
|
|
76
101
|
|
|
77
102
|
ZipEntry entry = availableEntries.get(fromPath);
|
|
78
103
|
String actualSourcePath = fromPath;
|
|
104
|
+
SafeZipFile matchedZipFile = null;
|
|
79
105
|
ResolvedResourceSource resolvedResource = null;
|
|
80
106
|
|
|
81
107
|
if (entry == null) {
|
|
@@ -87,10 +113,35 @@ final class BundledResourceCopier {
|
|
|
87
113
|
}
|
|
88
114
|
}
|
|
89
115
|
|
|
116
|
+
// Content (CRC32) match: robust across APK/AAB packaging because
|
|
117
|
+
// the checksum is over the uncompressed file content, not its
|
|
118
|
+
// path. Preferred over the resource-id heuristic below.
|
|
119
|
+
if (entry == null && crcByFrom != null) {
|
|
120
|
+
Long wantedCrc = crcByFrom.get(fromPath);
|
|
121
|
+
if (wantedCrc != null) {
|
|
122
|
+
ZipSource matched = crcToEntry.get(wantedCrc);
|
|
123
|
+
if (matched != null) {
|
|
124
|
+
entry = matched.entry;
|
|
125
|
+
matchedZipFile = matched.zipFile;
|
|
126
|
+
actualSourcePath = matched.entry.getName();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
90
131
|
if (entry == null) {
|
|
91
132
|
resolvedResource = resolveBundledResource(fromPath);
|
|
92
133
|
if (resolvedResource != null) {
|
|
93
134
|
actualSourcePath = resolvedResource.assetPath;
|
|
135
|
+
// resolveBundledResource resolved the density-correct
|
|
136
|
+
// file path; copy that exact entry from the already-open
|
|
137
|
+
// archives so the right variant is used. (openRawResource
|
|
138
|
+
// would re-resolve the id at the current configuration
|
|
139
|
+
// density and ignore the requested one.)
|
|
140
|
+
ZipEntry resolvedEntry = availableEntries.get(actualSourcePath);
|
|
141
|
+
if (resolvedEntry != null) {
|
|
142
|
+
entry = resolvedEntry;
|
|
143
|
+
resolvedResource = null;
|
|
144
|
+
}
|
|
94
145
|
}
|
|
95
146
|
}
|
|
96
147
|
|
|
@@ -104,7 +155,9 @@ final class BundledResourceCopier {
|
|
|
104
155
|
if (lastTarget != null) {
|
|
105
156
|
UpdateFileUtils.copyFile(lastTarget, target);
|
|
106
157
|
} else if (entry != null) {
|
|
107
|
-
SafeZipFile sourceZipFile =
|
|
158
|
+
SafeZipFile sourceZipFile = matchedZipFile != null
|
|
159
|
+
? matchedZipFile
|
|
160
|
+
: entryToZipFileMap.get(actualSourcePath);
|
|
108
161
|
if (sourceZipFile == null) {
|
|
109
162
|
sourceZipFile = baseZipFile;
|
|
110
163
|
}
|
|
@@ -247,6 +300,9 @@ final class BundledResourceCopier {
|
|
|
247
300
|
}
|
|
248
301
|
|
|
249
302
|
private InputStream openResolvedResourceStream(ResolvedResourceSource source) throws IOException {
|
|
303
|
+
// Defensive fallback only: reached when the density-resolved assetPath
|
|
304
|
+
// is not present as a zip entry in any loaded APK. Best-effort, resolves
|
|
305
|
+
// at the current configuration density.
|
|
250
306
|
try {
|
|
251
307
|
return context.getResources().openRawResource(source.resourceId);
|
|
252
308
|
} catch (Resources.NotFoundException e) {
|
|
@@ -40,6 +40,11 @@ class DownloadTask implements Runnable {
|
|
|
40
40
|
final ArrayList<String> copyFroms = new ArrayList<String>();
|
|
41
41
|
final ArrayList<String> copyTos = new ArrayList<String>();
|
|
42
42
|
final ArrayList<String> deletes = new ArrayList<String>();
|
|
43
|
+
// Maps a copy source path ("from") to the CRC32 of the file content,
|
|
44
|
+
// when provided by the manifest ("copiesCrc"). Lets the resource
|
|
45
|
+
// copier locate the file by content if the path is not present on
|
|
46
|
+
// device (APK baseline -> AAB install path shortening).
|
|
47
|
+
final HashMap<String, Long> copyCrcs = new HashMap<String, Long>();
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
private final Context context;
|
|
@@ -140,8 +145,11 @@ class DownloadTask implements Runnable {
|
|
|
140
145
|
JSONObject manifest,
|
|
141
146
|
ArrayList<String> copyFroms,
|
|
142
147
|
ArrayList<String> copyTos,
|
|
143
|
-
ArrayList<String> deletes
|
|
148
|
+
ArrayList<String> deletes,
|
|
149
|
+
HashMap<String, Long> copyCrcs
|
|
144
150
|
) throws JSONException {
|
|
151
|
+
JSONObject copiesCrc = manifest.optJSONObject("copiesCrc");
|
|
152
|
+
|
|
145
153
|
JSONObject copies = manifest.optJSONObject("copies");
|
|
146
154
|
if (copies != null) {
|
|
147
155
|
Iterator<?> keys = copies.keys();
|
|
@@ -153,6 +161,11 @@ class DownloadTask implements Runnable {
|
|
|
153
161
|
}
|
|
154
162
|
copyFroms.add(from);
|
|
155
163
|
copyTos.add(to);
|
|
164
|
+
if (copiesCrc != null && copyCrcs != null && copiesCrc.has(to)) {
|
|
165
|
+
// Same content => same crc, so grouping multiple "to" under
|
|
166
|
+
// one "from" stays consistent.
|
|
167
|
+
copyCrcs.put(from, copiesCrc.getLong(to));
|
|
168
|
+
}
|
|
156
169
|
}
|
|
157
170
|
}
|
|
158
171
|
|
|
@@ -220,7 +233,8 @@ class DownloadTask implements Runnable {
|
|
|
220
233
|
manifest,
|
|
221
234
|
contents.copyFroms,
|
|
222
235
|
contents.copyTos,
|
|
223
|
-
contents.deletes
|
|
236
|
+
contents.deletes,
|
|
237
|
+
contents.copyCrcs
|
|
224
238
|
);
|
|
225
239
|
continue;
|
|
226
240
|
}
|
|
@@ -285,7 +299,7 @@ class DownloadTask implements Runnable {
|
|
|
285
299
|
originBundleFile.delete();
|
|
286
300
|
}
|
|
287
301
|
|
|
288
|
-
bundledResourceCopier.copyFromResource(copyList);
|
|
302
|
+
bundledResourceCopier.copyFromResource(copyList, contents.copyCrcs);
|
|
289
303
|
}
|
|
290
304
|
|
|
291
305
|
private void doPatchFromPpk() throws IOException, JSONException {
|
|
@@ -185,18 +185,26 @@ final class ReactReloadManager {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
private static boolean isNewArchitectureEnabled(Context application) {
|
|
188
|
+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
188
192
|
try {
|
|
189
193
|
Class<?> buildConfigClass = Class.forName(application.getPackageName() + ".BuildConfig");
|
|
190
194
|
Field newArchitectureField = buildConfigClass.getField("IS_NEW_ARCHITECTURE_ENABLED");
|
|
191
|
-
|
|
195
|
+
if (newArchitectureField.getBoolean(null)) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
192
198
|
} catch (Throwable ignored) {
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
if (application instanceof ReactApplication) {
|
|
196
202
|
try {
|
|
197
203
|
ReactNativeHost reactNativeHost = ((ReactApplication) application).getReactNativeHost();
|
|
198
|
-
Method isNewArchEnabledMethod =
|
|
199
|
-
reactNativeHost.getClass()
|
|
204
|
+
Method isNewArchEnabledMethod = getDeclaredMethodInHierarchy(
|
|
205
|
+
reactNativeHost.getClass(),
|
|
206
|
+
"isNewArchEnabled"
|
|
207
|
+
);
|
|
200
208
|
isNewArchEnabledMethod.setAccessible(true);
|
|
201
209
|
Object result = isNewArchEnabledMethod.invoke(reactNativeHost);
|
|
202
210
|
return result instanceof Boolean && (Boolean) result;
|
|
@@ -207,6 +215,20 @@ final class ReactReloadManager {
|
|
|
207
215
|
return false;
|
|
208
216
|
}
|
|
209
217
|
|
|
218
|
+
private static Method getDeclaredMethodInHierarchy(Class<?> clazz, String methodName)
|
|
219
|
+
throws NoSuchMethodException {
|
|
220
|
+
Class<?> current = clazz;
|
|
221
|
+
while (current != null) {
|
|
222
|
+
try {
|
|
223
|
+
return current.getDeclaredMethod(methodName);
|
|
224
|
+
} catch (NoSuchMethodException ignored) {
|
|
225
|
+
current = current.getSuperclass();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
throw new NoSuchMethodException(methodName);
|
|
230
|
+
}
|
|
231
|
+
|
|
210
232
|
private static void reloadReactHost(Object reactHost, JSBundleLoader loader) throws Throwable {
|
|
211
233
|
try {
|
|
212
234
|
Field devSupportField = getCompatibleField(reactHost.getClass(), "useDevSupport");
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|