How to access Activity from a React Native Android module?

前端 未结 9 616
星月不相逢
星月不相逢 2020-12-04 21:53

I\'m attempting to bridge over the Android functionality of keeping the screen on to React Native. I figured I could do this with a simple module, however I don\'t know how

相关标签:
9条回答
  • 2020-12-04 22:06

    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 is my native UI component. I define a constructor so that I can pass in the activity:

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

    And finally in MainActivity.java where the ReactInstanceManager is defined, we can pass the activity to our custom React package:

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

    UPDATE FOR REACT NATIVE 0.29.0

    This is no longer how you access activity in a native module. See https://github.com/facebook/react-native/releases/tag/v0.29.0 for migration instructions

    0 讨论(0)
  • 2020-12-04 22:08

    It's not super clear from https://github.com/facebook/react-native/blob/master/docs/NativeModulesAndroid.md, but you can read the source code of the ToastModule in the react native distribution to see how they do it github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/modules/toast/ToastModule.java#L31-L33

    The idea is that the main activity will be passed as the reactContext in the initializer for the module called from ReactInstanceManager.builder() call in github.com/facebook/react-native/blob/3b4845f93c296ed93c348733809845d587227823/local-cli/generator-android/templates/package/MainActivity.java#L28 via the instantiation of MainReactPackage github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java#L49

    0 讨论(0)
  • 2020-12-04 22:11

    How do you pass the activity to a package from ReactApplication::getPackages() ?

    I do not understand. I cannot find any clear examples

    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;
      }
    }
    

    Answer: You do NOT pass Activity from MainApplication to the package.

    You call getCurrentActivity() from within your ReactContextBaseJavaModule

    0 讨论(0)
  • 2020-12-04 22:14

    I needed to modify an outdated module (react-android-360-video) and found this helpful...

    In android/app/src/main/java/com/webcdpmobiledemo/MainApplication.java, I used the new format for adding a package:

    ...
    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()
            );
          }
    
          ...
        };
    
        ...
    }
    

    And android/app/src/main/java/com/webcdpmobiledemo/MainActivity.java is essentially empty:

    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";
        }
    }
    

    Then, I modified the VrVideoViewPackage file, which needs to pass the reactContext to the VrVideoViewManager it calls:

    ...
    
    public class VrVideoViewPackage implements ReactPackage {
        ...
    
        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Arrays.<ViewManager>asList(
                    new VrVideoViewManager(reactContext)
            );
        }
    
    }
    

    And finally, in the VrVideoViewManager the activity can be accessed like so:

    ...
    
    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);
        }
    
        ...
    }
    
    0 讨论(0)
  • 2020-12-04 22:19

    I guess getCurrentActivity() method of ReactContextBaseJavaModule might be used like the following code which has been copied from React-Native original code;

    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 讨论(0)
  • 2020-12-04 22:21

    Get Activity / Window / Decor View with UI Thread context

    In your class file imports include...

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

    In your class constructor...

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

    Then in for class methods...

    @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
        }
      });
    }
    
    0 讨论(0)
提交回复
热议问题