Repository: react-native
Issue: (Android) getCurrentActivity() sometimes returns null inside ReactContextBaseJavaModule.getConstants()
Labels: Platform: Android,Needs: Author Feedback,Needs: Repro,Newer Patch Available

### 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.

1. Run `react-native init`
2. Open the android source code of it using Android Studio.
3. Create a fresh stub `NativeModule` that looks roughly like so:
```java
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() {}
}
```
4. Register the stub native module in a native package and add to your `ReactNativeHost.getPackages()` method.
5. In `index.js` (or `app.js`), add the following import at the top:
```js
import { NativeModules } from "react-native";
const stubConsts = NativeModules.StubNativeModule.value;
```
6. In command line, launch the app repeatedly (i.e. using `react-native run-android`), and follow the logs using `logcat`. 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](https://github.com/iamolegga/react-native-launch-arguments/issues/44), 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](https://github.com/facebook/react-native/blob/0.71-stable/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java#L267):
```java
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:
1. 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.
1. Dispatch the native-modules thread (more precisely, its initialization) upon activity resume.
1. 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.
1. 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.