React Native仅Android7.x触发ModuleHolder.getModule类ANR问题
问题现象
近期仅在Android 7/7.1/7.1.1系统上偶现ANR问题,具体堆栈信息如下:
Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.) "main" prio=5 tid=1 Waiting at java.lang.Object.wait! (Native method) at com.facebook.react.bridge.ModuleHolder.getModule (ModuleHolder.java:14) at com.facebook.react.bridge.NativeModuleRegistry.getModule (NativeModuleRegistry.java:11) at com.facebook.react.bridge.CatalystInstanceImpl.getNativeModule (CatalystInstanceImpl.java:5) at com.facebook.react.bridge.CatalystInstanceImpl.getNativeModule (CatalystInstanceImpl.java:1) at com.facebook.internal.instrument.InstrumentUtility.getUIManager (InstrumentUtility.java:4) at com.facebook.react.ReactInstanceManager.attachRootViewToInstance (ReactInstanceManager.java:4) at com.facebook.react.ReactRootView.attachToReactInstanceManager (ReactRootView.java:12) at com.facebook.react.ReactRootView.startReactApplication (ReactRootView.java:9) at com.myapp.ReactActivity.onCreate (ReactActivity.java:191) at android.app.Activity.performCreate (Activity.java:6845) at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1119) at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2700) at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2808) at android.app.ActivityThread.-wrap12 (ActivityThread.java) at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1541) at android.os.Handler.dispatchMessage (Handler.java:102) at android.os.Looper.loop (Looper.java:165) at android.app.ActivityThread.main (ActivityThread.java:6375) at java.lang.reflect.Method.invoke! (Native method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:912) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:802) "Jit thread pool worker thread 0" prio=5 tid=2 Native (still starting up) #00 pc 000000000001c0ac /system/lib64/libc.so (syscall+28) #00 pc 00000000000e6dd4 /system/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+160) #00 pc 0000000000463cc4 /system/lib64/libart.so (art::ThreadPool::GetTask(art::Thread*)+252) #00 pc 0000000000463180 /system/lib64/libart.so (art::ThreadPoolWorker::Run()+124) #00 pc 0000000000462ab0 /system/lib64/libart.so (art::ThreadPoolWorker::Callback(void*)+116) #00 pc 0000000000068440 /system/lib64/libc.so (__pthread_start(void*)+196) #00 pc 000000000001dd70 /system/lib64/libc.so (__start_thread+16) "HeapTaskDaemon" daemon prio=5 tid=7 Blocked #00 pc 000000000001beec /system/lib64/libc.so (syscall+28) #00 pc 00000000000e8148 /system/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+160) #00 pc 0000000000240ee8 /system/lib64/libart.so (art::gc::TaskProcessor::GetTask(art::Thread*)+360) #00 pc 00000000002417c4 /system/lib64/libart.so (art::gc::TaskProcessor::RunAllTasks(art::Thread*)+92) #00 pc 00000000001e5530 /system/framework/arm64/boot-core-libart.oat (Java_dalvik_system_VMRuntime_runHeapTasks__+124) at dalvik.system.VMRuntime.runHeapTasks (Native method) at java.lang.Daemons$HeapTaskDaemon.run (Daemons.java:435) at java.lang.Thread.run (Thread.java:760) ....Many more thread in WAITING state
环境信息
系统信息:
OS: macOS 11.4
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @2.60GHz
内存: 115.29 MB / 16.00 GB
Shell: 5.8 - /bin/zsh
二进制工具:
Node: 14.5.0 - ~/.nvm/versions/node/v14.5.0/bin/node
Yarn: 1.22.10 - /usr/local/bin/yarn
npm: 6.14.5 - ~/.nvm/versions/node/v14.5.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDK信息:
iOS SDK: 支持平台:DriverKit 21.0.1, iOS 15.0, macOS 12.0, tvOS 15.0, watchO 8.0
IDE信息:
Xcode: 13.1/13A1030d - /usr/bin/xcodebuild
npm依赖包:
react: 16.9.0 => 16.9.0
react-native: 0.61.4 => 0.61.4
npm全局包:
react-native-cli: 2.0.1
复现条件
目前无法稳定复现,已知问题仅在Android7.x系统上偶发,触发时机为调用rootView.startReactApplication(...)时。在Application的onCreate生命周期中,通过createReactContextInBackground()初始化了两个不同的React Instance Manager分别承载两个不同业务功能,第二个实例是在本次新版本中新增引入的。
根因分析
- 主线程卡在
ModuleHolder.getModule的native wait调用上,是React Native 0.61.x版本多ReactInstanceManager场景下的已知锁竞争问题。两个实例并行初始化时,NativeModule初始化流程会争抢全局锁,当主线程调用startReactApplication需要获取UIManager模块时,锁被另一个初始化线程持有,就会进入无限等待。 - Android 7.x的ART虚拟机锁调度、堆任务优先级策略和高版本系统有差异,HeapTaskDaemon等系统线程更容易抢占锁资源,放大了锁等待的概率,最终触发ANR超时。
修复方案
方案1:调整实例初始化时机
不要在Application onCreate中同时初始化两个ReactInstanceManager,采用懒加载策略:
- 保留核心业务对应的实例在Application层预初始化
- 第二个新增实例延迟到对应业务场景启动前再初始化,或者通过IdleHandler在主线程空闲时触发初始化,避免和主流程争抢资源。
方案2:修复React Native版本缺陷
你当前使用的0.61.4版本存在多实例锁死的已知缺陷,可二选一处理:
- 升级React Native到0.63.0及以上版本,官方已在该版本修复了
ModuleHolder.getModule的死锁问题 - 若暂时无法升级版本,可自行给
ModuleHolder类打补丁,移除初始化过程中不必要的同步锁,或者给wait调用添加超时逻辑,避免主线程无限等待。
方案3:Android 7.x专属优化
针对Android 7.x系统做适配,禁用堆任务守护线程抢占锁:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) { try { Class<?> vmRuntimeClazz = Class.forName("dalvik.system.VMRuntime"); Method getRuntimeMethod = vmRuntimeClazz.getMethod("getRuntime"); Object runtimeInstance = getRuntimeMethod.invoke(null); Method setHeapTaskDaemonEnabledMethod = vmRuntimeClazz.getMethod("setHeapTaskDaemonEnabled", boolean.class); setHeapTaskDaemonEnabledMethod.invoke(runtimeInstance, false); } catch (Exception e) { // 异常不影响主流程,可直接忽略 } }
将上述代码添加到Application的onCreate方法中即可。
方案4:调用逻辑优化
调用startReactApplication前先判断对应ReactInstanceManager的上下文是否初始化完成,若还在初始化流程中,先展示占位加载UI,等初始化成功回调触发后再绑定RootView,避免主线程阻塞。
内容的提问来源于stack exchange,提问作者Rishabh876

