@arms/rum-reactnative 0.1.3-beta.5 → 0.1.4-beta.2

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.
Files changed (89) hide show
  1. package/README.md +9 -2
  2. package/android/build.gradle +12 -1
  3. package/android/gradle.properties +3 -1
  4. package/android/src/main/AndroidManifest.xml +4 -1
  5. package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativeModule.kt +67 -14
  6. package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativePackage.kt +9 -2
  7. package/docs/ReactNative SDK/351/205/215/347/275/256/345/217/202/350/200/203.md" +25 -0
  8. package/docs/SDK /347/211/210/346/234/254/350/257/264/346/230/216.md" +20 -0
  9. package/docs//346/216/245/345/205/245ReactNative/345/272/224/347/224/250.md +2 -1
  10. package/ios/RumAppStateRecorder.h +69 -0
  11. package/ios/RumAppStateRecorder.mm +547 -0
  12. package/ios/RumCrashReporter.h +32 -0
  13. package/ios/RumCrashReporter.mm +97 -11
  14. package/ios/RumSdkReactnative.h +11 -1
  15. package/ios/RumSdkReactnative.mm +115 -29
  16. package/lib/commonjs/NativeRumSdkReactnative.js +1 -54
  17. package/lib/commonjs/collector/action/index.js +1 -108
  18. package/lib/commonjs/collector/exception/crash.js +1 -148
  19. package/lib/commonjs/collector/exception/index.js +1 -238
  20. package/lib/commonjs/collector/exception/oom.js +1 -0
  21. package/lib/commonjs/collector/index.js +1 -42
  22. package/lib/commonjs/collector/resource/api.js +1 -292
  23. package/lib/commonjs/collector/view/perf.js +1 -79
  24. package/lib/commonjs/collector/view/pv.js +1 -88
  25. package/lib/commonjs/expoConfigPlugin.js +1 -37
  26. package/lib/commonjs/index.js +1 -29
  27. package/lib/commonjs/plugin.js +1 -47
  28. package/lib/commonjs/processor/default-processor.js +1 -27
  29. package/lib/commonjs/processor/session-processor.js +1 -47
  30. package/lib/commonjs/reporter/index.js +1 -148
  31. package/lib/commonjs/reporter/monitor-reporter.js +1 -43
  32. package/lib/commonjs/shell.js +1 -91
  33. package/lib/commonjs/types/client.js +1 -6
  34. package/lib/commonjs/types/crash.js +1 -37
  35. package/lib/commonjs/types/index.js +1 -2
  36. package/lib/commonjs/types/oom.js +1 -0
  37. package/lib/commonjs/utils/action.js +1 -305
  38. package/lib/commonjs/utils/api.js +1 -84
  39. package/lib/commonjs/utils/base.js +1 -95
  40. package/lib/commonjs/utils/crash.js +1 -149
  41. package/lib/commonjs/utils/platform.js +1 -162
  42. package/lib/commonjs/utils/session.js +1 -158
  43. package/lib/commonjs/utils/url.js +1 -59
  44. package/lib/commonjs/utils/view.js +1 -93
  45. package/lib/module/NativeRumSdkReactnative.js +1 -51
  46. package/lib/module/collector/action/index.js +1 -103
  47. package/lib/module/collector/exception/crash.js +1 -143
  48. package/lib/module/collector/exception/index.js +1 -233
  49. package/lib/module/collector/exception/oom.js +1 -0
  50. package/lib/module/collector/index.js +1 -8
  51. package/lib/module/collector/resource/api.js +1 -287
  52. package/lib/module/collector/view/perf.js +1 -79
  53. package/lib/module/collector/view/pv.js +1 -83
  54. package/lib/module/expoConfigPlugin.js +1 -33
  55. package/lib/module/index.js +1 -7
  56. package/lib/module/plugin.js +1 -42
  57. package/lib/module/processor/default-processor.js +1 -22
  58. package/lib/module/processor/session-processor.js +1 -42
  59. package/lib/module/reporter/index.js +1 -144
  60. package/lib/module/reporter/monitor-reporter.js +1 -38
  61. package/lib/module/shell.js +1 -84
  62. package/lib/module/types/client.js +1 -4
  63. package/lib/module/types/crash.js +1 -33
  64. package/lib/module/types/index.js +1 -2
  65. package/lib/module/types/oom.js +1 -0
  66. package/lib/module/utils/action.js +1 -299
  67. package/lib/module/utils/api.js +1 -76
  68. package/lib/module/utils/base.js +1 -90
  69. package/lib/module/utils/crash.js +1 -144
  70. package/lib/module/utils/platform.js +1 -150
  71. package/lib/module/utils/session.js +1 -154
  72. package/lib/module/utils/url.js +1 -54
  73. package/lib/module/utils/view.js +1 -88
  74. package/lib/typescript/commonjs/src/NativeRumSdkReactnative.d.ts +10 -0
  75. package/lib/typescript/commonjs/src/collector/exception/oom.d.ts +34 -0
  76. package/lib/typescript/commonjs/src/collector/index.d.ts +1 -0
  77. package/lib/typescript/commonjs/src/shell.d.ts +1 -1
  78. package/lib/typescript/commonjs/src/types/client.d.ts +27 -0
  79. package/lib/typescript/commonjs/src/types/oom.d.ts +58 -0
  80. package/lib/typescript/commonjs/src/utils/crash.d.ts +13 -0
  81. package/lib/typescript/module/src/NativeRumSdkReactnative.d.ts +10 -0
  82. package/lib/typescript/module/src/collector/exception/oom.d.ts +34 -0
  83. package/lib/typescript/module/src/collector/index.d.ts +1 -0
  84. package/lib/typescript/module/src/shell.d.ts +1 -1
  85. package/lib/typescript/module/src/types/client.d.ts +27 -0
  86. package/lib/typescript/module/src/types/oom.d.ts +58 -0
  87. package/lib/typescript/module/src/utils/crash.d.ts +13 -0
  88. package/package.json +1 -2
  89. package/src/NativeRumSdkReactnative.ts +18 -0
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  ## 功能特性
6
6
 
7
7
  - **原生崩溃采集** — 自动捕获 iOS(KSCrash)与 Android(Java + NDK)原生崩溃,支持堆栈双侧截断与数据体积优化
8
+ - **OOM采集(iOS)** — 看门狗终止(Watchdog Termination / FOOM)的事后推断上报,默认关闭,需显式开启
8
9
  - **API性能监控** — 自动拦截 XMLHttpRequest / fetch 请求,采集耗时、状态码等指标
9
10
  - **JS错误捕获** — 自动捕获 JavaScript 运行时异常
10
11
  - **控制台错误监听** — 监听 `console.error` 输出
@@ -81,6 +82,7 @@ armsRum.init({
81
82
  | api | boolean \| object | `true` | API请求监控(XMLHttpRequest / fetch) |
82
83
  | jsError | boolean \| object | `true` | JavaScript运行时错误捕获 |
83
84
  | consoleError | boolean \| object | `true` | `console.error`监听 |
85
+ | oom | boolean | `false` | OOM(iOS 看门狗终止推断)采集,仅 iOS 前台场景,需显式开启,详见[配置参考](docs/ReactNative%20SDK配置参考.md) |
84
86
 
85
87
  ### sessionConfig Session配置
86
88
 
@@ -418,6 +420,7 @@ SDK 仅持久化一个键 `_arms_uid`(匿名用户 UID),用于跨冷启动
418
420
  1. **必须重新构建原生工程**:新增了 TurboModule 方法,升级或首次接入后需要重新执行 iOS 的 `pod install`、Android 的 Gradle Sync;Expo 工程请执行 `expo prebuild --clean` 后再编译,否则会报 protocol method 缺失。
419
421
  2. **隔离命名空间,不污染宿主 App**:iOS 走独立 `NSUserDefaults` suite、Android 走独立 prefs 文件,与宿主 App 自身的偏好数据互不干扰;用户卸载 / 抹机时会一并清除。
420
422
  3. **从旧版本(依赖 AsyncStorage)升级会重置一次 UID**:原 AsyncStorage 中的 `_arms_uid` 不会自动迁移到新存储,老用户首次升级后会被分配新 UID(仅影响一次 UV 统计的连续性,不影响功能与后续使用)。如对历史 UID 连续性有强诉求,请提 issue。
423
+ 4. **RN 0.68-0.70 的 Android 宿主前置条件**:宿主工程 `android/build.gradle` 的 buildscript 需存在 `kotlin-gradle-plugin` classpath,且 Kotlin 版本 **≥ 1.8.0**(RN 0.70 模板默认 1.6.x,无法读取 SDK 依赖的 kotlin-stdlib 1.8 metadata,需手动提升)。AGP 无需改动——SDK 构建脚本对 AGP <7.3 自动回退 AndroidManifest package 属性。iOS 侧无额外配置,`pod install` 即可(0.68-0.70 自动走旧桥导出路径)。因含原生代码,**Expo Go 不可用**(需 `expo prebuild` 或 EAS Build)。
421
424
 
422
425
  ## 版本要求
423
426
 
@@ -425,9 +428,10 @@ SDK 的 peerDependencies 声明为 `react-native: >=0.68.0`,原生构建配置
425
428
 
426
429
  | 功能点 | 关键依赖 / API | 最低 RN 版本 | 说明 |
427
430
  |--------|--------------|-------------|------|
428
- | **原生崩溃采集** | TurboModule + codegen | 0.68 | podspec / build.gradle 按版本条件分支,自动适配 0.68-0.70 和 0.71+ |
431
+ | **原生崩溃采集** | TurboModule + codegen | 0.68 | 自动适配 0.68-0.70 旧桥导出与 0.71+ Spec 协议/interop;RN ≤0.70 Android 前置条件见注意事项 4 |
429
432
  | NDK 崩溃采集(C/C++) | CMake + NDK `externalNativeBuild` | 0.68 | 随原生崩溃采集一起启用 |
430
433
  | iOS 崩溃采集(KSCrash) | KSCrash 2.5.1(CocoaPods 源码依赖,已锁定版本) | 0.68 | `pod install` 时自动编译 |
434
+ | **OOM 采集(iOS)** | TurboModule + KSCrash + 状态快照持久化 | 0.68 | 仅 iOS,默认关闭(`collectors.oom` 显式开启);Android 为空实现 |
431
435
  | **JS 异常采集** | `ErrorUtils.setGlobalHandler` | 0.68 | 全版本可用 |
432
436
  | Hermes Promise rejection | `HermesInternal.enablePromiseRejectionTracker` | 0.70(推荐) | Hermes 为默认引擎时完整支持 |
433
437
  | JSC Promise rejection 兜底 | `globalThis.onunhandledrejection` | 0.68 | 非 Hermes 引擎有降级方案 |
@@ -443,8 +447,11 @@ SDK 的 peerDependencies 声明为 `react-native: >=0.68.0`,原生构建配置
443
447
  | **自监控(Self-Monitoring)** | `fetch` 上报 Protobuf 至 `/rum/monitor` | 0.68 | 纯 JS 实现 |
444
448
  | **Expo 集成** | Expo Config Plugin | Expo SDK 49+(RN 0.72+) | 仅配置透传,零运行时依赖 |
445
449
 
446
- > **总结**:SDK 声明支持 RN ≥ 0.68,原生构建配置(podspec / build.gradle)已做多版本条件适配。RN 0.71+ 使用 `install_modules_dependencies` / `react-android`;RN 0.68-0.70 使用手动依赖 / `react-native:+`。JS 侧通过 `TurboModuleRegistry.get` + `NativeModules` 双路径获取原生模块,原生模块缺失时自动降级为空实现,不影响纯 JS 能力。
450
+ > **总结**:SDK 声明支持 RN ≥ 0.68。iOS 经 `__has_include` 条件协议 + `RCT_EXPORT_METHOD` 旧桥导出、Android 经 `@ReactMethod` 方法表 + `namespace` DSL 条件守卫,原生能力在 0.68-0.70(旧桥路径)与 0.71+(TurboModule 协议 / interop)均可用;构建层 RN 0.71+ 使用 `install_modules_dependencies` / `react-android`,0.68-0.70 使用手动依赖 / `react-native:+`。JS 侧通过 `TurboModuleRegistry.get` + `NativeModules` 双路径获取原生模块,原生模块缺失时自动降级为空实现,不影响纯 JS 能力。RN ≤0.70 的 Android 宿主需满足 Kotlin ≥1.8 前置条件(见「升级 / 接入注意事项」第 4 条)。
447
451
 
448
452
  ## 相关文档
449
453
 
454
+ - [接入ReactNative应用](docs/接入ReactNative应用.md)
455
+ - [ReactNative SDK配置参考](docs/ReactNative%20SDK配置参考.md)
456
+ - [SDK 版本说明](docs/SDK%20版本说明.md)
450
457
  - [ARMS用户体验监控文档](https://help.aliyun.com/zh/arms/user-experience-monitoring/)
@@ -70,7 +70,11 @@ if (isRn71Plus) {
70
70
  }
71
71
 
72
72
  android {
73
- namespace "com.rumsdkreactnative"
73
+ // AGP 7.3+ 才支持 namespace DSL(RN 0.70 模板默认 AGP 7.2.1);低版本回退
74
+ // AndroidManifest.xml 的 package 属性(RN 0.68-0.70 兼容,见 dev-docs 打通方案 A1)
75
+ if (android.hasProperty("namespace")) {
76
+ namespace "com.rumsdkreactnative"
77
+ }
74
78
 
75
79
  compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
76
80
 
@@ -109,6 +113,13 @@ android {
109
113
  }
110
114
  }
111
115
 
116
+ // CMake 显式链接 c++_shared 与 react-android 携带的同名 so 在 androidTest 变体
117
+ // 合并时冲突(mergeAndroidTestNativeLibs 报 2 files found)。两者为同一 STL,取任一
118
+ // 均可,pickFirst 去重(库模块自身声明,宿主无需额外配置)。
119
+ packagingOptions {
120
+ pickFirst "**/libc++_shared.so"
121
+ }
122
+
112
123
  lintOptions {
113
124
  disable "GradleCompatible"
114
125
  }
@@ -1,4 +1,6 @@
1
- RumSdkReactnative_kotlinVersion=2.0.21
1
+ # 默认 1.8.0:stdlib metadata 可被 1.8+ 编译器读取(向上兼容),覆盖 RN 0.68-0.70
2
+ # 手动配置场景与 0.71+ 默认工具链;宿主 rootProject.ext.kotlinVersion 定义时优先跟随宿主
3
+ RumSdkReactnative_kotlinVersion=1.8.0
2
4
  RumSdkReactnative_minSdkVersion=24
3
5
  RumSdkReactnative_targetSdkVersion=34
4
6
  RumSdkReactnative_compileSdkVersion=35
@@ -1,4 +1,7 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
1
+ <!-- package 属性:AGP <7.3(RN 0.68-0.70)下的包名来源;AGP 7.3+ 由 build.gradle
2
+ 条件 namespace 取代(该属性被忽略,仅 deprecation warning,属预期) -->
3
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
4
+ package="com.rumsdkreactnative">
2
5
  <application>
3
6
  <!--
4
7
  崩溃处理器自启动:通过 ContentProvider 在 Application.onCreate() 之前安装
@@ -10,6 +10,8 @@ import android.view.WindowManager
10
10
  import com.facebook.react.bridge.Arguments
11
11
  import com.facebook.react.bridge.Promise
12
12
  import com.facebook.react.bridge.ReactApplicationContext
13
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
14
+ import com.facebook.react.bridge.ReactMethod
13
15
  import com.facebook.react.module.annotations.ReactModule
14
16
  import com.rumsdkreactnative.crash.CrashContextManager
15
17
  import com.rumsdkreactnative.crash.CrashHandler
@@ -17,9 +19,13 @@ import org.json.JSONArray
17
19
  import org.json.JSONObject
18
20
  import java.io.File
19
21
 
22
+ // 不继承 codegen 生成的 Spec 基类(RN ≤0.70 不运行 codegen、无此产物,见
23
+ // dev-docs/RN 0.68-0.70 兼容性打通方案.md A2):直接继承 ReactContextBaseJavaModule,
24
+ // 方法经 @ReactMethod 导出(RN 0.71+ 经 TurboModule interop 调用);
25
+ // 代价是失去 codegen 对 TS Spec 与原生签名一致性的编译期校验,签名变更需人工同步 Spec
20
26
  @ReactModule(name = RumSdkReactnativeModule.NAME)
21
27
  class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
22
- NativeRumSdkReactnativeSpec(reactContext) {
28
+ ReactContextBaseJavaModule(reactContext) {
23
29
 
24
30
  private val prefs: SharedPreferences =
25
31
  migratePrefs(reactContext).also {
@@ -36,7 +42,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
36
42
 
37
43
  // region Storage
38
44
 
39
- override fun getItem(key: String, promise: Promise) {
45
+ @ReactMethod
46
+ fun getItem(key: String, promise: Promise) {
40
47
  try {
41
48
  promise.resolve(prefs.getString(key, null))
42
49
  } catch (e: Throwable) {
@@ -44,7 +51,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
44
51
  }
45
52
  }
46
53
 
47
- override fun setItem(key: String, value: String, promise: Promise) {
54
+ @ReactMethod
55
+ fun setItem(key: String, value: String, promise: Promise) {
48
56
  try {
49
57
  prefs.edit().putString(key, value).apply()
50
58
  promise.resolve(null)
@@ -53,7 +61,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
53
61
  }
54
62
  }
55
63
 
56
- override fun removeItem(key: String, promise: Promise) {
64
+ @ReactMethod
65
+ fun removeItem(key: String, promise: Promise) {
57
66
  try {
58
67
  prefs.edit().remove(key).apply()
59
68
  promise.resolve(null)
@@ -62,7 +71,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
62
71
  }
63
72
  }
64
73
 
65
- override fun clear(promise: Promise) {
74
+ @ReactMethod
75
+ fun clear(promise: Promise) {
66
76
  try {
67
77
  prefs.edit().clear().apply()
68
78
  promise.resolve(null)
@@ -71,7 +81,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
71
81
  }
72
82
  }
73
83
 
74
- override fun getAllKeys(promise: Promise) {
84
+ @ReactMethod
85
+ fun getAllKeys(promise: Promise) {
75
86
  try {
76
87
  val array = Arguments.createArray()
77
88
  for (key in prefs.all.keys) {
@@ -87,7 +98,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
87
98
 
88
99
  // region Device Info
89
100
 
90
- override fun getDeviceInfo(promise: Promise) {
101
+ @ReactMethod
102
+ fun getDeviceInfo(promise: Promise) {
91
103
  try {
92
104
  val ctx = reactApplicationContext
93
105
  val map = Arguments.createMap()
@@ -170,7 +182,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
170
182
  * 崩溃文件命名:crash_{timestamp}_{id}.json(Java)或 crash_native_{timestamp}_{pid}.json(NDK)
171
183
  * 注意:排除 context 文件(.rum_crash_context)和临时文件
172
184
  */
173
- override fun getPendingCrashes(promise: Promise) {
185
+ @ReactMethod
186
+ fun getPendingCrashes(promise: Promise) {
174
187
  try {
175
188
  val arr = JSONArray()
176
189
  if (crashDir.exists() && crashDir.isDirectory) {
@@ -244,7 +257,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
244
257
  /**
245
258
  * 解析 crashIds JSON array,按 crash_id 删除对应文件。
246
259
  */
247
- override fun clearCrashReports(crashIds: String, promise: Promise) {
260
+ @ReactMethod
261
+ fun clearCrashReports(crashIds: String, promise: Promise) {
248
262
  try {
249
263
  val ids = HashSet<String>()
250
264
  if (crashIds.isNotEmpty()) {
@@ -307,7 +321,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
307
321
  }
308
322
 
309
323
  /** 上次运行是否发生过崩溃:crashDir 中存在崩溃报告文件即认为发生过 */
310
- override fun crashedLastRun(promise: Promise) {
324
+ @ReactMethod
325
+ fun crashedLastRun(promise: Promise) {
311
326
  try {
312
327
  val crashed = crashDir.exists() &&
313
328
  (crashDir.listFiles()?.any { it.isFile && isCrashReportFile(it.name) } ?: false)
@@ -318,7 +333,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
318
333
  }
319
334
 
320
335
  /** 更新崩溃上下文快照(session/view 变更时调用) */
321
- override fun updateCrashContext(context: String) {
336
+ @ReactMethod
337
+ fun updateCrashContext(context: String) {
322
338
  try {
323
339
  crashContextManager.updateContext(context)
324
340
  } catch (_: Throwable) {
@@ -327,7 +343,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
327
343
  }
328
344
 
329
345
  /** 清除所有崩溃报告文件(用于清理无 crash_id 的旧版 NDK 文件) */
330
- override fun clearAllCrashReports(promise: Promise) {
346
+ @ReactMethod
347
+ fun clearAllCrashReports(promise: Promise) {
331
348
  try {
332
349
  if (crashDir.exists() && crashDir.isDirectory) {
333
350
  val files = crashDir.listFiles { f -> isCrashReportFile(f.name) }
@@ -346,7 +363,8 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
346
363
  }
347
364
 
348
365
  /** 清理过期和超限的崩溃文件 */
349
- override fun cleanupOldCrashes(maxFiles: Double, maxAgeMs: Double, promise: Promise) {
366
+ @ReactMethod
367
+ fun cleanupOldCrashes(maxFiles: Double, maxAgeMs: Double, promise: Promise) {
350
368
  try {
351
369
  if (!crashDir.exists() || !crashDir.isDirectory) {
352
370
  promise.resolve(null)
@@ -422,11 +440,46 @@ class RumSdkReactnativeModule(reactContext: ReactApplicationContext) :
422
440
 
423
441
  // endregion
424
442
 
443
+ // region OOM (iOS-only watchdog termination, no-op on Android)
444
+
445
+ /**
446
+ * Android 无 OOM(看门狗终止推断)能力(D2):Java OutOfMemoryError
447
+ * 已由 CrashHandler 崩溃链路捕获,系统低内存杀进程检测不在本期范围。
448
+ * 空实现仅为满足 TurboModule Spec 双端对齐。
449
+ */
450
+ @ReactMethod
451
+ fun getPendingOomReports(promise: Promise) {
452
+ try {
453
+ promise.resolve("[]")
454
+ } catch (e: Throwable) {
455
+ promise.reject(ERR_CRASH, e)
456
+ }
457
+ }
458
+
459
+ /** 清除 OOM 报告文件:Android 无 OOM 文件,no-op */
460
+ @ReactMethod
461
+ fun clearOomReports(oomIds: String, promise: Promise) {
462
+ try {
463
+ promise.resolve(null)
464
+ } catch (e: Throwable) {
465
+ promise.reject(ERR_CRASH, e)
466
+ }
467
+ }
468
+
469
+ /** 同步 JS 侧采集器配置(如 oom 开关):Android 无 OOM 能力,接受但忽略 */
470
+ @ReactMethod
471
+ fun updateCollectorsConfig(config: String) {
472
+ // no-op
473
+ }
474
+
475
+ // endregion
476
+
425
477
  // region Test Crash
426
478
 
427
479
  private external fun nativeTriggerCrash(type: Int)
428
480
 
429
- override fun triggerTestCrash(crashType: String) {
481
+ @ReactMethod
482
+ fun triggerTestCrash(crashType: String) {
430
483
  when (crashType) {
431
484
  // TurboModule 架构下,@ReactMethod void 方法中抛出的 Java 异常会被 C++ 层
432
485
  // 拦截并转换为 JS 异常,不会到达 Thread.getDefaultUncaughtExceptionHandler()。
@@ -1,13 +1,17 @@
1
1
  package com.rumsdkreactnative
2
2
 
3
- import com.facebook.react.BaseReactPackage
3
+ // 基类用 TurboReactPackage 而非 BaseReactPackage(见 dev-docs/RN 0.68-0.70 兼容性打通方案
4
+ // 方案 A4):BaseReactPackage 为 RN 0.74 新增类,RN 0.68-0.73 宿主下编译失败;
5
+ // TurboReactPackage 自 RN 0.68 起存在(0.76+ 标记 deprecated 但仍可用,仅为警告)。
6
+ // 两者抽象方法签名一致(getModule / getReactModuleInfoProvider)。
7
+ import com.facebook.react.TurboReactPackage
4
8
  import com.facebook.react.bridge.NativeModule
5
9
  import com.facebook.react.bridge.ReactApplicationContext
6
10
  import com.facebook.react.module.model.ReactModuleInfo
7
11
  import com.facebook.react.module.model.ReactModuleInfoProvider
8
12
  import java.util.HashMap
9
13
 
10
- class RumSdkReactnativePackage : BaseReactPackage() {
14
+ class RumSdkReactnativePackage : TurboReactPackage() {
11
15
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
16
  return if (name == RumSdkReactnativeModule.NAME) {
13
17
  RumSdkReactnativeModule(reactContext)
@@ -19,11 +23,14 @@ class RumSdkReactnativePackage : BaseReactPackage() {
19
23
  override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
20
24
  return ReactModuleInfoProvider {
21
25
  val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
26
+ // 7 参构造(含 hasConstants):RN ≤0.72 仅提供此签名;0.73+ 保留为 deprecated
27
+ // 兼容构造(hasConstants 被忽略),故 0.68-0.79+ 全版本可编译。模块无 constants,传 false。
22
28
  moduleInfos[RumSdkReactnativeModule.NAME] = ReactModuleInfo(
23
29
  RumSdkReactnativeModule.NAME,
24
30
  RumSdkReactnativeModule.NAME,
25
31
  false, // canOverrideExistingModule
26
32
  false, // needsEagerInit
33
+ false, // hasConstants(RN ≤0.72 必填,0.73+ 忽略)
27
34
  false, // isCxxModule
28
35
  true // isTurboModule
29
36
  )
@@ -104,6 +104,7 @@ Collector是RUM SDK用于收集React Native应用监控数据的最小单元。
104
104
  | api | boolean \| object | 监听API请求(XMLHttpRequest / fetch) | 否 | true |
105
105
  | jsError | boolean \| object | 监听JS错误 | 否 | true |
106
106
  | consoleError | boolean \| object | 监听Console错误 | 否 | true |
107
+ | oom | boolean | OOM(iOS看门狗终止推断)采集,详细说明请参见[oom配置](#oom-配置) | 否 | false |
107
108
 
108
109
  ### 示例
109
110
 
@@ -131,6 +132,30 @@ armsRum.init({
131
132
  });
132
133
  ```
133
134
 
135
+ ## oom 配置
136
+
137
+ iOS看门狗终止(Watchdog Termination / FOOM,前台 OOM)的事后推断采集。App在前台被系统看门狗终止时无任何信号可捕获,表现为无提示"闪退",现有崩溃管线天然采集不到;开启本能力后,SDK在运行期持续持久化 App 状态快照,下次冷启动时用排除法推断上次是否发生看门狗终止,并通过标准 exception 事件链路上报(`type='crash'` + `subtype='oom'`,name 为 `WatchdogTermination`)。
138
+
139
+ ```typescript
140
+ armsRum.init({
141
+ endpoint: '<your-endpoint>',
142
+ collectors: {
143
+ oom: true,
144
+ },
145
+ });
146
+ ```
147
+
148
+ **能力范围与限制**:
149
+
150
+ - 仅支持 iOS,且仅覆盖**前台激活状态**被杀的推断(后台被杀不判定不上报);Android 无此能力(Java `OutOfMemoryError` 已由崩溃采集链路捕获)
151
+ - 启发式推断存在精度上限:无堆栈(`stack` 为空),`timestamp` 为估算值,事件通过 `snapshots.estimated: true` 结构化标记推断性质
152
+ - 每次启动最多上报 1 条 OOM 事件;首次安装后被杀、调试器附加期间被杀、设备重启等场景不判定(业界同类能力同款排除条件)
153
+ - 判定依据为上次会话持久化的状态快照,需重新构建原生 App(如 `expo run:ios` / `pod install` + 重新编译)后生效
154
+
155
+ > **注意**:
156
+ > - `oom` 默认为 `false`,需显式开启(`oom: true`);其开关方向与崩溃采集相反(崩溃采集默认开启,配置 `crash: false` 才关闭)。
157
+ > - 建议与崩溃采集同时开启(保持 `crash` 默认开启即可)。崩溃采集关闭时,原生崩溃监控仍会安装、KSCrash 崩溃报告照常写入但不会被读取和清理;残留的陈旧报告会使紧跟崩溃之后的 OOM 判定让位崩溃链路(防双报,预期行为),整体方向保守——OOM 倾向被抑制(漏判方向),而非误报增多。
158
+
134
159
  ## tracing 链路追踪配置
135
160
 
136
161
  支持布尔值开关或详细配置对象:
@@ -4,6 +4,26 @@
4
4
 
5
5
  ## 版本列表
6
6
 
7
+ ### v0.1.4(2026年09月)
8
+
9
+ iOS 看门狗终止(OOM)推断采集、RN 0.68-0.70 原生兼容打通与发布产物混淆修复。
10
+
11
+ #### v0.1.4-beta.2(2026年09月23日)
12
+
13
+ 打通 RN 0.68-0.70 原生兼容。
14
+
15
+ - iOS/Android 崩溃采集与 OOM 在低版本旧架构可用(Android 宿主需 Kotlin ≥1.8,详见接入文档前提条件)
16
+
17
+ #### v0.1.4-beta.1(2026年09月23日)
18
+
19
+ 新增 iOS 看门狗终止(OOM)事后推断采集。
20
+
21
+ - 新增 iOS OOM 采集,默认关闭需显式开启
22
+ - 崩溃与 OOM 配置独立,判定互斥防双报
23
+ - OOM 事件英文证据式表述,estimated 标记推断性质
24
+ - 锁定 KSCrash 依赖版本
25
+ - 修复发布流程,生产产物启用代码混淆压缩
26
+
7
27
  ### v0.1.3(2026年07月)
8
28
 
9
29
  原生崩溃采集安全加固与自监控系统集成。
@@ -22,7 +22,8 @@
22
22
  - Node.js >= 18
23
23
  - npm或yarn包管理工具
24
24
  - Android 构建:需安装 NDK 与 CMake(由宿主工程的 `ndkVersion` 决定,SDK 不强制特定版本)
25
- - iOS 构建:需 CocoaPods(用于拉取 `KSCrash` 崩溃采集依赖)
25
+ - **RN 0.68-0.70 的 Android 工程额外要求**:宿主 `android/build.gradle` 的 buildscript 需声明 `kotlin-gradle-plugin` classpath,且 Kotlin 版本 **≥ 1.8.0**(RN 0.70 模板默认 1.6.x,与 SDK 依赖的 kotlin-stdlib 1.8 metadata 不兼容,需手动提升);AGP 7.2 及以下无需改动,SDK 构建脚本自动回退 AndroidManifest package 属性
26
+ - iOS 构建:需 CocoaPods(用于拉取 `KSCrash` 崩溃采集依赖);RN 0.68-0.70 无需额外配置,`pod install` 后自动走旧桥导出路径
26
27
 
27
28
  ### 安装步骤
28
29
 
@@ -0,0 +1,69 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ /**
6
+ * RumAppStateRecorder
7
+ *
8
+ * iOS 看门狗终止(OOM)事后推断采集(P1 阶段,方案见
9
+ * dev-docs/OOM采集调研与实施方案.md 4.2 节)。负责:
10
+ * - 运行期持久化 App 状态快照(app_state.json):生命周期通知驱动、仅状态变化时写
11
+ * - 下次启动时按 10 项排除条件判定上次是否发生看门狗终止(判定逻辑纯函数化,见下)
12
+ * - 判定通过时构造 oom_{timestamp}_{uuid}.json 事件文件,交由 JS 侧回捞上报
13
+ * (原生落盘、JS 回捞,保持 Ack 删文件链路的可靠性)
14
+ *
15
+ * 与 RumCrashReporter 的职责边界:RumCrashReporter 管信号/崩溃(KSCrash),
16
+ * 本类管状态记录与 OOM 推断,不使用信号处理器、不与 KSCrash 冲突。
17
+ * 判定逻辑(RumWatchdogTerminationLogic)与本类同文件实现。
18
+ */
19
+ @interface RumAppStateRecorder : NSObject
20
+
21
+ /**
22
+ * 安装 OOM 采集(幂等,dispatch_once 保证判定只执行一次)。
23
+ *
24
+ * 驱动方:RumSdkReactnative.updateCollectorsConfig——JS 侧 OomCollector 在
25
+ * SDK init 且 collectors.oom === true 时调用 updateCollectorsConfig("{\"oom\":true}")
26
+ * 触发;未开启时本方法不会被调用:不安装 Recorder、不执行判定、不产生
27
+ * 任何 OOM 相关 I/O(D6 默认关闭)。
28
+ *
29
+ * 上次会话是否开启由 app_state.json 的存在性隐式承载(上次未开启则无快照,
30
+ * 判定条件①不通过,不会误判),无需独立的开关持久化。
31
+ */
32
+ + (void)setup;
33
+
34
+ /** 获取待上报的 OOM 事件(已解析为字典数组,逐文件处理,不一次性全量读入拼接) */
35
+ + (NSArray<NSDictionary *> *)pendingOomReports;
36
+
37
+ /** 按 oomId 删除已上报的 OOM 事件文件 */
38
+ + (void)clearOomReportsWithIds:(NSArray<NSString *> *)oomIds;
39
+
40
+ /**
41
+ * 看门狗终止判定(纯函数化:输入上次快照 + 当前环境 + 崩溃证据,输出是否判定,
42
+ * 便于后续经桥伪造输入测试或 XCTest)。
43
+ *
44
+ * 10 项排除条件全部满足才判定(方案 4.2.2,D5/D7 硬约束):
45
+ * ① 存在上次快照;② 非模拟器;③ appVersion 未变;④ osVersion 未变;
46
+ * ⑤ systemBootTime 未变;⑥ vendorId 一致且非空;⑦ 上次非调试态;
47
+ * ⑧ 上次非正常终止;⑨ 上次前台激活(D5 前台限定);
48
+ * ⑩ 上次运行内无崩溃证据(crashed_last_run.flag 不存在 且 KSCrash reportStore
49
+ * 无晚于上次快照 updatedAt 的 pending 报告——时间范围限定排除陈旧报告,
50
+ * D7 判定层互斥)。
51
+ *
52
+ * @param previousState 上次会话的 app_state.json 快照(不存在时传 nil)
53
+ * @param currentState 本次会话采集的当前环境快照
54
+ * @param flagExists crashed_last_run.flag 是否存在
55
+ * @param hasPendingReports 上次运行内是否存在待处理崩溃报告(调用方对 KSCrash
56
+ * pending 报告施加时间范围限定后传入:仅最新报告时间戳
57
+ * 晚于上次快照 updatedAt 时为 YES,陈旧报告不计入)
58
+ * @param isSimulator 是否运行在模拟器(生产路径由 TARGET_OS_SIMULATOR
59
+ * 编译宏求值;参数化便于模拟器上运行的测试伪造真机分支)
60
+ */
61
+ + (BOOL)evaluateWatchdogTerminationFromPreviousState:(nullable NSDictionary *)previousState
62
+ currentState:(NSDictionary *)currentState
63
+ crashedLastRunFlagExists:(BOOL)flagExists
64
+ hasPendingCrashReports:(BOOL)hasPendingReports
65
+ runningOnSimulator:(BOOL)isSimulator;
66
+
67
+ @end
68
+
69
+ NS_ASSUME_NONNULL_END