Skip to content

feat(native): bounded prepareBundle on Android and HarmonyOS - #669

Merged
sunnylqm merged 1 commit into
masterfrom
claude/prepare-bundle-timeout
Oct 15, 2026
Merged

sunnylqm merged 1 commit into
masterfrom
claude/prepare-bundle-timeout

Conversation

@sunnylqm

@sunnylqm sunnylqm commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

架构审查里的一处宿主接口差异:三端中只有 iOS 允许宿主限定 prepareBundle 的等待时间。

改了什么

  • Android: 新增 PushyRuntime.prepareBundle(context, timeoutMillis, callback)。
    • 检测轮次改为在独立线程上运行,宿主只在等待一侧加时间上限,做法与 iOS 相同。超时返回 failed/timeout,轮次继续在后台运行,结果仍对下次启动生效。
    • 不传超时时最多等 30 分钟,与 iOS 的默认值一致。
    • 宿主等待改在一个按需扩缩的线程池上进行。以前所有宿主调用排在同一个单线程 worker 上,第二个调用者要等第一个调用者整段等待结束,自己设的超时就失效了。
  • HarmonyOS: prepareBundle(timeoutMs?) 让等待与计时器赛跑,超时后的语义与 Android 相同。不传超时时仍等到轮次结束,因为 ArkTS 是单线程的,这样的等待不会阻塞任何东西。
  • README: 中英文都写明了三端的超时入口和超时后的行为。

已有的无参调用保持原有接口和结果语义,只是 Android 不再无限等待。

验证

  • bun lint 通过,bun test 439 个用例全部通过。新增的鸿蒙用例验证了超时返回 failed/timeout、轮次不被中断,以及之后的调用能拿到轮次的结果。
  • Android:testReleaseUnitTest 编译了改动后的主代码,JVM 单测全部通过。Android 侧的超时没有专门的单测,因为 SyncCoordinator 的轮次状态是进程级静态量,现有 JVM 测试只覆盖 performAttempts。

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Summary by CodeRabbit

  • New Features
    • Bundle preparation on Android, iOS, and HarmonyOS now accepts an optional timeout. If it expires, the call returns a timeout result while preparation continues in the background; completed results can be used on the next launch.
    • Without a specified timeout, Android and iOS wait up to 30 minutes. HarmonyOS waits for the current preparation round to finish.
  • Documentation
    • Updated English and Chinese documentation with timeout options and behavior.

Only iOS let a host bound its wait (prepareBundle(timeout:completion:)).
Android waited on the round's latch without limit, and ran the round on the
caller's single host worker, so a second caller queued behind the first
caller's whole wait. HarmonyOS awaited the round without limit.

- Android: PushyRuntime.prepareBundle(context, timeoutMillis, callback). The
  round runs on its own thread (claimRound / runClaimedRound, as iOS), the
  caller waits at most the timeout and gets failed/timeout on expiry while
  the round carries on. Without a timeout it waits at most 30 minutes, the
  iOS default. Host waits run on a cached daemon pool, so one caller's wait
  never delays another's.
- HarmonyOS: prepareBundle(timeoutMs?) races the round against a timer;
  without one it keeps waiting for the round (nothing blocks on ArkTS).
- READMEs document the three timeout entry points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 08ff0210-9c0a-4d95-8d58-ef1375e2a40a
📥 Commits

Reviewing files that changed from the base of the PR and between 58d65ca and 2813456.

📒 Files selected for processing: 9
  • README-CN.md
  • README.md
  • android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java
  • android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java
  • android/src/main/java/cn/reactnative/modules/update/Threads.java
  • harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets
  • harmony/pushy/src/main/ets/SyncCoordinator.ts
  • src/__tests__/nativeHostApi.test.ts
  • src/__tests__/nativeHostApiNaming.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

Android and HarmonyOS bundle preparation now accept optional timeouts. When a wait expires, the API returns a failed/timeout result while the preparation round continues. Android and iOS default to a 30-minute wait; HarmonyOS waits for the round when no timeout is provided.

Changes: Bundle preparation timeout

Layer / File(s) Summary
Timeout API and contract
android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java, android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java, harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets, harmony/pushy/src/main/ets/SyncCoordinator.ts, README.md, README-CN.md
Android and HarmonyOS preparation APIs accept timeout values. Android uses a 30-minute default. Both READMEs describe timeout results, continued background processing, and platform-specific default waits.
Timeout execution and validation
android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java, android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java, android/src/main/java/cn/reactnative/modules/update/Threads.java, harmony/pushy/src/main/ets/SyncCoordinator.ts, src/__tests__/nativeHostApi.test.ts, src/__tests__/nativeHostApiNaming.test.ts
Android runs claimed rounds separately from caller waits and returns failed/timeout when the wait expires. HarmonyOS races the round against a timer when a timeout is supplied. Tests verify timeout results and that a later call can receive the completed round result.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant HostCaller
  participant PushyRuntime
  participant SyncCoordinator
  participant ClaimedRound
  HostCaller->>PushyRuntime: prepareBundle with timeoutMillis
  PushyRuntime->>SyncCoordinator: prepareBundle with timeoutMillis
  SyncCoordinator->>ClaimedRound: Start claimed round
  SyncCoordinator-->>PushyRuntime: Return failed/timeout if wait expires
  PushyRuntime-->>HostCaller: Return timeout result
  ClaimedRound-->>SyncCoordinator: Complete round in background
Loading
sequenceDiagram
  participant BundleProvider
  participant SyncCoordinator
  participant PreparationRound
  participant TimeoutTimer
  BundleProvider->>SyncCoordinator: prepareBundleNative with timeoutMs
  SyncCoordinator->>PreparationRound: Start round
  SyncCoordinator->>TimeoutTimer: Wait up to timeoutMs
  TimeoutTimer-->>SyncCoordinator: Timeout expires
  SyncCoordinator-->>BundleProvider: Return failed/timeout
  PreparationRound-->>SyncCoordinator: Complete round in background
Loading
Merge Risk: ⚪ Minimal · up to 28134

No merge-blocking issue is established for the timeout changes; merge after normal checks.

Pre-merge checks: Passed 5
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: bounded prepareBundle waiting on Android and HarmonyOS.
Docstring Coverage Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 6 files. (3 skipped: 3 …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sunnylqm
sunnylqm merged commit bf4522c into master Oct 15, 2026
14 checks passed
@sunnylqm
sunnylqm deleted the claude/prepare-bundle-timeout branch October 15, 2026 04:57
sunnylqm added a commit that referenced this pull request Oct 15, 2026
…gine

SyncCoordinator's hand-written round (runConfiguredRound, the endpoint query
loop, the two-budget performAttempts loop and the result mapping) becomes a
loop over FlowBridge.stepRound (new JNI binding over
flowcore::StepRoundJson) that runs the commands it returns: the breadcrumb,
the launch env, HTTP, the installed check, downloads and the commit.
Platform parts stay: the round thread and its claim, crash hold and the
pending-activation bookkeeping, the bounded host wait (#669), OkHttp with the
downgrade and body-size guards, the download executor and its cancel.

Aligned with the engine (NATIVE_CHECKUPDATE_DESIGN §12.4): request timeouts
are the engine's (idle 10s, whole call 15s, capped by the round deadline);
a commit lost to a config change finishes as cancelled/config_changed; a
cache-only commit that throws is failed/commit_failed instead of
internal_error.

librnpushy.so is rebuilt with NDK 28.2.13676358 for the new native method.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sunnylqm added a commit that referenced this pull request Oct 15, 2026
…gine (#676)

* refactor(android): drive the native round through the shared round engine

SyncCoordinator's hand-written round (runConfiguredRound, the endpoint query
loop, the two-budget performAttempts loop and the result mapping) becomes a
loop over FlowBridge.stepRound (new JNI binding over
flowcore::StepRoundJson) that runs the commands it returns: the breadcrumb,
the launch env, HTTP, the installed check, downloads and the commit.
Platform parts stay: the round thread and its claim, crash hold and the
pending-activation bookkeeping, the bounded host wait (#669), OkHttp with the
downgrade and body-size guards, the download executor and its cancel.

Aligned with the engine (NATIVE_CHECKUPDATE_DESIGN §12.4): request timeouts
are the engine's (idle 10s, whole call 15s, capped by the round deadline);
a commit lost to a config change finishes as cancelled/config_changed; a
cache-only commit that throws is failed/commit_failed instead of
internal_error.

librnpushy.so is rebuilt with NDK 28.2.13676358 for the new native method.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(android): treat only a zero hold deadline as none

System.nanoTime's origin is arbitrary, so a crash-hold deadline may be
negative; it must still cap the round.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant