Skip to content

Releases: reactnativecn/react-native-update

v10.55.1

Choose a tag to compare

@sunnylqm sunnylqm released this 31 Aug 12:30
b15223c

English

Bug fixes

  • iOS: switchVersion / restartApp did not restart the app in use_frameworks! builds on the New Architecture. The reload path probed RCTReloadCommand.h with a quoted __has_include, which only resolves in static-library builds. Under use_frameworks! (common in hybrid apps pulled into dynamic frameworks by Swift pods) the probe failed, and the compiled fallback [self.bridge reload] is a silent no-op in bridgeless mode — switchVersion resolved successfully but the app never reloaded, so the update only took effect after the user manually killed and relaunched the app. The probe now falls back to <React/RCTReloadCommand.h> and both build styles trigger RCTTriggerReloadCommandListeners. (89c638e)
  • Development environment: no more fabricated download success when there is nothing to download. In dev, when the check response carries no full-package URL, downloadUpdate used to report downloadSuccess and return the target hash without installing anything, steering app code into switchVersion, which then failed with SWITCH_VERSION_FAILED: Bundle version … not found.. It now logs the development-environment guidance and returns undefined, so nothing downstream treats the no-op as a real update. (9ad95fb)

Upgrading is recommended for all iOS apps built with use_frameworks! — on affected setups hot updates previously only applied after a manual app relaunch.

中文

问题修复

  • iOS:新架构下 use_frameworks! 构建的应用,switchVersion / restartApp 不会触发重启。 重启路径此前用引号形式的 __has_include 探测 RCTReloadCommand.h,该形式只在静态链接构建下可解析;在 use_frameworks!(混编应用常因 Swift pod 被迫开启动态框架)下探测失败,编入的兜底 [self.bridge reload] 在 bridgeless 模式下是静默空操作——switchVersion 正常 resolve 但应用不会重载,更新只能在用户手动杀掉进程重开后生效。现探测失败时回退到 <React/RCTReloadCommand.h>,两种构建方式均会触发 RCTTriggerReloadCommandListeners。(89c638e)
  • 开发环境:无内容可下载时不再伪造下载成功。 开发环境下,当检查结果中没有全量包 URL 时,downloadUpdate 此前会在未安装任何内容的情况下上报 downloadSuccess 并返回目标 hash,引导业务代码调用 switchVersion,进而报 SWITCH_VERSION_FAILED: Bundle version … not found.。现改为打印开发环境提示并返回 undefined,下游不会再把这次空操作当成真实更新。(9ad95fb)

建议所有使用 use_frameworks! 构建的 iOS 应用升级——受影响的集成方式下,此前热更新只能在手动重启应用后生效。

v10.55.0

Choose a tag to compare

@sunnylqm sunnylqm released this 31 Aug 07:07
8251452

中文

  • 默认开启 JavaScript 未捕获错误上报,并关联当前热更版本。
  • 新增 captureException 手动上报和 disableErrorReporting 显式退出。
  • 上报尽力而为,保留并链式调用 Sentry、Crashlytics 等既有全局处理器。

English

  • Enable uncaught JavaScript error reporting by default and associate reports with the active OTA release.
  • Add captureException for manual reports and disableErrorReporting for explicit opt-out.
  • Keep delivery best-effort and chain existing Sentry, Crashlytics, and other global handlers.

v10.54.0

Choose a tag to compare

@sunnylqm sunnylqm released this 30 Aug 03:26
9c6f73e

English

Security hardening batch (CODE_AUDIT §13): blocks HTTPS→HTTP downgrade; uses a two-phase install with digest-bearing completion records; scopes check deduplication by fingerprint; supports multi-mirror download fallback; persists state transactionally; enforces archive resource limits and a strict check-response schema; and adds markJsCheckCompleted, the autoMarkSuccessDelayMs / healthCheck / testChannel options, getUpdateMetadata with Sentry and Crashlytics helpers, nested checkUpdate extra data, and currentBundleSha256. The Expo postinstall script was removed, and published package files are now constrained by a whitelist.

中文

本版本集中完成安全加固(CODE_AUDIT §13):禁止 HTTPS→HTTP 降级;采用两阶段安装并在完成记录中携带摘要;按 fingerprint 隔离检查去重;支持多镜像下载回退;以事务方式持久化状态;限制归档资源用量并严格校验检查响应 schema;同时新增 markJsCheckCompletedautoMarkSuccessDelayMs / healthCheck / testChannel 选项、getUpdateMetadata 及 Sentry/Crashlytics 辅助方法、嵌套的 checkUpdate extra 数据和 currentBundleSha256。此外移除了 Expo postinstall 脚本,并通过白名单限制 npm 包中发布的文件。

v10.53.1

Choose a tag to compare

@sunnylqm sunnylqm released this 20 Aug 04:51
a0589a6

English

Hardening follow-up to 10.53.0's PackageInstaller migration. No behaviour change on the healthy path, and no API change.

The APK install path can no longer throw into the bridge

A native module method runs on the native modules thread: a reject there is an ordinary error return, but a throw reaches React Native's exception handler and takes the app down. The 10.53.0 migration left three spots on downloadAndInstallApk able to throw instead of reject:

  • declaresInstallPermission() caught only NameNotFoundException, so any other PackageManager failure escaped;
  • install() acquired the PackageInstaller and built its SessionParams outside the try;
  • downloadAndInstallApk() read its options (NoSuchKeyException on the new architecture) and enqueued the download unguarded.

Every failure of this path now comes back as an APK_INSTALL_PERMISSION_REQUIRED or APK_INSTALL_FAILED rejection.

To be explicit about the question this came from: a missing REQUEST_INSTALL_PACKAGES declaration never crashed — 10.53.0 already rejected before starting the download, and it deliberately checks the manifest before calling canRequestPackageInstalls(), which itself throws SecurityException when the permission is not declared. Reporting is unchanged here: an incomplete integration still fails loudly and early, with no fallback path.

One contradictory outcome removed

If a session was committed successfully and only session.close() then failed, install() rejected the promise even though the system had already taken the install — while the status receiver was about to report the real outcome. The catch now settles nothing once the session is committed; from that point the receiver owns the result.

Verified by a standalone compile of the Android sources; the change is defensive only.

中文

针对 10.53.0 PackageInstaller 迁移的加固版本。正常路径行为不变,无 API 变更。

APK 安装路径不再可能把异常抛进 bridge

原生模块方法跑在 native modules 线程上:在那里 reject 是正常的错误返回,而 throw 会进入 React Native 的异常处理器、直接带崩应用。10.53.0 的迁移在 downloadAndInstallApk 上留了三处可能 throw 而非 reject 的地方:

  • declaresInstallPermission() 只接了 NameNotFoundException,PackageManager 的其他失败会逃逸;
  • install() 获取 PackageInstaller 与构造 SessionParams 的几行在 try 之外;
  • downloadAndInstallApk() 读取参数(新架构下可能抛 NoSuchKeyException)和入队下载没有保护。

现在这条路径的任何失败都会以 APK_INSTALL_PERMISSION_REQUIREDAPK_INSTALL_FAILED 的 promise 拒绝返回。

针对本次的起因明确一句:未声明 REQUEST_INSTALL_PACKAGES 从来不会崩溃——10.53.0 本就在开始下载前直接 reject,并且刻意先检查清单再调用 canRequestPackageInstalls()(该方法在权限未声明时自身会抛 SecurityException)。报错行为也没有变化:接入不完整依旧是提前、明确地报错,不做任何降级兜底。

移除一处矛盾结论

若会话已 commit 成功、仅 session.close() 报错,install() 之前仍会 reject——而此时系统已经接手安装,状态接收器正准备报告真实结果。现在 commit 之后 catch 不再 settle promise,结果一律由接收器给出。

已通过 Android 源码的独立编译验证;本次改动纯属防御性加固。

v10.53.0

Choose a tag to compare

@sunnylqm sunnylqm released this 20 Aug 04:24

English

Android full-package install moves to PackageInstaller

downloadAndInstallApk no longer hands the APK to the system through an ACTION_INSTALL_PACKAGE / ACTION_VIEW intent and a bundled FileProvider. It now stages the file into a PackageInstaller session and commits it, which changes three things that mattered in practice:

  • The promise reflects the real install outcome. Previously it resolved the moment the intent was fired — a rejected or failed install looked exactly like a successful one. The session now reports back through a status receiver: it resolves once the system installer takes over (confirmation screen shown, or the install succeeds outright) and rejects with the installer's own failure message when the session cannot be created, written, committed, or continued.
  • No storage permission, no public Downloads staging. The pre-API-24 path used to write pushy_update.apk into the shared Downloads directory and ask for WRITE_EXTERNAL_STORAGE from JS first. Both are gone — the APK is always downloaded to the app's private directory and read from there. You can drop WRITE_EXTERNAL_STORAGE from your manifest if it was only there for us.
  • The library's FileProvider is gone. PushyFileProvider, the ${applicationId}.pushy.fileprovider authority and pushy_file_paths.xml were removed from the library manifest; a non-exported PackageInstallerStatusReceiver replaces them. One less provider merged into your app, and one less source of authority collisions.

The session also declares an originating URI/UID and INSTALL_REASON_USER, and on Android 12+ explicitly requests USER_ACTION_REQUIRED, so the system prompt behaves consistently instead of depending on OEM intent handling. Failed sessions are abandoned rather than left dangling.

Action required if you use downloadAndInstallApk

Declare the install permission in your app's AndroidManifest.xml:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

On Android 8.0+ the call now pre-flights before downloading anything:

  • permission not declared → rejects immediately with APK_INSTALL_PERMISSION_REQUIRED and a message telling you to declare it (no silent fallback — a misconfigured integration fails loudly instead of downloading an APK it can never install);
  • declared but not yet trusted by the user → opens the "install unknown apps" settings screen and rejects with the same code, so you can retry after the user grants it.

Hot updates (JS bundle) are unaffected — this path only runs when you ship a full new APK.

New error codes

APK_INSTALL_PERMISSION_REQUIRED and APK_INSTALL_FAILED are added to UpdateErrorCode (and to the Android / C++ code tables). STORAGE_PERMISSION_REJECTED / STORAGE_PERMISSION_ERROR remain in the type for compatibility but are no longer emitted.

Build floor

The library's Gradle defaults move up: minSdkVersion 16 → 21 (PackageInstaller requires API 21), compileSdkVersion 28 → 31, buildToolsVersion 31.0.0. These are safeExtGet defaults — if your root build.gradle sets ext.minSdkVersion / compileSdkVersion (every current RN template does), nothing changes for you.

No iOS or HarmonyOS changes in this release.

中文

Android 整包安装迁移到 PackageInstaller

downloadAndInstallApk 不再通过 ACTION_INSTALL_PACKAGE / ACTION_VIEW intent 加内置 FileProvider 把 APK 交给系统,而是把文件写入 PackageInstaller 会话并提交。三个实际差别:

  • Promise 真实反映安装结果。 以前 intent 一发出就 resolve——用户取消或安装失败,和成功完全无法区分。现在会话通过状态接收器回报:系统安装器接手(弹出确认页,或直接安装成功)时 resolve;会话创建、写入、提交或后续流程失败时,带上安装器自己的失败信息 reject。
  • 不再需要存储权限,也不再落公共 Downloads 目录。 旧的 API 24 以下路径会把 pushy_update.apk 写进共享 Downloads 目录,并先从 JS 申请 WRITE_EXTERNAL_STORAGE。两者都已移除——APK 一律下载到应用私有目录并从那里读取。如果你的清单里的 WRITE_EXTERNAL_STORAGE 只是为我们加的,现在可以删掉。
  • 库自带的 FileProvider 已移除。 PushyFileProvider${applicationId}.pushy.fileprovider authority 和 pushy_file_paths.xml 都从库清单中删除,取而代之的是一个不导出的 PackageInstallerStatusReceiver。合并进你 App 的 provider 少了一个,authority 冲突的来源也少了一个。

会话还会声明来源 URI/UID 与 INSTALL_REASON_USER,并在 Android 12+ 显式请求 USER_ACTION_REQUIRED,系统弹窗行为因此保持一致,不再取决于厂商对 intent 的处理。失败的会话会被 abandon,不会残留。

使用 downloadAndInstallApk 的必须改动

在 App 的 AndroidManifest.xml 中声明安装权限:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

Android 8.0+ 上,该调用现在会在下载之前先做前置检查:

  • 未声明权限 → 立即以 APK_INSTALL_PERMISSION_REQUIRED reject,并提示补声明(不做静默降级——接入不完整就直接报错,而不是下载一个根本装不上的 APK);
  • 已声明但用户尚未授权 → 拉起"安装未知应用"设置页,并以同一错误码 reject,便于你在用户授权后重试。

热更新(JS bundle)不受影响——这条路径只在你要下发整包新 APK 时才走。

新增错误码

UpdateErrorCode(以及 Android / C++ 错误码表)新增 APK_INSTALL_PERMISSION_REQUIREDAPK_INSTALL_FAILEDSTORAGE_PERMISSION_REJECTED / STORAGE_PERMISSION_ERROR 为兼容保留在类型中,但已不再抛出。

构建下限

库的 Gradle 默认值上调:minSdkVersion 16 → 21(PackageInstaller 要求 API 21)、compileSdkVersion 28 → 31、buildToolsVersion 31.0.0。这些都是 safeExtGet 的默认值——只要你的根 build.gradle 设了 ext.minSdkVersion / compileSdkVersion(当前所有 RN 模板都设了),对你没有任何影响。

本版本无 iOS 与 HarmonyOS 变更。

v10.52.3

Choose a tag to compare

@sunnylqm sunnylqm released this 15 Aug 10:17
2b4e2d9

English

v10.52.2 was tagged but never reached npm — its publish run failed in prepack, so v10.52.3 supersedes it and also carries its content. The previous version on npm is v10.52.1.

Fix: type errors on React Native 0.87+

The package ships its TypeScript sources, so React Native's own typings are type-checked against them in every consumer project. Three of ours broke on React Native 0.87's generated types (types_generated/, used under moduleResolution: "bundler"):

  • NativeEventEmitter / DeviceEventEmitter are now generic and type listeners as (...args: readonly Object[]) => unknown, which a (data: ProgressData) => void callback is not assignable to. The download-progress listeners now take the raw event and narrow it internally.
  • PermissionsAndroidStatic is no longer exported; the web/non-native shim derives the shape from the exported value instead (typeof PermissionsAndroid).
  • Linking.getInitialURL() widened to string | null | undefined, so parseLinking accepts an optional URL.

All three spellings compile on both old and new React Native typings — verified against React Native 0.73 and 0.87 — so the minimum React Native version does not change.

Pushy: additional API endpoints

1-4.rnupdate.online are added to the Pushy preset's main endpoints, alongside update.react-native.cn and update.reactnative.cn.

No native Android, iOS, or HarmonyOS changes are included in this release.

中文

v10.52.2 虽已创建 tag,但从未发布到 npm——其发布流程在 prepack 阶段失败,因此 v10.52.3 取代该版本并包含其全部内容。此前 npm 上的最新版本为 v10.52.1

修复 React Native 0.87+ 类型错误

本包会发布 TypeScript 源码,因此每个使用方项目都会使用 React Native 自身的类型定义对其进行类型检查。React Native 0.87 生成的新类型(types_generated/,在 moduleResolution: "bundler" 下使用)使以下三处代码无法通过检查:

  • NativeEventEmitter / DeviceEventEmitter 现在是泛型,并将监听器定义为 (...args: readonly Object[]) => unknown,因此 (data: ProgressData) => void 回调无法直接赋值。下载进度监听器现在会接收原始事件,并在内部收窄类型。
  • PermissionsAndroidStatic 不再导出;Web/非原生 shim 改为从导出的值推导类型(typeof PermissionsAndroid)。
  • Linking.getInitialURL() 的返回类型扩大为 string | null | undefined,因此 parseLinking 现在接受可选 URL。

以上三处写法均已在 React Native 0.73 与 0.87 的类型定义下验证通过,因此最低 React Native 版本不变。

Pushy:新增 API 端点

Pushy preset 的主要端点新增 1-4.rnupdate.online,与 update.react-native.cnupdate.reactnative.cn 一同使用。

本版本不包含 Android、iOS 或 HarmonyOS 原生代码变更。

v10.52.2

Choose a tag to compare

@sunnylqm sunnylqm released this 13 Aug 09:33
132b3db

English

Added additional API endpoints to the Pushy preset.

Note: This tag never reached npm because its publish run failed in prepack. It is superseded by v10.52.3, which includes all of its changes.

中文

为 Pushy preset 新增更多 API 端点。

注意: 此 tag 的发布流程在 prepack 阶段失败,因此从未发布到 npm。它已由包含其全部变更的 v10.52.3 取代。

v10.52.1

Choose a tag to compare

@sunnylqm sunnylqm released this 13 Aug 00:16
7739748

v10.52.1

Fix: the native cold-start check never armed on HarmonyOS

HarmonyOS only. Android and iOS builds are identical to 10.52.0.

RNOH exposes an ArkTS TurboModule to JS exclusively through a C++ method table (PushyTurboModule.cpp), and that table was never taught the methods added since 10.50: syncNativeConfig, getNativeCheckCache, getBundleHash, resetToPackagedBundle. On the JS side they were undefined, so the SDK's old-native feature detection silently treated the capability as absent. The consequences on HarmonyOS in 10.51.0–10.52.0:

  • the native cold-start check never ran (its config was never persisted), which also made 10.52.0's resumable downloads and zero-delay retry dead code there;
  • resetToPackagedBundle() was unusable from JS.

This release registers the missing methods, with a warning comment making the rule explicit: every new spec method must also be registered in the C++ glue.

Verified end-to-end on the DevEco simulator, which also settled an open question in our design doc: an uncaught JS error does not kill the process on RNOH — Hermes runs on RNOH-managed threads and the error only gets logged. The native check therefore completes its round even when JS dies on launch: in the experiment, a bundle that crashed 1s into every launch was replaced and the fix booted on the next launch. HarmonyOS needs no crash-moment hold (the mechanism 10.52.0 added for Android & iOS); its process model is naturally immune, and with this fix the recovery path is fully live there.

Why CI missed it: the HarmonyOS e2e suite never ran the native-check scenario, and the graceful old-native fallback hid the breakage. A HarmonyOS native-check e2e case is queued as follow-up.

HarmonyOS users on 10.51.0–10.52.0 should upgrade. Everything else is unchanged from 10.52.0.


修复:原生冷启动检测在鸿蒙上从未生效

仅鸿蒙。Android 与 iOS 构建与 10.52.0 完全一致。

RNOH 的 ArkTS TurboModule 只能通过 C++ 方法表(PushyTurboModule.cpp)暴露给 JS,而该表从未注册 10.50 起新增的方法:syncNativeConfiggetNativeCheckCachegetBundleHashresetToPackagedBundle。JS 侧拿到的是 undefined,SDK 的老原生特性检测于是静默跳过。10.51.0–10.52.0 期间在鸿蒙上的后果:

  • 原生冷启动检测从未运行(配置从未落盘),10.52.0 新增的断点续传与零延迟重试在鸿蒙也因此是死代码;
  • resetToPackagedBundle() 在 JS 侧不可用。

本版补全注册,并以醒目注释固化规则:每个新增的 spec 方法都必须同步注册进 C++ 胶水层。

已在 DevEco 模拟器上端到端验证,同时也解决了设计文档里一个悬而未决的问题:RNOH 下未捕获的 JS 错误不会杀死进程——Hermes 跑在 RNOH 自管线程上,错误只会被记录。因此即使 JS 启动即崩,原生检测也照常完成整轮:实验中一个每次启动 1 秒即崩的 bundle 被替换,下次启动直接进入修复版。鸿蒙不需要 10.52.0 为 Android/iOS 引入的崩溃时刻扣留机制——其进程模型天然免疫,补上本修复后救援链路在鸿蒙完整生效。

CI 未能发现的原因:鸿蒙 e2e 套件从未运行 native-check 场景,而兼容老原生的优雅降级把断裂藏住了。补充鸿蒙 native-check e2e 用例已列入后续。

10.51.0–10.52.0 的鸿蒙用户请升级。其余与 10.52.0 一致。

v10.52.0

Choose a tag to compare

@sunnylqm sunnylqm released this 12 Aug 14:39
2d7f2b3

v10.52.0

Crash-moment rescue + resumable downloads: crash-on-launch bricks are now recoverable

10.51 introduced the native cold-start check: a device bricked by a bad hot update pulls the fix on its next launch, natively, without JS. Live testing found its boundary — it rescued a version that crashed 10 seconds into the session, but not one that crashed ~230ms into every launch: the check needs a few seconds, the JS crash kills the whole process, and that round's download progress was thrown away every time. No number of restarts converged.

This release closes that gap with two mechanisms (design: NATIVE_CHECKUPDATE_DESIGN.md §11):

Crash-moment rescue (Android & iOS)

When the app dies of an uncaught JS error during startup, the process is still alive for a moment — and JS will never run again. The SDK now uses exactly that window: it briefly holds the dying process (hard-capped: ~10s on a background thread crash, ~3.5s on a main-thread crash) to finish the update check and download, then lets the crash proceed.

  • Zero false positives, zero cost on the healthy path: the crash itself is the trigger. No crash-loop counters, no startup blocking, nothing runs unless the app is actually dying within 60s of launch (or with a check round in flight).
  • The fix boots on the next launch, with no extra restart: it is committed at the end of the dying session. In the rescue window a downloaded fix is always activated — JS is no longer around to decide.
  • Crash reporters keep working: the SDK chains the previously installed handler (the same etiquette Sentry/Crashlytics/Bugly follow) and always hands the crash over afterwards, whether the rescue ran, timed out, or failed. The hold runs on a worker thread with a hard timeout, so it can only delay the process death, never prevent it.
  • Honest boundary: this catches exception-shaped crashes (the shape JS bricks take). Native signal crashes, ANRs and OOM kills are not covered. HarmonyOS does not get the hold in this release (its error observer cannot drive async network IO); it gets everything below.

Resumable downloads (all three platforms)

Update downloads now survive process death: partial files are kept with a sidecar recording what they belong to (URL, ETag/Last-Modified, total size) and resume via HTTP Range with If-Range validation — a changed server file falls back to a clean full transfer instead of appending mismatched bytes; a fully-received archive that never got unpacked is recognized and goes straight to unpacking. An archive that downloads fully but fails to unpack is classified as poisoned and dropped along with its sidecar, so a corrupt file can never trap the resume loop.

A launch that follows an interrupted round also skips the usual 5s startup delay and resumes immediately — repeated short-lived launches make monotonic progress instead of starting over.

Telemetry

A rescued version that survives to markSuccess reports a forceBootRescue / crashRescue receipt, so the dashboard can show which devices were actually pulled back.

Everything else is unchanged from 10.51.1. No API changes; no action needed beyond upgrading.


崩溃时刻救援 + 断点续传:「启动即崩」的砖现在也能救回来

10.51 引入了原生冷启动检测:被坏热更卡死的设备下次启动由原生侧拉回修复版,不依赖 JS。真机实测找到了它的边界——它救回了"启动 10 秒后才崩"的版本,却救不了"每次启动 230ms 即崩"的:检测需要数秒,JS 崩溃会杀掉整个进程,那一轮的下载进度全部作废,重启多少次都无法收敛。

本版用两个机制补上这个缺口(设计文档:NATIVE_CHECKUPDATE_DESIGN.md §11):

崩溃时刻救援(Android 与 iOS)

应用死于启动阶段的未捕获 JS 错误时,进程还有片刻存活——而 JS 已永远不会再跑。SDK 现在正是利用这个窗口:短暂扣住垂死的进程(硬上限:后台线程崩溃约 10s、主线程崩溃约 3.5s),把更新检查与下载做完,再放行崩溃流程。

  • 零误判、健康路径零成本:崩溃本身就是触发信号。没有崩溃计数器、不阻塞启动,只有应用真的在启动 60 秒内死亡(或有在途检查轮)时才会介入。
  • 修复版下次启动直接生效,无需额外重启:提交发生在垂死会话的末尾。救援窗口内下载到的修复版一律激活——JS 已经没有机会做决策了。
  • 崩溃上报不受影响:SDK 以链式方式保存并在结束后调用原有的崩溃处理器(与 Sentry/Crashlytics/Bugly 相同的惯例),无论救援完成、超时还是失败。扣留跑在带硬超时的工作线程上,只可能推迟进程死亡,不可能阻止它。
  • 诚实边界:覆盖 exception 形态的崩溃(JS 砖的典型形态);原生 signal 崩溃、ANR、OOM 击杀不在覆盖内。鸿蒙本版暂无扣留机制(其错误观察者无法驱动异步网络 IO),但以下能力全部具备。

断点续传(三端)

更新下载现在能在进程死亡后接着传:partial 文件连同 sidecar(记录所属 URL、ETag/Last-Modified、总长)一起保留,以 HTTP Range + If-Range 验证续传——服务端文件已变更时自动退回完整重传,绝不拼接不匹配的字节;已收齐但没来得及解压的归档会被识别并直接进入解压。下载完整但解压失败的归档判定为有毒,连 sidecar 一起删除,坏文件永远不会困住续传循环。

紧随中断轮次的下一次启动还会跳过常规的 5 秒延迟立即续传——反复的短命启动也能单调累积进度,而不是每次从零开始。

遥测

被救援送进来的版本活到 markSuccess 时会补报 forceBootRescue / crashRescue 回执,控制台由此可以看到哪些设备真的被捞回来了。

其余与 10.51.1 一致。无 API 变更,升级即可。

v10.51.1

Choose a tag to compare

@sunnylqm sunnylqm released this 12 Aug 12:04
c89ad36

Fix: Android build failure on RN 0.87 (AGP 9)

10.51.0 fails the very first build in projects created on or upgraded to RN 0.87:

A problem occurred configuring project ':react-native-update':
> Build Type debug contains custom resource values, but the feature is disabled.

AGP 9 flipped the default of the resValues build feature to off for library modules, while this library relies on resValue to write pushy_build_time. This release re-enables the feature in the library's gradle the way AGP's release notes recommend (guarded on AGP ≥ 4, so older projects configure exactly as before):

buildFeatures {
    resValues true
}

This idiom is not deprecated and is not on AGP 10's removal list — it is the long-term form. What AGP 10 removes are the android.newDsl / android.builtInKotlin transition opt-outs in the app template, which do not involve this library.

Verified end-to-end on RN 0.87.0 with an Android emulator against production: build, native package upload, hot update publish, binding-time dependency validation, native cold-start check (downloads, then correctly leaves activation to JS), JS short-circuiting onto the natively completed download, restart into the new bundle, markSuccess.

RN 0.87 does not change the Hermes bytecode format (HBC 98 since ~0.84), so hot update bundles and runtimes remain compatible.

Everything else is identical to 10.51.0. RN 0.87 users should upgrade straight to this version.


修复:RN 0.87(AGP 9)下无法构建 Android

10.51.0 在 RN 0.87 新建或升级的项目上会在第一次构建就失败:

A problem occurred configuring project ':react-native-update':
> Build Type debug contains custom resource values, but the feature is disabled.

原因是 AGP 9 把 library 模块的 resValues 特性默认值从开翻到关,而本库依赖 resValue 写入 pushy_build_time。本版在库的 gradle 中按官方推荐方式显式启用该特性(带 AGP ≥ 4 守卫,老项目行为不变):

buildFeatures {
    resValues true
}

该写法未被弃用、也不在 AGP 10 的移除清单上,可长期使用。AGP 10 将移除的是应用模板里的 android.newDsl / android.builtInKotlin 过渡开关,与本库无关。

已在 RN 0.87.0 + Android 模拟器上实测完整链路:构建、上传原生包、发布热更、绑定期依赖校验、原生冷启动检测(下载后将激活权正确留给 JS)、JS 复用原生已完成的下载、重启生效、markSuccess。

RN 0.87 未更改 Hermes 字节码格式(约 0.84 起均为 HBC 98),热更包与运行时无兼容性问题。

其余功能与 10.51.0 相同。RN 0.87 用户请直接升级到本版。