Skip to content

feat: add react-native-perf-stats native module#52

Merged
huhuanming merged 11 commits intomainfrom
feat/react-native-perf-stats
May 8, 2026
Merged

feat: add react-native-perf-stats native module#52
huhuanming merged 11 commits intomainfrom
feat/react-native-perf-stats

Conversation

@huhuanming
Copy link
Copy Markdown
Contributor

@huhuanming huhuanming commented May 7, 2026

Summary

  • 新增 @onekeyfe/react-native-perf-stats workspace 包,基于 nitro-modules,含 Android (Kotlin/JNI/CMake) 与 iOS (Swift) 实现
  • 提供 CPU / RSS / UI FPS / JS FPS 四项指标,原生采样线程独立于 JS 线程;JS FPS tracker 在 start/stop 时自动管理
  • 提供原生悬浮 overlay:Android 使用 WindowManager.addViewTYPE_APPLICATION_ABOVE_SUB_PANEL,无需悬浮窗权限)并跟随 Activity 生命周期;iOS 使用独立 UIWindow 保证盖在 modal 之上
  • 持续异常(CPU/RSS 过高、UI/JS FPS 过低)通过 native-logger 输出 warn,含抖动抑制与冷却
  • 所有 native-modules / native-views 包版本从 3.0.24 升至 3.0.28,并同步 yarn.lock

Test plan

  • yarn install 通过
  • iOS 端集成 perf-stats 后能正常 link 与 build;overlay 在 modal 上层显示
  • Android 端集成 perf-stats 后能正常 build;切换 / 销毁 Activity 时 overlay 不泄漏
  • 验证 @onekeyfe/react-native-perf-stats 在宿主 app 中可被 import 并调用 start/stop/showOverlay/sample
  • 触发持续高 CPU / 低 FPS 场景,确认 native-logger 能收到 warn

Open in Devin Review

huhuanming added 6 commits May 7, 2026 20:14
Scaffold @onekeyfe/react-native-perf-stats workspace package using
nitro-modules, with Android (Kotlin/JNI) and iOS (Swift) bindings.
Move running=true and handler lifecycle into a single synchronized
block, and add a generation token so any in-flight tick whose
scheduler has been stopped drops itself instead of rescheduling on
a quitting (or freshly-recreated) handler.

Previous code post()ed the running=true assignment to the handler
thread; if stop() landed between the post and its execution, the
lambda resurrected running=true after stop had nulled the handler
and quit the looper, leaving running=true with no live scheduler.
The next start() then early-returned on the running check and the
sampler never recovered.
Periodic sampler now emits OneKeyLog.warn when CPU >= 150% or
RSS >= 800 MB for 5 consecutive samples, with a per-category 30 s
cooldown to avoid log flooding. One-shot sample() calls do not
trip this path; counters live on the sampler thread (Android
HandlerThread, iOS serial queue) so they need no extra locking,
while the cooldown clock persists across stop/start cycles.
WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL is
@hide in the public Android SDK, so referencing it by name fails
to compile against a non-internal android.jar. The runtime still
honours the value (FIRST_SUB_WINDOW + 5 = 1005), so use the
literal directly via a private const and document the intent.

Bumps the workspace to 3.0.26 since 3.0.25 already shipped to npm
with the broken constant.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new Nitro-based native module @onekeyfe/react-native-perf-stats that samples process CPU/RSS and can display a draggable native overlay, and also bumps the workspace versions of existing native-modules/native-views packages.

Changes:

  • Added new workspace package native-modules/react-native-perf-stats with Android (Kotlin/JNI/CMake) + iOS (Swift) implementations and JS Nitro typings/entrypoint.
  • Bumped multiple native-modules/native-views package versions (and updated related peer dependency constraints) plus updated yarn.lock.

Reviewed changes

Copilot reviewed 50 out of 51 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
yarn.lock Adds the new workspace entry and updates peer dependency constraints/lockfile data for the version bump.
native-views/react-native-tab-view/package.json Version bump to 3.0.26.
native-views/react-native-skeleton/package.json Version bump to 3.0.26.
native-views/react-native-scroll-guard/package.json Version bump to 3.0.26.
native-views/react-native-pager-view/package.json Version bump to 3.0.26.
native-views/react-native-auto-size-input/package.json Version bump to 3.0.26.
native-modules/react-native-zip-archive/package.json Version bump to 3.0.26.
native-modules/react-native-tcp-socket/package.json Version bump to 3.0.26.
native-modules/react-native-split-bundle-loader/package.json Version + peer dependency bump for bundle-update to >=3.0.26.
native-modules/react-native-splash-screen/package.json Version bump to 3.0.26.
native-modules/react-native-ping/package.json Version bump to 3.0.26.
native-modules/react-native-perf-memory/package.json Version bump to 3.0.26.
native-modules/react-native-pbkdf2/package.json Version bump to 3.0.26.
native-modules/react-native-network-info/package.json Version bump to 3.0.26.
native-modules/react-native-lite-card/package.json Version bump to 3.0.26.
native-modules/react-native-keychain-module/package.json Version bump to 3.0.26.
native-modules/react-native-get-random-values/package.json Version bump to 3.0.26.
native-modules/react-native-dns-lookup/package.json Version bump to 3.0.26.
native-modules/react-native-device-utils/package.json Version bump to 3.0.26.
native-modules/react-native-cloud-kit-module/package.json Version bump to 3.0.26.
native-modules/react-native-cloud-fs/package.json Version bump to 3.0.26.
native-modules/react-native-check-biometric-auth-changed/package.json Version bump to 3.0.26.
native-modules/react-native-bundle-update/package.json Version bump to 3.0.26.
native-modules/react-native-background-thread/package.json Version + peer dependency bump for bundle-update to >=3.0.26.
native-modules/react-native-async-storage/package.json Version bump to 3.0.26.
native-modules/react-native-app-update/package.json Version bump to 3.0.26.
native-modules/react-native-aes-crypto/package.json Version bump to 3.0.26.
native-modules/native-logger/package.json Version bump to 3.0.26.
native-modules/react-native-perf-stats/package.json New package definition for @onekeyfe/react-native-perf-stats.
native-modules/react-native-perf-stats/README.md Usage + API docs for the new perf stats module.
native-modules/react-native-perf-stats/LICENSE New package license file.
native-modules/react-native-perf-stats/nitro.json Nitro/Nitrogen configuration for codegen/autolinking.
native-modules/react-native-perf-stats/turbo.json Turborepo task config for building outputs/inputs.
native-modules/react-native-perf-stats/tsconfig.json TypeScript config for the new package.
native-modules/react-native-perf-stats/tsconfig.build.json Build TS config enabling declaration emit.
native-modules/react-native-perf-stats/eslint.config.mjs ESLint configuration for the new package.
native-modules/react-native-perf-stats/babel.config.js Babel config for local development/build.
native-modules/react-native-perf-stats/lefthook.yml Git hooks config for lint/typecheck.
native-modules/react-native-perf-stats/.gitignore Package-specific ignores (generated outputs, builds, etc.).
native-modules/react-native-perf-stats/src/ReactNativePerfStats.nitro.ts Nitro HybridObject spec typings and API comments.
native-modules/react-native-perf-stats/src/index.tsx JS entrypoint creating the hybrid object and re-exporting types.
native-modules/react-native-perf-stats/ReactNativePerfStats.podspec CocoaPods spec wiring Nitrogen iOS autolinking + deps.
native-modules/react-native-perf-stats/ios/ReactNativePerfStats.swift iOS sampler + overlay implementation and anomaly logging.
native-modules/react-native-perf-stats/android/gradle.properties Android build defaults (SDKs, Kotlin version).
native-modules/react-native-perf-stats/android/build.gradle Android build config + Nitrogen autolinking + deps.
native-modules/react-native-perf-stats/android/CMakeLists.txt JNI shared library build + Nitrogen CMake autolinking include.
native-modules/react-native-perf-stats/android/src/main/AndroidManifest.xml Adds an init ContentProvider for early overlay bootstrap.
native-modules/react-native-perf-stats/android/src/main/java/com/margelo/nitro/reactnativeperfstats/PerfStatsInitProvider.kt Initializes overlay lifecycle tracking before Application.onCreate.
native-modules/react-native-perf-stats/android/src/main/java/com/margelo/nitro/reactnativeperfstats/ReactNativePerfStats.kt Android sampler + overlay implementation and anomaly logging.
native-modules/react-native-perf-stats/android/src/main/java/com/margelo/nitro/reactnativeperfstats/ReactNativePerfStatsPackage.kt ReactPackage stub + System.loadLibrary for the JSI library.
native-modules/react-native-perf-stats/android/src/main/cpp/cpp-adapter.cpp JNI_OnLoad adapter to initialize the Nitrogen bindings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread native-modules/react-native-perf-stats/src/ReactNativePerfStats.nitro.ts Outdated
Comment thread native-modules/react-native-bundle-update/package.json
huhuanming added 5 commits May 8, 2026 03:35
PerfSample gains uiFps (native, Choreographer / CADisplayLink
counted on the main thread) and jsFps (JS-side requestAnimationFrame
count pushed via setJsFpsHint). Native owns UI FPS read-and-reset on
each periodic tick, then caches the value so one-shot sample() calls
don't steal frames from the next interval. JS FPS hints stale out
after 2 s so the overlay doesn't surface a frozen number when the
JS-side tracker has been stopped.

New JS exports startJsFpsTracker / stopJsFpsTracker run an rAF loop
+ setInterval reporter so consumers don't have to wire that
themselves. setJsFpsHint remains as a low-level escape hatch.

Anomaly logging extends to ui_fps <= 45 (and > 0) and js_fps <= 30
(and > 0), each with the same 5-sample sustain and 30 s cooldown
already used for CPU/RSS. The overlay now renders four lines.

Bindings regenerated via nitrogen; .nitro.ts updated.
ReactNativePerfStats.start now also runs startJsFpsTracker with the
sampler's interval, and .stop tears it down. Callers no longer have
to wire the rAF lifecycle separately for jsFps to populate.
startJsFpsTracker / stopJsFpsTracker remain exported as escape
hatches, and starting with a different reportIntervalMs while the
loop is already running cleanly restarts it.

Bumps the workspace to 3.0.27 since 3.0.26 already shipped without
this auto-management.
The overlay UILabel was being added directly to the host app's key
UIWindow, so any view controller presented via UIKit's modal
presentation (RN <Modal>, native action sheets, etc.) rendered on
top of it regardless of subview z-order — modal presentation works
*above* the entire root window's view hierarchy, so no amount of
bringSubviewToFront would help.

Switch to a dedicated UIWindow at windowLevel = .alert + 1, which
sits above modal-presented controllers and system alerts. Back the
window with OverlayPassthroughWindow, a UIWindow subclass that
forwards every touch outside the label to the windows below via
hitTest, so the overlay never swallows taps the rest of the app
needs to receive.
Overlay.detach() looked up WindowManager via currentActivity, but
onActivityDestroyed posts detach() to the main handler and then clears
currentActivity synchronously — the queued detach ran with a null
activity and removeView was skipped, leaking the overlay view. Resolve
WindowManager from the view's own context instead.

Also align JSDoc on showOverlay() with the actual implementation: Android
uses WindowManager.addView with the Activity window token, and iOS hosts
the overlay on a dedicated passthrough UIWindow.
@huhuanming huhuanming merged commit f20546a into main May 8, 2026
2 checks passed
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.

3 participants