Androidで、JavaのActivityからc++のNativeActivityを呼び出す

Androidでは、c++のみで書かれるNativeActivityがあります。
github.com
これは通常のActivityと同じように、別のActivityから遷移させることが可能です。
今回は、この遷移についてやった内容を記事にしました。

サンプルプロジェクト

上のGoogleのサンプルのnative-activityサンプルを利用し、タッチイベントでNativeActivityに遷移を行うサンプルプロジェクトをGitHubに上げました。
github.com
これについて解説を行います。

プロジェクト作成

Include C++ supportにチェックを付けてプロジェクトを開始します。
NDKのインストールなどは、メッセージに従ってやって下さい。

NativeActivityを動かすための準備

今回は、先程貼ったGoogleのサンプルコードを、遷移先で動かそうと思います。
そのための準備を行っていきます。

main.cpp

Include C++ supportで自動生成されるnative-lib.cppは使わないので消します。
次に、プロジェクトのapp/src/main/cppフォルダに、サンプルプロジェクトのandroid-ndk/native-activity/app/src/main/cpp/main.cppを置きます。

CMakeLists.txtの編集

app/CMakeLists.txtの内容を、以下で置き換えます。

cmake_minimum_required(VERSION 3.4.1)

# build native_app_glue as a static lib
set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}")
add_library(native_app_glue STATIC
    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)

# now build app's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Werror")

# Export ANativeActivity_onCreate(),
# Refer to: https://github.com/android-ndk/ndk/issues/381.
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")

add_library( native-activity SHARED src/main/cpp/main.cpp )

target_include_directories(native-activity PRIVATE ${ANDROID_NDK}/sources/android/native_app_glue)

find_library( log-lib log )

target_link_libraries( native-activity
                       android
                       native_app_glue
                       EGL
                       GLESv1_CM
                       ${log-lib} )

大概がサンプルからのコピペで、ソースのパス位しか変えていません。
注意点としては、ビルド後のコードはnative-activityとなっている点でしょうか。
後述するAndroidManifest.xmlと、MainActivityでのloadLibraryでは、この名前を指定します。

build.gradleの編集

app/build.gradleのexternNativeBuild内、cmakeの項目に、abiFiltersとargumentsの追記を行い、以下のようにします。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongrong.changetonativeactivitysample"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
                arguments '-DANDROID_PLATFORM=android-9',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
            }
        }
    }
︙

自動生成されるcmakeの項目にはcmakeのフラグについて記述(c++11を指定した場合、c++11に関する記述)がありますが、これは先程CMakeLists.txt内に記載を行ったので、消してしまって構いません。

AndroidManifest.xmlの編集

AndroidManifest.xmlの、application部に、activityに関して追記を行い、以下のようにします。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wrongrong.changetonativeactivitysample">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <!-- Our activity is the built-in NativeActivity framework class.
     This will take care of integrating with our NDK code. -->
        <activity android:name="android.app.NativeActivity"
            android:label="@string/app_name"
            android:configChanges="orientation|keyboardHidden">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name"
                android:value="native-activity" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
MainActivity.java

ここまで行った変更に合わせ、MainActivity.javaの内容を変更します。

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-activity");
    }

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

        TextView tv = (TextView) findViewById(R.id.sample_text);
    }

    //タップイベントで、nativeactivityに遷移する
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Intent intent[] = new Intent[1];
        intent[0] = new Intent(MainActivity.this, NativeActivity.class);
        startActivities(intent);

        return true;
    }
}

ここまで紹介した手順によって、NativeActivityに遷移することができます。

アクティビティの遷移は、以下のサイトを参考にさせていただきました。
akira-watson.com

終わりに

今回はNativeActivityへの遷移まで実践しました。
このような場合のActivity間のデータのやり取りはどのようにするのかはまだ検証していないので、検証したらいつか記事にするかもしれません。