React Nativeからaar形式のアプリを呼び出す
やりたいこと
aar形式のアプリケーションが配布されてて、それをReact Nativeアプリから呼び出したい。
完成したもの
そもそもaarとは
Androidアプリケーションやライブラリプロジェクトを配布するためのバイナリ形式。
Androidライブラリプロジェクトのコンパイル済みコード、リソース、およびメタデータを1つのアーカイブにまとめたもの。
これにより、再利用可能なコンポーネントを簡単に他のAndroidアプリケーションに組み込むことができる。
やってみる
3つの手順に分けて説明していきます。
Ⅰ. Androidで画面遷移を作る
Ⅱ. 遷移先の画面をAAR化
Ⅲ. 遷移前の画面をReact Nativeにする
いきなりReact NativeからAARを呼び出したらなんだかもやっとなりそうなので、
Ⅰ. Android(Java)コード→Android(Java)コード
Ⅱ. Android(Java)コード→AAR
Ⅲ. React Nativeコード→AAR
と段階を踏んでいきますが、周りくどかったら思いっきり下までスクロールしてください。
Ⅰ. Androidで画面遷移を作る
Android Studioでアンドロイドプロジェクトを作成 File → New → New Project
モジュールを作成 File → New Module → Android Library
プロジェクト直下に、上で指定した名前のライブラリができていればok。(以下mylibraryとして説明)
appディレクトリ配下にも通常通りJavaファイルを置けるが、aar形式に変換されるのは「mylibrary」以下のもの。
なので、ライブラリ化したコードは「mylibrary」のsrcディレクトリに入れる。
- モジュールに画面を書いてみる aar化する前にプロジェクトのソースディレクトリ(app/src/)のコードからモジュールの画面を呼び出してみたいと思います。(蛇足的かもしれませんが) 今はモジュールってなに?状態なので普通にコード書けるよ(aarに変換できる場所ってだけだよ)ってことを確かめてみたかったので。
「mylibrary/java/com/example/mylibrary/」以下にModule用のActivityを作成します。
package com.example.mylibrary; import android.os.Bundle; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class ModuleActivity extends AppCompatActivity { private TextView textView; private boolean buttonTap = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
なんの変哲もないActivityです。
AndroidManifest.xmlとresフォルダも作ります。
AndroidManifest.xmlは以下。resは割愛
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" tools:targetApi="31"> <activity android:name="com.example.mylibrary.ModuleActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
次に、いったんModuleから離れてプロジェクトのディレクトリ(「/app/java/com/example/aarlibrary」)にmylibrary以下のコードを呼び出すActivityを書きます。
package com.example.aarlibrary; 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; private boolean buttonTap = false; @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.mylibrary.ModuleActivity"); startActivity(intent); } }
layoutフォルダもちろっといじりましたが、appディレクトリの画面からモジュールの画面を呼ぶことができました。
ここから遷移元の画面をReact Nativeにしていきます。 後、遷移先の画面はまだaar化していませんが、これからaar化していきます。
Ⅱ. AndroidモジュールをAAR化
build.gradleの設定変更 ビルド時にAPKではなくAARファイルで出力するために、app/build.gradleに以下の設定をします。
applicationIdを削除する
- pluginsブロックで宣言されているidを'com.android.application'から'com.android.library'にする
plugins { id 'com.android.library' // after // id 'com.android.application' //before } android { ... defaultConfig { // applicationを削除 ... } }
- ビルドする プロジェクトのルートディレクトリで以下を実行
./gradlew clean build
ビルドに成功すれば、mylibrary/build/outputs/aar以下にaarファイルが出力されていると思います。
Androidからaarを呼ぶ 今までは同一プロジェクトのネイティブコードから画面を呼んでいましたが、ここからはaarを呼びます。 といってもやることはたったの2つです。
aarを配置
- gradleに依存関係を追記 -(後は呼び出すだけ)
まず、aarを呼びたいプロジェクトのapp/libs配下にaarファイルをコピーします。 もしこの記事と同じ手順でやっている方がいたら、プロジェクトは新しく作っても、先のプロジェクトからモジュール部分を削除して使ってもよいです。
次にapp/gradleに以下を記述します。
implementation files('libs/mylibrary-release.aar')
最後に、画面を呼び出すActivityで呼び出すクラス名を変更します。 クラス名はAARのパッケージ名に合わせます。
- MainActivity.java
intent.setClassName(getApplicationContext(), "com.example.mylibrary.ModuleActivity");
これで、aarファイルから画面を呼び出すことができました。
Ⅲ. React NativeからAARを呼び出す。
React NativeからAARを呼び出すと書きましたがブリッジの仕組みを使えば
①React NativeからAndroidのネイティブコードを呼び出して
②AndroidからAARを呼び出す
に言い換えられます。
②はもうクリアしています。
また、①は以下の記事で説明させていただいているのでご参照ください。
teisyoku-tabetai.hatenablog.com
なのであとはやるだけになります。
- aarをReact Nativeプロジェクトに配置後、Gradleの依存関係を追加 aarをandroid/app/libs以下に格納。 app/build.gradleに以下を追加
implementation files('libs/mylibrary-release.aar')
package com.integratedandroidscreen; import java.util.HashMap; import java.util.Map; import android.content.Intent; import androidx.annotation.NonNull; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.Arguments; import com.facebook.react.modules.core.DeviceEventManagerModule; public class AndroidScreenModule extends ReactContextBaseJavaModule { private static final String REACT_CLASS = "AndroidScreen"; private final ReactApplicationContext reactApplicationContext; public AndroidScreenModule(ReactApplicationContext reactApplicationContext) { super(reactApplicationContext); this.reactApplicationContext = reactApplicationContext; } @Override public String getName() { return "AndroidScreen"; } @ReactMethod public void moveAndroidScreen() throws InterruptedException { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName(reactApplicationContext, "com.example.mylibrary.ModuleActivity"); reactApplicationContext.startActivity(intent); } }
- AnndroidScreenPackage
package com.integratedandroidscreen; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; public class AndroidScreenPackage implements ReactPackage { @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new AndroidScreenModule(reactContext)); return modules; } }
- MainApplication
@Override protected List<ReactPackage> getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); packages.add(new AndroidScreenPackage()); // ★ここだけ追加 return packages; }
実行
npx react-native run-android
するといくつかのエラーが派生。
- エラー①
* What went wrong: Execution failed for task ':app:processDebugMainManifest'. > Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:12:7-34 is also present at [mylibrary-release.aar] AndroidManifest.xml:9:9-35 value=(true). Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:7:5-12:19 to override.
aarのAndroidManifest.xmlとプロジェクトのものが競合してるらしく、applicationに以下を追加で解決。
<application ... tools:replace="android:allowBackup" ... >
- エラー②
uses-sdk:minSdkVersion 21 cannot be smaller than version 24 declared in library [mylibrary-releas.aar]
Sdkのバージョンがaarでは最低24なのにReact Nativeの方で21になっているというエラーのため、build.gradleのbuildscript部分を下記のように変更
buildscript { ext { ... minSdkVersion = 24 // 修正後 ... } }
- エラー③
* What went wrong: Execution failed for task ':app:processDebugResources'. > A failure occurred while executing com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$TaskAction > Android resource linking failed ERROR:C:\xxx\integratedAndroidScreen\android\app\build\intermediates\incremental\debug\mergeDebugResources\merged.dir\values\values.xml:2170: AAPT: error: resource attr/colorPrimaryVariant (aka com.integratedandroidscreen:attr/colorPrimaryVariant) not found.
androidアプリの画面作成に必要なマテリアルが見つからないというエラーのため、以下の依存関係(aar化したgradleには存在していた)を追加で解決。
implementation 'com.google.android.material:material:1.8.0'
以上までで、冒頭に示したようにReact Nativeからaar形式のアプリ呼び出しを実現できることができました。 React Nativeへのaar組み込みはできるにはできますが、バージョンの互換性だったりbuild.gradleの依存関係だったりには少し注意を払う必要がありそうです。