@onekeyfe/react-native-perf-stats 3.0.35 → 3.0.36
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/android/src/main/java/com/margelo/nitro/reactnativeperfstats/PerfStatsInitProvider.kt +11 -4
- package/android/src/main/java/com/margelo/nitro/reactnativeperfstats/ReactNativePerfStats.kt +333 -24
- package/ios/ReactNativePerfStats.swift +360 -15
- package/lib/module/index.js +77 -5
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/ReactNativePerfStats.nitro.d.ts +109 -1
- package/lib/typescript/src/ReactNativePerfStats.nitro.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +31 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JFunc_void_MemoryWarningEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JHybridReactNativePerfStatsSpec.cpp +24 -0
- package/nitrogen/generated/android/c++/JHybridReactNativePerfStatsSpec.hpp +3 -0
- package/nitrogen/generated/android/c++/JMemoryWarningEvent.hpp +66 -0
- package/nitrogen/generated/android/c++/JMemoryWarningLevel.hpp +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/Func_void_MemoryWarningEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/HybridReactNativePerfStatsSpec.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/MemoryWarningEvent.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/MemoryWarningLevel.kt +21 -0
- package/nitrogen/generated/android/reactnativeperfstatsOnLoad.cpp +2 -0
- package/nitrogen/generated/ios/ReactNativePerfStats-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/ReactNativePerfStats-Swift-Cxx-Bridge.hpp +37 -0
- package/nitrogen/generated/ios/ReactNativePerfStats-Swift-Cxx-Umbrella.hpp +7 -0
- package/nitrogen/generated/ios/c++/HybridReactNativePerfStatsSpecSwift.hpp +27 -0
- package/nitrogen/generated/ios/swift/Func_void_MemoryWarningEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridReactNativePerfStatsSpec.swift +3 -0
- package/nitrogen/generated/ios/swift/HybridReactNativePerfStatsSpec_cxx.swift +39 -0
- package/nitrogen/generated/ios/swift/MemoryWarningEvent.swift +58 -0
- package/nitrogen/generated/ios/swift/MemoryWarningLevel.swift +40 -0
- package/nitrogen/generated/shared/c++/HybridReactNativePerfStatsSpec.cpp +3 -0
- package/nitrogen/generated/shared/c++/HybridReactNativePerfStatsSpec.hpp +7 -0
- package/nitrogen/generated/shared/c++/MemoryWarningEvent.hpp +84 -0
- package/nitrogen/generated/shared/c++/MemoryWarningLevel.hpp +76 -0
- package/package.json +1 -1
- package/src/ReactNativePerfStats.nitro.ts +114 -1
- package/src/index.tsx +90 -5
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
MemoryWarningEvent,
|
|
4
|
+
ReactNativePerfStats as ReactNativePerfStatsType,
|
|
5
|
+
} from './ReactNativePerfStats.nitro';
|
|
3
6
|
|
|
4
7
|
const nativeImpl =
|
|
5
8
|
NitroModules.createHybridObject<ReactNativePerfStatsType>('ReactNativePerfStats');
|
|
@@ -28,6 +31,11 @@ let jsFpsIntervalId: ReturnType<typeof setInterval> | null = null;
|
|
|
28
31
|
let jsFpsFrameCount = 0;
|
|
29
32
|
let jsFpsCurrentInterval: number | null = null;
|
|
30
33
|
|
|
34
|
+
// Module-level latch so forceGarbageCollection's "no GC binding" branch
|
|
35
|
+
// warns exactly once per JS realm. Avoids spamming the console when a
|
|
36
|
+
// memory-warning handler calls forceGarbageCollection on every event.
|
|
37
|
+
let forceGcMissingWarned = false;
|
|
38
|
+
|
|
31
39
|
/**
|
|
32
40
|
* Start the JS-side FPS ticker. Normally invoked automatically by
|
|
33
41
|
* `ReactNativePerfStats.start` and stopped by `.stop`; exported as
|
|
@@ -39,9 +47,17 @@ let jsFpsCurrentInterval: number | null = null;
|
|
|
39
47
|
* interval is a no-op.
|
|
40
48
|
*/
|
|
41
49
|
export function startJsFpsTracker(reportIntervalMs: number = 1000): void {
|
|
42
|
-
|
|
50
|
+
// Clamp before use: `setInterval(_, 0)` fires every event-loop tick and
|
|
51
|
+
// the per-second divide below would race away to Infinity; NaN/negative
|
|
52
|
+
// values are equally meaningless here. 100 ms is below one rAF frame
|
|
53
|
+
// on a 60 Hz display, so it's already finer than the data warrants;
|
|
54
|
+
// 60 s is an arbitrary upper sanity bound.
|
|
55
|
+
const safeInterval = Number.isFinite(reportIntervalMs)
|
|
56
|
+
? Math.max(100, Math.min(60_000, Math.trunc(reportIntervalMs)))
|
|
57
|
+
: 1000;
|
|
58
|
+
if (jsFpsCurrentInterval === safeInterval) return;
|
|
43
59
|
stopJsFpsTracker();
|
|
44
|
-
jsFpsCurrentInterval =
|
|
60
|
+
jsFpsCurrentInterval = safeInterval;
|
|
45
61
|
|
|
46
62
|
const tick = () => {
|
|
47
63
|
jsFpsFrameCount += 1;
|
|
@@ -50,10 +66,10 @@ export function startJsFpsTracker(reportIntervalMs: number = 1000): void {
|
|
|
50
66
|
jsFpsRafId = requestAnimationFrame(tick);
|
|
51
67
|
|
|
52
68
|
jsFpsIntervalId = setInterval(() => {
|
|
53
|
-
const fps = (jsFpsFrameCount * 1000) /
|
|
69
|
+
const fps = (jsFpsFrameCount * 1000) / safeInterval;
|
|
54
70
|
jsFpsFrameCount = 0;
|
|
55
71
|
nativeImpl.setJsFpsHint(fps);
|
|
56
|
-
},
|
|
72
|
+
}, safeInterval);
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
/** Stop the JS-side FPS ticker. Idempotent. */
|
|
@@ -89,4 +105,73 @@ export const ReactNativePerfStats = {
|
|
|
89
105
|
hideOverlay: (): void => nativeImpl.hideOverlay(),
|
|
90
106
|
sample: () => nativeImpl.sample(),
|
|
91
107
|
setJsFpsHint: (fps: number): void => nativeImpl.setJsFpsHint(fps),
|
|
108
|
+
// Memory pressure is independent of the sampler — these stay active
|
|
109
|
+
// even after `stop()`. iOS only emits `level: 'critical'`.
|
|
110
|
+
addMemoryWarningListener: (
|
|
111
|
+
callback: (event: MemoryWarningEvent) => void,
|
|
112
|
+
): number => nativeImpl.addMemoryWarningListener(callback),
|
|
113
|
+
removeMemoryWarningListener: (id: number): void =>
|
|
114
|
+
nativeImpl.removeMemoryWarningListener(id),
|
|
115
|
+
/**
|
|
116
|
+
* Run the same native reclaim path the OS memory-warning observer
|
|
117
|
+
* triggers, on demand. Returns immediately (heavy work runs async
|
|
118
|
+
* on iOS). See the spec doc for what gets dropped on each platform.
|
|
119
|
+
*/
|
|
120
|
+
cleanupNativeCaches: (): void => nativeImpl.cleanupNativeCaches(),
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Best-effort hint to the JS engine that now is a good time to GC.
|
|
124
|
+
*
|
|
125
|
+
* Hermes does not expose a public `collectGarbage` binding in production
|
|
126
|
+
* builds; the only stable JS-level entry point is the (undocumented)
|
|
127
|
+
* `HermesInternal.gc` property, which is present in some builds and
|
|
128
|
+
* absent in others. We feature-detect it and fall back to a no-op so
|
|
129
|
+
* callers never have to branch.
|
|
130
|
+
*
|
|
131
|
+
* Returns `true` only if a GC binding was both found AND invoked
|
|
132
|
+
* without throwing. A `false` return therefore covers three cases —
|
|
133
|
+
* binding missing (production Hermes is the common case), binding
|
|
134
|
+
* present but threw, and any unexpected failure. The first miss is
|
|
135
|
+
* logged once via `console.warn` so the caller knows it landed in the
|
|
136
|
+
* "no binding" branch; throws are logged on every occurrence because
|
|
137
|
+
* those are real errors.
|
|
138
|
+
*
|
|
139
|
+
* Cost: when honoured, Hermes does a stop-the-world collection that
|
|
140
|
+
* can take 100–500 ms — never call this on the hot path. Memory-warning
|
|
141
|
+
* handlers are the intended use case.
|
|
142
|
+
*/
|
|
143
|
+
forceGarbageCollection(): boolean {
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
+
const g = globalThis as any;
|
|
146
|
+
const hi = g?.HermesInternal;
|
|
147
|
+
if (hi && typeof hi.gc === 'function') {
|
|
148
|
+
try {
|
|
149
|
+
hi.gc();
|
|
150
|
+
return true;
|
|
151
|
+
} catch (e) {
|
|
152
|
+
console.warn('[PerfStats] HermesInternal.gc threw:', e);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (typeof g?.gc === 'function') {
|
|
157
|
+
// V8-style binding (only present with --expose-gc; never in
|
|
158
|
+
// production Hermes, but harmless to try).
|
|
159
|
+
try {
|
|
160
|
+
g.gc();
|
|
161
|
+
return true;
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.warn('[PerfStats] globalThis.gc threw:', e);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!forceGcMissingWarned) {
|
|
168
|
+
forceGcMissingWarned = true;
|
|
169
|
+
console.warn(
|
|
170
|
+
'[PerfStats] forceGarbageCollection: no GC binding found ' +
|
|
171
|
+
'(HermesInternal.gc / globalThis.gc absent). Production Hermes ' +
|
|
172
|
+
'strips these; this warning fires once per JS realm.',
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
},
|
|
92
177
|
};
|