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

Posted

技术标签:

【中文标题】如何从 React Native Android 模块访问 Activity?【英文标题】:How to access Activity from a React Native Android module? 【发布时间】:2015-12-18 16:35:21 【问题描述】:

我正在尝试将保持屏幕显示的 android 功能过渡到 React Native。我想我可以用一个简单的模块来做到这一点,但是我不知道如何从所述模块访问当前的 Android Activity。

我需要 Activity 引用,这样我就可以在上面调用.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

我试图通过像 ((Activity)getReactApplicationContext().getBaseContext()) 这样的转换来获取活动,但这会引发“无法转换为 Android.app.Activity”错误

【问题讨论】:

关于android和native组件之间通信的例子。 Repo Link 【参考方案1】:

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)。这是我的代码的要点,以防它帮助任何有类似问题的人 - gist.github.com/mdp/a501edd93632f13fd9f8 如何将当前活动传递给添加包的新方法。 return Arrays.asList(new MainReactPackage(), new ThePackageClass(this) 这个答案从 react-native 29.0 起不再有效,我们在 MainApplication.java 中声明包 @SudoPlz 你是对的。现在您可以扩展ReactContextBaseJavaModule 并调用getCurrentActivity()。但是,这仅适用于原生模块,不适用于原生 UI 组件。我仍然没有找到在SimpleViewManager 类中获取活动的方法... 如何将活动从 ReactApplication::getPackages() 传递给包?我不明白。我找不到任何明确的例子。【参考方案2】:

我猜ReactContextBaseJavaModule的getCurrentActivity()方法可能会像下面的代码一样使用,它是从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 的主仓库,它似乎还活着。我也试图找到任何关于它的删除但我不能。你能分享更多关于它的信息吗? 当然 ReactContextBaseJavaModule.getCurrentActivity 在最新的 RN (0.56) 中工作,并返回 RN 应用程序上唯一的一个活动:MainActivity,除非您使用的是 react-native-navigation。【参考方案3】:

已编辑:

问题在于 getReactApplicationContext() 返回的是应用程序的上下文,而不是活动。您不能将 Application 上下文类型转换为 Activity。

这是一个简单的解决方法

由于react-native中通常只有一个activity(Main Activity),我们可以在MainActivity中写一个静态函数来返回这个activity

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()

【讨论】:

这行得通,但我假设你在 GitHub 上看到了评论:github.com/facebook/react-native/issues/…。我正在使用这种方法,并且效果很好。如果您编辑您的答案以描述该方法,我会接受它。 我还没有考虑创建自己的 ReactPackage 实现。既然你已经想通了,如果你能把你的知识传授给社区,对社区会有很大的帮助 刚刚发布了我自己的答案。看看并试一试。 我是 android 新手,你能告诉我如何在 react native 包中调用 getCurrentActivity() 吗?我正在寻找集成github.com/msmakhlouf/react-native-android-sms 插件,但由于活动this,它似乎不起作用。【参考方案4】:

使用 UI 线程上下文获取活动/窗口/装饰视图

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

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

在你的类构造函数中...

private static ReactApplicationContext reactContext;
MyModule(ReactApplicationContext context) 
  super(context);
  reactContext = context;

然后在for类方法中...

@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
    
  );

【讨论】:

【参考方案5】:

自定义UI组件,可以在构造函数中获取上下文,后面可以通过context.getCurrentActivity()获取activity

【讨论】:

兄弟我有点困惑。我正在尝试使用“GoogleSignIn.getAccountForExtension”,但无法将上下文链接到它。请查看***.com/questions/65948333/… @grooot 兄弟链接失效了 请检查这个兄弟。 ***.com/questions/65950331/…【参考方案6】:

对于 0.28 及之前的版本,您可以使用相同的字段从 ReactInstanceManagerImplprivate @Nullable Activity mCurrentActivity;、0.29+ 的 ReactContextBaseJavaModule 获取活动。

【讨论】:

【参考方案7】:

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

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

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;
  

答案:您不会将 Activity 从 MainApplication 传递到包。

您从 ReactContextBaseJavaModule 中调用 getCurrentActivity()

【讨论】:

没关系。 ReactContextBaseJavaModule 类有一个 getCurrentActivity() 方法。【参考方案8】:

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

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

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

注意ReactApplicationContext#getCurrentActivity包私有。所以你不能使用reactContext.getCurrentActivity()

ReactContextBaseJavaModule 受到保护。因此,推导出它可以实现:
protected @Nullable final Activity getCurrentActivity() 
    return mReactApplicationContext.getCurrentActivity();
  
ReactApplicationContext 是包私有的(默认访问)。所以你无法得到它:
/* package */ @Nullable Activity getCurrentActivity() 
    return mCurrentActivity;
  

布诺斯

既然是@Nullable,请务必检查它的可用性:

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

【讨论】:

有趣,你如何添加现有的原生代码/ui? ***.com/questions/69562692/… 嗯。我会发布答案【参考方案9】:

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 传递给从 github.com/facebook/react-native/blob/3b4845f93c296ed93c348733809845d587227823/local-cli/generator 中调用的 ReactInstanceManager.builder() 调用的模块的初始化程序-android/templates/package/MainActivity.java#L28 通过 MainReactPackage github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java 的实例化#L49

【讨论】:

没有说明如何获取Activity。【参考方案10】:

我需要修改一个过时的模块 (react-android-360-video),发现这很有帮助...

android/app/src/main/java/com/webcdpmobiledemo/MainApplication.java中,我使用new格式来添加包:

...
import com.vrvideocomponent.VrVideoViewPackage;

public class MainApplication extends Application implements ReactApplication 

    ...

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) 
      ...

      @Override
      protected List<ReactPackage> getPackages() 
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new VrVideoViewPackage()
        );
      

      ...
    ;

    ...

android/app/src/main/java/com/webcdpmobiledemo/MainActivity.java 基本上是空的:

package com.yourproject;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity 

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() 
        return "YourProject";
    

然后,我修改了VrVideoViewPackage文件,需要将reactContext传递给它调用的VrVideoViewManager

...

public class VrVideoViewPackage implements ReactPackage 
    ...

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) 
        return Arrays.<ViewManager>asList(
                new VrVideoViewManager(reactContext)
        );
    


最后,在VrVideoViewManager 中,可以像这样访问活动:

...

import android.app.Activity;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.bridge.ReactContext;

...

public class VrVideoViewManager extends SimpleViewManager<VrVideoView> 
    ...

    public VrVideoViewManager(ReactContext reactContext) 
        // Do not store mActivity, always getCurrentActivity when needed
        Activity mActivity = mContext.getCurrentActivity();
    

    @Override
    protected VrVideoView createViewInstance(ThemedReactContext reactContext) 

        // You can also activity from ThemedReactContext
        Activity mActivity = reactContext.getCurrentActivity();

        VrVideoView vrView = new VrVideoView(mActivity);
        vrView.setEventListener(new ActivityEventListener(vrView));
        vrView.pauseVideo();
        return new VrVideoView(mActivity);
    

    ...

【讨论】:

Upto Package 没问题,之后呢?我必须从模块“公共类 DeviceModule 扩展 ReactContextBaseJavaModule”中获取 DeviceToken 一个字符串值作为来自 .js 的“PackageReference.ReactOverridemethod.StringValue”???? @Ramaraju 我不确定我是否理解你的问题,你能改写一下吗?我提供的解决方案是在 ReactPackage 类和 SimpleViewManager 类中提取活动。如果您想从 ReactContextBaseJavaModule 类中提取,您可以查看 efkan 的答案

以上是关于如何从 React Native Android 模块访问 Activity?的主要内容,如果未能解决你的问题,请参考以下文章

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

如何从 iOS 中删除 react-native-firebase,同时将其保留在 Android 中?

如何修复这个 react-native iOS 错误?

如何让 React Native 仅在 Android 操作系统上从包中获取/导入功能

React-native-android - 如何将图像保存到Android文件系统并在手机的“图库”中查看

如何从 React-native 项目中删除一个添加的权限?