AndroidからReact Nativeアプリ呼び出し(Java)

やりたいこと

既にあるReact NativeアプリをJavaかKotlinで呼び出して組み込みたいという需要があったときに、 どんな感じでやればいいかを試してみます。 (Java

実現したいこと

Java製の画面にボタンがあって、ボタンを押すとReact Nativeの画面に遷移するのをやりたいです。

React Nativeをbundle化

  1. React Nativeアプリを作成 teisyoku-tabetai.hatenablog.com

  2. React Nativeをbundle化

assetsフォルダがない場合は作成します。

mkdir android/app/src/main/assets

bundle化

npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res

index.android.bundleができていれば完了です。

React Native関連のモジュールの用意

ここのところの方法は、いろいろ試した挙句できなかったので、以下の参考文献の内容を記述させていただいています。

nishabe.medium.com

mkdir integratedReactNativeProject
cd integratedReactNativeProject && touch package.json

package.jsonの中身

{
  "name": "sampleReactApp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "yarn react-native start"
  }
}
npm i yarn
yarn add react-native

Androidプロジェクト作成 & React Nativeの依存関係を記述

  1. Android StudioAndroidプロジェクトを作成
    node_modulesと同階層に移動させる。

  2. Androidプロジェクトに依存関係を記述

プロジェクト配下のbuild.gradle(appディレクトリと同階層)に以下を追記

allprojects {
    repositories {
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url ("$rootDir/../node_modules/react-native/android")
        }
        maven {
            // Android JSC is installed from npm
            url("$rootDir/../node_modules/jsc-android/dist")
        }
        google()
        jcenter()

    }
}
  • app配下のbuild.gradleに以下を追記(※1)
dependencies {
    ...
    implementation "com.facebook.react:react-native:+" // From node_modules
    implementation "org.webkit:android-jsc:+"
}

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
applyNativeModulesAppBuildGradle(project)

(※1) implementation "com.facebook.react:react-native:+" について
わざわざnpmでReact Nativeを入れてnode_modulesから引っ張ってきてるのはmavenから取ってこれなかったため。(2023年3月現在) Gradleあまり詳しくないので、もっと良い方法がありそう... central.sonatype.com

  • gradle.propertiesに以下を追加
android.enableJetifier=true
  • settings.gradleに以下を追記(※2)
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); 
applyNativeModulesSettingsGradle(settings)

(※2) プロジェクト配下のbuild.gradleにrepositoriesを追記したので、settings.gradleにもある場合はコメントアウト(二重で記述するとエラー)

3 起動用のActivityを追加

package com.example.myapplication; // プロジェクト名に応じて変更
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;

import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.soloader.SoLoader;

import java.util.List;


public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SoLoader.init(this, false);

        mReactRootView = new ReactRootView(this);
        List<ReactPackage> packages = new PackageList(getApplication()).getPackages();

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setCurrentActivity(this)
                .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index")
                .addPackages(packages)
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager, "sampleReactApp", null);  // (※3)

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }

}

(※3) 元のReact Nativeプロジェクトのプロジェクト名に合わせる必要がある。プロジェクト名が分からなかったらindex.android.bundleから探せば行けるかもです。

4 Android Studioで実行
Android StudioからReact Nativeアプリを呼び出すことができました。
サンプル画面のロゴ画像が表示できていませんが、あまり気にしないこととします。

画面遷移を作る

  1. 先に表示させるAndroid画面用のアクティビティを作成

MainActivity.java

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);

        textView = findViewById(R.id.text_view);

        button.setOnClickListener( v -> {
                this.onClick();
        });
    }

    public void onClick() {
        Intent intent = new Intent();
        intent.setClassName(getApplicationContext(), "com.example.myapplication.MyReactActivity");
        startActivity(intent);
    }
}

2 MainActivity用のActivityを追加

<activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MyReactActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

3 app/src/main/res/layoutのactivity_main.xmlを編集

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Androidアプリ"
        android:textSize="34sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.281" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#42CC47"
        android:backgroundTint="#4CAF50"
        android:text="React Nativeアプリへ遷移"
        android:textColorHint="#FFFFFF"
        android:textColorLink="#FFFFFF"
        android:textSize="16sp"
        app:iconTint="#4CAF50"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.6"
        app:rippleColor="#4CAF50" />

</androidx.constraintlayout.widget.ConstraintLayout>

以上の手順で、冒頭に示したような、AndroidからReact Nativeへの画面遷移を実現できました。