Android C++開発 [1] : CMakeプロジェクトの作成
お久しぶりです!
この夏はものすごく忙しくてブログを書く時間がとれませんでした。
何をやっていたかというとAndroidで動作するライブラリをC++で作り、Unityラッパーも作成してUnity上でも利用できるようにする、という事をしていました。
C++でのAndroid開発はかなりレアな体験で、色々と学べたのでシェアします。
レアすぎて需要が無さそうだなあ(笑)
プロジェクトの作成
Android StudioでC++を扱うプロジェクトを作成するにはプロジェクト作成のウィザードの中で "Include C++ support" のチェックを付けます。
生成されるプロジェクトは下記のものを含みます。
名前 | 説明 |
---|---|
java | 画面のActivity等のJavaソースコード類 |
cpp | C++ソースコード |
build.gradle(Module: app) | ビルド設定 |
build.gradle(Module: app)について
Android のビルドシステムにはGradleが使われます。
C++のビルドで使われるCMakeとの関連付けはこのファイルの中で行われます。
"externalNativeBuild"の設定でC++ビルドのオプションとしてC++11を利用すること、CMakeのビルド設定が書かれているCMakeLists.txtへのパスが指定されています。
独自のカスタマイズを行う場合はこれらを編集します。
とりあえず実行
このまま特に変更をせずにアプリを起動するとC++側から"Hello from C++"という文字列を取得して画面に表示するものが起動します。
コードで何が行われているかを解説します。
native-lib.cpp
extern "C" JNIEXPORT jstring JNICALL Java_com_example_satoshi_1maemoto_myapplication_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
C++側ではマネージドのJavaのVM環境と連携をとるためJNIの規約に沿った名前のメソッドが用意されています。
規約としてはJavaのパッケージ名の "." を "_" に置き換え、引数でJavaVMへのポインタを受け取るというのがシンプルな説明です。
実際には様々なかたちのデータを引数でやり取りするため様々なテクニックがあります。
例えば配列の配列を受け取るとか、Java側のコールバックメソッドのアドレスを受け取りC++からJavaをコールバックするなど、、この辺りは次回以降扱いたいと思います。
JNIの規約で作ったC++関数は下記のようにJavaから呼び出すことができます。
MainActivity.java
public class MainActivity extends AppCompatActivity { // C++ライブラリ(native-lib.so)をロード static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // C++側の stringFromJNI()関数を呼び出し文字列を取得 TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } // C++関数は native 指定で宣言が必要 public native String stringFromJNI(); }
実機でのUnitTest
プロジェクトの何も手を加えない状態でUnitTestのモックも作成されています。
UnitTestには2種類あり、開発環境内で動作させる通常のUnitTestと、デバイス内で動作をさせるInstrumentedTestがあります。
やっぱり事件は現場で起こるのでできるだけ後者を行いたいものです。ビルド時間はかかってしまうのですが。
あらかじめ用意されているモックは特にC++側との連携はされていないものですが、もちろんC++関数のテストも可能です。 次回以降このInstrumentedTest内でUIを表示するにはどうすれば良いかなども扱いたいです。
ExampleInstrumentedTest.java
@RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.example.satoshi_maemoto.myapplication", appContext.getPackageName()); } }
InstrumentedTestの実行は、クラスを右クリックしてRunまたはDebugを選択します。