26

I am using REACT-NATIVE to build android app. I want to call android activity from React-Native code. (say when I clicked the button in my react native code, it should call android activity)

I have 4 class files

  • MainActivity.java (created by react-native when opened in android studio)
  • MainApplication.java (created by react-native)
  • Login.java (android activity file)
  • Example.java (android activity file)

Want to achieve following flow:

Login.java -> React-Native js -> Example.java

I already went through following links, but unable to understand

https://stackoverflow.com/a/32825290/4849554

Similar question asked here

React Native Android: Showing an Activity from Java

Community
  • 1
  • 1
Ritesh
  • 1,030
  • 2
  • 12
  • 28

1 Answers1

50

To start an Android activity, you need to create a custom native module. Assume one called ActivityStarter; it might be used from JavaScript as follows:

import { ..., NativeModules, ... } from 'react-native';

export default class DemoComponent extends Component {
    render() {
        return (
        <View>
            <Button
                onPress={() => NativeModules.ActivityStarter.navigateToExample()}
                title='Start example activity'
            />
        </View>
        );
    }
}

ActivityStarter is just a Java class that implements a React Native Java interface called NativeModule. The heavy lifting of this interface is already done by BaseJavaModule, so one normally extends either that one or ReactContextBaseJavaModule:

class ActivityStarterModule extends ReactContextBaseJavaModule {

    ActivityStarterModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

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

    @ReactMethod
    void navigateToExample() {
        ReactApplicationContext context = getReactApplicationContext();
        Intent intent = new Intent(context, ExampleActivity.class);
        context.startActivity(intent);
    }
}

The name of this class doesn't matter; the ActivityStarter module name exposed to JavaScript comes from the getName() method.

The default app generated by react-native init contains a MainApplication class that initializes React Native. Among other things it extends ReactNativeHost to override its getPackages method:

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

If you're adding React Native to an existing app, this page has you override your Activity's onCreate as follows:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mReactRootView = new ReactRootView(this);
    mReactInstanceManager = ReactInstanceManager.builder()
            .setApplication(getApplication())
            .setBundleAssetName("index.android.bundle")
            .setJSMainModuleName("index.android")
            .addPackage(new MainReactPackage())
            .setUseDeveloperSupport(BuildConfig.DEBUG)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build();
    mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);

    setContentView(mReactRootView);
}

Note addPackage(new MainReactPackage()). Regardless of which approach you use, you need to add a custom package that exposes our custom module. It might look like this:

class ActivityStarterReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new ActivityStarterModule(reactContext));
        return modules;
    }

    // UPDATE: This method was deprecated in 0.47
    // @Override
    // public List<Class<? extends JavaScriptModule>> createJSModules() {
    //     return Collections.emptyList();
    // }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

Finally, update MainApplication to include our new package:

@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new ActivityStarterReactPackage(), // This is it!
            new MainReactPackage()
    );
}

Or you can do addPackage(new ActivityStartecReactPackage()) to ReactInstanceManager.builder().

You can find a complete, self-contained example here.


UPDATE

createJSModules was removed from the ReactPackage interface in version 0.47, and has been commented out of the sample. You'll still need it if you're stuck with an older version of RN for some reason.


UPDATE MARCH 2019

The sample project now supports similar functionality for iOS.

Petter Hesselberg
  • 5,062
  • 2
  • 24
  • 42
  • Hi @peter can you please tell whether this is possible to do? One Android app is integrated with RN..where page A is in Android and page B is in RN..on click of a button in page A, RN activity started and page B get loaded..so my Q is can the button on page A can be RN component instead of native Android – Aravind S Sep 11 '18 at 18:27
  • @Peter, Would it be possible to contact you privately? – Paul Sep 12 '18 at 22:25
  • If it's via button's interface then I am able to use it. My requirement is a bit different. I want to launch activity directly without any interactions. Is it possible – Jimit Patel Apr 10 '19 at 06:12
  • I don't think that should be a problem. `NativeModules.ActivityStarter.navigateToExample()` doesn't have to be called as the result of clicking a button. – Petter Hesselberg Apr 10 '19 at 07:54
  • Hi، im tring to the similar thing. I want to launch my app with some paramter to route in headlesstask forground service. I tried your code in my module with main action. got the error. Cant find activity for intent. Can you help me pleae. – shahrooz.bazrafshan Sep 16 '19 at 14:43