如何从React Native Android模块访问Activity?

43

我试图通过一个简单的模块将 Android 屏幕常亮功能应用于 React Native,但我不知道如何从该模块中获取当前 Android Activity 的访问权限。

为了实现这个目标,我需要获取 Activity 引用,以便在其上调用 .getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

我尝试通过强制转换来获取 Activity,例如:((Activity)getReactApplicationContext().getBaseContext()),但是会抛出“无法转换为 Android.app.Activity”错误。


有关Android和本地组件之间通信的示例,请参见[Repo Link](http://github.com/amalChandran/ReactNative_Android_integration)。 - amalBit
11个回答

42

CustomReactPackage.java:

public class CustomReactPackage implements ReactPackage {

    private Activity mActivity = null;

    public CustomReactPackage(Activity activity) {
        mActivity = activity;
    }

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        // Add native modules here
        return modules;
    }

    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        List<ViewManager> modules = new ArrayList<>();
        // Add native UI components here
        modules.add(new LSPlayerManager(mActivity));
        return modules;
    }
}

LSPlayerManager 是我的本地 UI 组件。我定义了一个构造函数,以便可以传入活动:

public LSPlayerManager(Activity activity) {
    mActivity = activity;
}

最后在 MainActivity.java 中,定义了 ReactInstanceManager,我们可以将该活动传递给我们的自定义 React 包:

mReactInstanceManager = ReactInstanceManager.builder()
        .setApplication(getApplication())
        .setBundleAssetName("index.android.bundle")
        .setJSMainModuleName("src/index.android")
        .addPackage(new MainReactPackage())
        .addPackage(new CustomReactPackage(this)) // <--- LIKE THIS!
        .setUseDeveloperSupport(BuildConfig.DEBUG)
        .setInitialLifecycleState(LifecycleState.RESUMED)
        .build();

React Native 0.29.0 更新说明

这不再是在本地模块中访问活动的方式,请参阅 https://github.com/facebook/react-native/releases/tag/v0.29.0 获取迁移说明。



这绝对是最好的答案,也正是我在自己的项目中所做的。在我的情况下,我正在React Native中使用嵌入式ZXing QR码扫描器,并且需要启动扫描并读取结果(onActivityResult)。如果有人遇到类似的问题,这是我的代码要点 - https://gist.github.com/mdp/a501edd93632f13fd9f8 - mdp
如何将当前活动传递给添加包的新方法?返回Arrays.<ReactPackage>asList( new MainReactPackage(), new ThePackageClass(this) <-- 这里的this不起作用 ); 这里的this不起作用。 - Kenneth P.
1
从react-native 29.0及以后的版本开始,我们需要在MainApplication.java中声明包,因此此答案已不再有效。 - SudoPlz
9
@SudoPlz 你是对的。现在你可以扩展 ReactContextBaseJavaModule 并调用 getCurrentActivity()。然而,这只适用于原生模块,而不是原生 UI 组件。我仍然没有找到在 SimpleViewManager 类中获取活动的方法... - marcshilling
1
从ReactApplication :: getPackages()中如何将活动传递到包?我不明白。我找不到任何清晰的例子。 - Ed of the Mountain

41

我猜 getCurrentActivity() 方法在ReactContextBaseJavaModule中被使用,就像下面这段代码一样,它是从React-Native原始代码中复制过来的;

import android.app.Activity;
import com.facebook.react.bridge.ReactContextBaseJavaModule;

public class AwesomeModule extends ReactContextBaseJavaModule {

  public AwesomeModule(ReactApplicationContext reactContext) {
    super(reactContext);
  }

  @Override
  public String getName() {
    return "AwesomeAndroid";
  }

  private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY";
  private static final String ERROR_NO_ACTIVITY_MESSAGE = "Tried to do the something while not attached to an Activity";

  @ReactMethod
  public void doSomething(successCallback, errorCallback) {

    final Activity activity = getCurrentActivity();

    if (activity == null) {
      errorCallback(ERROR_NO_ACTIVITY, ERROR_NO_ACTIVITY_MESSAGE);
      return;
    }

  }

}

我在回答中提供的链接指定了react-native的主存储库,它似乎是有效的。此外,我尝试查找有关其删除的任何信息,但我找不到。您能否分享更多相关信息? - efkan
1
当然,ReactContextBaseJavaModule.getCurrentActivity在最新的RN(0.56)中是有效的,并且返回RN应用程序中唯一的一个活动:MainActivity,除非您正在使用react-native-navigation。 - aMarCruz

9

编辑后:

问题在于getReactApplicationContext()返回的上下文是应用程序的上下文而不是活动的上下文。你无法将应用程序上下文强制转换为活动上下文。

这是一个简单的解决方法

由于通常在react-native中只有一个活动(主活动),我们可以在MainActivity中编写一个静态函数来返回该活动。

private static Activity mCurrentActivity = null;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mCurrentActivity = this;
    ...
}
...
public static Activity getActivity(){
    Activity activity = new Activity();
    activity = mCurrentActivity;
    return activity;
}

然后从桥接模块中调用MainActivity.getActivity()

1
这个方法可行,但我假设你在 GitHub 上看到了评论:https://github.com/facebook/react-native/issues/2996#issuecomment-142913032。我正在使用那种方法,它运行得非常好。如果你编辑你的答案来描述那种方法,我会接受它。 - marcshilling
1
我还没有研究过创建自己的ReactPackage实现。既然你已经弄清楚了,如果你能传授你的知识,那将对社区有很大帮助。 - Nishanth Shankar
我刚刚发布了自己的答案。看看并试一试吧。 - marcshilling
我是Android的新手,你能告诉我如何在React Native包中调用getCurrentActivity()吗?我想要集成https://github.com/msmakhlouf/react-native-android-sms插件,但由于活动“this”的原因,它似乎无法正常工作。 - Mohit Pandey

2

如果您想在ReactContextBaseJavaModule子类中获取前台活动的当前实例,请使用:

// class MyReactModule extends ReactContextBaseJavaModule
Activity activity = getCurrentActivity();

这来自于ReactContextBaseJavaModule#getCurrentActivity,它使用了reactContext.getCurrentActivity()

注意ReactApplicationContext#getCurrentActivity包私有(package private)的。因此你不能使用reactContext.getCurrentActivity()

  • ReactContextBaseJavaModule是受保护的。因此派生它可以实现:
protected @Nullable final Activity getCurrentActivity() {
    return mReactApplicationContext.getCurrentActivity();
  }
  • ReactApplicationContext是包私有的(默认访问权限)。因此,您无法获取它:
/* package */ @Nullable Activity getCurrentActivity() {
    return mCurrentActivity;
  }

Bunos

Since it's @Nullable, make sure you check it's availability:

if (reactContext.hasCurrentActivity()) {
   // There is. So get it
   Activity theActivity = getCurrentActivity();
}

有趣,你知道如何添加现有的本地代码/用户界面吗?https://dev59.com/Hqn2oIgBc1ULPQZFGt2N - Wils
嗯,我会发布一个答案。 - Mahdi-Malv

2

使用UI线程上下文获取Activity / Window / Decor View

在你的类文件导入中包括...

import android.view.WindowManager;
import android.view.Window;
import android.view.View;

在您的类构造函数中...
private static ReactApplicationContext reactContext;
MyModule(ReactApplicationContext context) {
  super(context);
  reactContext = context;
}

然后在类方法中...
@ReactMethod
public void doSomethingRequiringWindow() {
  // get access to current UI thread first
  reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
      Window window = reactContext
       .getCurrentActivity().getWindow();
      View decorView = reactContext
       .getCurrentActivity().getWindow().getDecorView();
      // you have access to main ui thread so you can effect
      // immediate behavior on window and decorView
    }
  });
}

1

对于自定义UI组件,您可以在构造函数中获取上下文,稍后可以通过context.getCurrentActivity()获取活动


兄弟,我有点困惑。我正在尝试使用“GoogleSignIn.getAccountForExtension”,但无法将上下文链接到它。请查看此链接:https://stackoverflow.com/questions/65948333/null-object-reference-while-using-googlesignin - grooot
@grooot兄弟,链接已失效。 - chengsam
请检查这个链接:https://dev59.com/Gr_qa4cB1Zd3GeqPDkFq - grooot

0
对于0.28及之前的版本,你可以从ReactInstanceManagerImplprivate @Nullable Activity mCurrentActivity;获取activity。而在0.29+版本中,你可以使用相同的字段从ReactContextBaseJavaModule获取activity。

0

我的代码是这样的 final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()); 但运行时出现了错误


0

如何从ReactApplication::getPackages()将activity传递给包?

我不明白,找不到任何明确的示例。

package com.bluetoothioexample;

import android.app.Application;
import android.util.Log;

import com.facebook.react.bridge.ReactContextBaseJavaModule;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

import com.subsite.bluetoothio.BluetoothIOPackage;
import com.oblador.vectoricons.VectorIconsPackage;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new BluetoothIOPackage(this), //<- How pass the activity
                                        // from ReactApplication ?
          new VectorIconsPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}

答案:您不需要从MainApplication传递Activity到包中。
您可以在ReactContextBaseJavaModule中调用getCurrentActivity()。

1
没关系。ReactContextBaseJavaModule类有一个getCurrentActivity()方法。 - Ed of the Mountain

-1

https://github.com/facebook/react-native/blob/master/docs/NativeModulesAndroid.md中并不是非常清楚,但你可以阅读React Native分发中ToastModule的源代码来看他们是如何做到的 github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/modules/toast/ToastModule.java#L31-L33

这个想法是将主活动作为reactContext传递给模块的初始化器,在通过MainReactPackage的实例化github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java#L49中从ReactInstanceManager.builder()调用的模块中被调用的MainActivity.java#L28。


不解释如何获取Activity。 - marcshilling

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接