如何从 React Native Android 模块访问 Activity?
How to access Activity from a React Native Android module?
我正在尝试桥接 Android 将屏幕保持在 React Native 上的功能。我想我可以用一个简单的模块来做到这一点,但是我不知道如何从所述模块访问当前的 Android Activity。
我需要 Activity 参考,这样我就可以调用 .getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
我试图像这样通过转换获得 activity ((Activity)getReactApplicationContext().getBaseContext())
,但这会引发 "cannot be cast to Android.app.Activity" 错误
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
想法是主 activity 将作为 reactContext 在 github.com/facebook/react-native/blob/3b4845f93c296ed93c348733809845d587227823/local-cli/generator-android/templates/package/MainActivity 中从 ReactInstanceManager.builder() 调用的模块的初始化程序中传递.java#L28 通过实例化 MainReactPackage github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java#L49
已编辑:
问题是 getReactApplicationContext() return 是应用程序的上下文,而不是 Activity。您不能将应用程序上下文强制转换为 Activity。
这是一个简单的解决方法
由于react-native中通常只有一个activity(MainActivity),我们可以在MainActivity中写一个静态函数到return 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()
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 组件。我定义了一个构造函数,以便我可以传入 activity:
public LSPlayerManager(Activity activity) {
mActivity = activity;
}
最后在定义了 ReactInstanceManager
的 MainActivity.java
中,我们可以将 activity 传递给我们的自定义 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 更新
这不再是您在本机模块中访问 activity 的方式。有关迁移说明,请参阅 https://github.com/facebook/react-native/releases/tag/v0.29.0
我猜想 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;
}
}
}
对于 0.28 及之前的版本,您可以使用相同的字段从 ReactInstanceManagerImpl
的 private @Nullable Activity mCurrentActivity;
、0.29+ 的 ReactContextBaseJavaModule
中获取 activity。
如何将 activity 传递给来自 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()
我需要修改一个过时的模块 (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
中可以像这样访问 activity:
...
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);
}
...
}
使用 UI 线程上下文获取 Activity / Window / 装饰视图
在您的 class 文件中导入包括...
import android.view.WindowManager;
import android.view.Window;
import android.view.View;
在你的 class 构造函数中...
private static ReactApplicationContext reactContext;
MyModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
然后在 class 方法中...
@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
}
});
}
对于自定义的UI组件,可以在构造函数中获取context,之后可以通过context.getCurrentActivity()
获取activity
如果你想在 ReactContextBaseJavaModule
子类中获取前景 activity 的当前实例,请使用:
// class MyReactModule extends ReactContextBaseJavaModule
Activity activity = getCurrentActivity();
它来自 ReactContextBaseJavaModule#getCurrentActivity
,它使用 reactContext.getCurrentActivity()
Note: ReactApplicationContext#getCurrentActivity
is package private. So you can not use reactContext.getCurrentActivity()
.
ReactContextBaseJavaModule
's 受保护。所以推导它使它可以实现:
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();
}
我正在尝试桥接 Android 将屏幕保持在 React Native 上的功能。我想我可以用一个简单的模块来做到这一点,但是我不知道如何从所述模块访问当前的 Android Activity。
我需要 Activity 参考,这样我就可以调用 .getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
我试图像这样通过转换获得 activity ((Activity)getReactApplicationContext().getBaseContext())
,但这会引发 "cannot be cast to Android.app.Activity" 错误
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
想法是主 activity 将作为 reactContext 在 github.com/facebook/react-native/blob/3b4845f93c296ed93c348733809845d587227823/local-cli/generator-android/templates/package/MainActivity 中从 ReactInstanceManager.builder() 调用的模块的初始化程序中传递.java#L28 通过实例化 MainReactPackage github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java#L49
已编辑:
问题是 getReactApplicationContext() return 是应用程序的上下文,而不是 Activity。您不能将应用程序上下文强制转换为 Activity。
这是一个简单的解决方法
由于react-native中通常只有一个activity(MainActivity),我们可以在MainActivity中写一个静态函数到return 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()
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 组件。我定义了一个构造函数,以便我可以传入 activity:
public LSPlayerManager(Activity activity) {
mActivity = activity;
}
最后在定义了 ReactInstanceManager
的 MainActivity.java
中,我们可以将 activity 传递给我们的自定义 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 更新
这不再是您在本机模块中访问 activity 的方式。有关迁移说明,请参阅 https://github.com/facebook/react-native/releases/tag/v0.29.0
我猜想 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;
}
}
}
对于 0.28 及之前的版本,您可以使用相同的字段从 ReactInstanceManagerImpl
的 private @Nullable Activity mCurrentActivity;
、0.29+ 的 ReactContextBaseJavaModule
中获取 activity。
如何将 activity 传递给来自 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()
我需要修改一个过时的模块 (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
中可以像这样访问 activity:
...
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);
}
...
}
使用 UI 线程上下文获取 Activity / Window / 装饰视图
在您的 class 文件中导入包括...
import android.view.WindowManager;
import android.view.Window;
import android.view.View;
在你的 class 构造函数中...
private static ReactApplicationContext reactContext;
MyModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
然后在 class 方法中...
@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
}
});
}
对于自定义的UI组件,可以在构造函数中获取context,之后可以通过context.getCurrentActivity()
如果你想在 ReactContextBaseJavaModule
子类中获取前景 activity 的当前实例,请使用:
// class MyReactModule extends ReactContextBaseJavaModule
Activity activity = getCurrentActivity();
它来自 ReactContextBaseJavaModule#getCurrentActivity
,它使用 reactContext.getCurrentActivity()
Note:
ReactApplicationContext#getCurrentActivity
is package private. So you can not usereactContext.getCurrentActivity()
.
ReactContextBaseJavaModule
's 受保护。所以推导它使它可以实现:
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(); }