feat: add react-native-perf-stats native module#52
Merged
huhuanming merged 11 commits intomainfrom May 8, 2026
Merged
Conversation
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.
There was a problem hiding this comment.
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-statswith 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.
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.
ByteZhang1024
approved these changes
May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@onekeyfe/react-native-perf-statsworkspace 包,基于 nitro-modules,含 Android (Kotlin/JNI/CMake) 与 iOS (Swift) 实现start/stop时自动管理WindowManager.addView(TYPE_APPLICATION_ABOVE_SUB_PANEL,无需悬浮窗权限)并跟随 Activity 生命周期;iOS 使用独立UIWindow保证盖在 modal 之上3.0.24升至3.0.28,并同步 yarn.lockTest plan
yarn install通过@onekeyfe/react-native-perf-stats在宿主 app 中可被 import 并调用start/stop/showOverlay/sample