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 CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  ### 区域服务说明
8
8
 
9
- - 中国区服务使用 **Pushy**(<https://pushy.reactnative.cn>),由**武汉青罗网络科技有限公司**运营,服务器与用户数据存放于中国境内,也通过 cloudflare 智能分流,完全支持海外用户高速访问。**使用人民币支付订阅**。
10
- - 全球区服务使用 **Cresc**(<https://cresc.dev>),由 **CHARMLOT PTE. LTD.** 运营,服务器与用户数据存放于新加坡。**使用美元支付订阅**。
11
- - 中国区与全球区服务由不同公司实体独立运营,服务器、数据存放位置及控制台系统彼此隔离。如果可以使用网银和支付宝结算,建议使用 Pushy,否则建议使用 Cresc。
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(HashMap<String, ArrayList<File>> resToCopy) throws IOException {
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 = entryToZipFileMap.get(actualSourcePath);
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
- return newArchitectureField.getBoolean(null);
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().getDeclaredMethod("isNewArchEnabled");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-update",
3
- "version": "10.40.1",
3
+ "version": "10.41.0",
4
4
  "description": "react-native hot update",
5
5
  "main": "src/index",
6
6
  "scripts": {
Binary file
Binary file