(Android) getCurrentActivity() sometimes returns null inside ReactContextBaseJavaModule.getConstants()
还没有人认领这个 Issue。
- 主要语言
- C++
- 星标
- 127k
- 派生
- 25.3k
- 平均合并
- 1 天 23 小时
- 30 天内合并 PR
- 4
描述
Description
ReactContextBaseJavaModule has a method called getCurrentActivity(). When used inside ReactContextBaseJavaModule.getConstants() (actually, BaseJavaModule.getConstants()) - namely, during app launch (Javascript init), it sometimes returns the current activity, and sometimes null in what seems to be a random behavior.
React Native Version
0.70.7
Output of npx react-native info
System:
OS: macOS 12.5.1
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 42.30 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 14.18.2 - ~/.nvm/versions/node/v14.18.2/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v14.18.2/bin/yarn
npm: 6.14.18 - ~/.nvm/versions/node/v14.18.2/bin/npm
Watchman: 2022.07.04.00 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.10.1
SDKs:
iOS SDK:
Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1
Android SDK:
Android NDK: 19.2.5345600
IDEs:
Android Studio: 2022.2 AI-222.4459.24.2221.9971841
Xcode: 14.2/14C18 - /usr/bin/xcodebuild
Languages:
Java: 17.0.6 - /usr/bin/javac
npmPackages:
@react-native-community/cli: Not Found
react: 18.1.0 => 18.1.0
react-native: 0.70.7 => 0.70.7
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
Steps to reproduce
⚠️ This is a theoretical guide that I haven't tried applying manually, but it should work.
- Run
react-native init - Open the android source code of it using Android Studio.
- Create a fresh stub
NativeModulethat looks roughly like so:
public class StubNativeModule extends ReactContextBaseJavaModule {
StubNativeModule(ReactApplicationContext context) {
super(context);
}
@NonNull
@Override
public String getName() {
return "StubNativeModule";
}
@Override
public Map<String, Object> getConstants() {
Activity activity = getCurrentActivity();
Log.e("StubNM", "Has activity?" + (activity != null));
return null;
}
// Required due to an old RN bug
@ReactMethod
public void oldBugWorkaround() {}
}
- Register the stub native module in a native package and add to your
ReactNativeHost.getPackages()method. - In
index.js(orapp.js), add the following import at the top:
import { NativeModules } from "react-native";
const stubConsts = NativeModules.StubNativeModule.value;
- In command line, launch the app repeatedly (i.e. using
react-native run-android), and follow the logs usinglogcat. You will end up seeing something like this, in consequent runs:
StubNM E Has activity? true
...
StubNM E Has activity? true
...
StubNM E Has activity? false
...
StubNM E Has activity? true
...
StubNM E Has activity? false
(and so on and so forth)
Snack, code example, screenshot, or link to a repository
For starters, this report is in fact the reason behind this bug, which has been reported by me to the react-native-launch-arguments repo.
As for the problem itself - it is rooted in the handling of mCurrentActivity, here:
public void onHostResume(@Nullable Activity activity) {
mLifecycleState = LifecycleState.RESUMED;
mCurrentActivity = new WeakReference(activity);
// ...
}
While mCurrentActivity is set in onHostResume(), which is a bit late compared to when the activity gets initially created, getConstants() can be called by RN's initialization sequence in very early stages. Add to that the fact the two occur in an asynchronous way (i.e. in different, async threads) - you get that the call to getCurrentActivity() is very likely to miss the completion of the initialization of ReactContext.mCurrentActivity. These circumstances induce the mentioned behavior, which is highly nondeterministic and "feels random".
Solutions
There are quite a few nonoverlapping alternatives to how this can be resolved:
- Have the RN context start holding the activity upon creation, rather than wait for it to be resumed. Accordingly, clean-up might have to move to the destroyed phase. This is the most straightforward way but likely also the one introducing the most risk.
- Dispatch the native-modules thread (more precisely, its initialization) upon activity resume.
- Perform the native-modules init inside the existing thread in a more synchronous way, waiting for the activity to be available before moving forward with initialization.
- Enable native-modules (or native-packages) to specify various constraints, one of which could be the activity's state; Then, break the current init loop onto several ones based on constraints fulfillment.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java 开始,尤其关注 onHostResume(),并跟踪 ReactContextBaseJavaModule.getConstants() 和 BaseJavaModule 初始化是如何被调用的。使用 StubNativeModule 示例和 logcat 重现 activity 间歇性可用的情况。完成标准是达成一致的生命周期或初始化设计,使 getCurrentActivity() 在加载常量期间的行为具有确定性。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- android, java, react-native
- 领域
- mobile-dev
- Issue 类型
- 缺陷
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100