久々のAndroid開発(いきなりndkでOpenGL)序章
私には出来ませんって 断り続けたものの お客さんがかんたんに許してくれるわけでもなく
3回ほど断ったけど 結局 やってやんよ! というしか無かった Android開発
もちろん NDAの範囲で〜 の話だけど
具体的なことはかけないのだが NDKでAndroidの開発である
過去の栄光
こんなもの捨ててしまえばいいのだ
ただ、知識は捨てる必要はない。先人の7年前の私の話をしよう
7〜8年前に、わたしは某社の社長から特命を受けた
AndroidとiOSに両対応したゲームエンジンを作れと
当時はAndroidは2.xで iPhone3 とかの時代だった
OpenGLESは1であったし、なんといっても私は AndroidもiPhoneも持ってない
当然 スマホの開発はしたことないし、OpenGLも触ったことがなかった
そんな状況ではあるが、社長はR&Dとして期日も設けず、好きに開発してくれと
結局それから毎日 鬼のように働き3週間でプロトタイプを作成した
その時にとった戦略は、メインライブラリをC++で作り
AndroidにはJNIで、Objective-CにはそのままC++で 呼び出す事だった
最初に面倒なAndroid対応を行った
JNIも初めてで、意外とJavaとの呼び出しに罠が多く苦労した(String関連のメモリの所有者、GC、リフレクションなど)
OpenGLは初めてだけど すぐに理解できた
今の私だと、あの速度で理解出来たのか不明 いや、きっとやればできるはずだ・・
Objective-Cは C++がそのまま動くので何も問題なくおわった
開発中にGLES2.0が出たので、両対応したり
今考えても3週間で誰の助けもなくよく終わったと思う
その後、Javaの側をつくったり、GLES2.0のレジスタにおさまらないスキンメッシュをCPU計算して
SIMDのNEONを使ったり
色んな部分をマルチスレッド化したり
Javaへ例外をやり取りしたり、RTTIに対応したり、本来非対応なものを、ライブラリ無理やり入れて動かした
また、NativeActivityが出たので対応したり
今回の話
あれから7、8年たち、色々と状況変わっているだろうが、結局いつもと同じく やるしかない
自由を手に入れるには 案件を片付けるしかない
まずは環境構築だ
環境
以前はWindowsで vs-androidを使った
これはVisualStudioにプラグインとしてのせて、VisualStudioにツールチェーンを構築してAPK作って転送する
とっても素晴らしいものであった
何が素晴らしいかって、clangでビルド出来て、C++11が使え(本来使えないが無理やり使えるようにツールチェーン作った)
Boostもちゃんと無理やり入れた
そして C++のビルドが早く、Javaを変更しなければそちらのビルドが走らず高速で実行できる
また、C++部分にブレークポイントうってデバッグもでき、控えめに言って最高であった
それに加え、コマンドラインでのビルド(リリース、自動ビルド用)と
EclipseでのNDKビルド環境を作った
Eclipseビルドは C++部分の自動ビルドが非常に遅く、バックグラウンドでCMakeでビルドするほうが早く
そのようなシステムを作ったが、vs-androidよりビルドは遅いし、そのごJavaのコンパイルも入り
C++部分のデバッグもVisualStudio様に比べて弱く 非常に使いにくかった
もちろん Java部分の開発にはとてもパワフルだった
ので、C++部分の開発(全体の98%)は vs-androidにて
Java部分(2%)は Eclipseで開発
というスタイルであった
今回は、WindowsPCもMacPCも持っているものの、できればMacにすべて開発移したかったけど
vs-androidの便利さ考えると 悩むところ
ってことで、とりあえず AndroidStudioでのNDK開発を検証してみる
AndroidStudio、NDKなど環境を整え 新しいプロジェクトの作成
すばらしい! 最初から C++14まで選べて、Exception、RTTIも標準でできる
これだけでテンションあがる
生成コード
まず、ビルドがGradleになっている。使ったことないけど 評判は良い
前つかってたときは Antで、Mavienがいいよって中国の技術者に教わって Maven対応したんだけど
とりあえずGradleの沼に入るのは今ではない
C++側のビルドはCMakeだ。何も文句ない
Java側のソース
package com.murasame_lab.cstudio1; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); }
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_murasame_1lab_cstudio1_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
最初らしく、C++コードから "Hello from C++" の文字を返し
それをJavaで画面に表示している
NewStringUTF() は、メモリJavaが破棄してくれるんだっけ?
そのあたりは 8年前に一通り調べたけど
正直記憶がない。3週間で一夜漬けしたので かんたんに思い出すことは難しいだろう
もう一度JNI周りは調べ直すしかない
記憶はないが 私にはコードという記録がある
いつか思い出すはずだ
ちなみに Static関数でライブラリのLoadをするのは、一つのお作法のようだ
8年前に色々な方法でベストを探したけど、最もかんたんにかけて問題がないのがこの方法であった
とりあえず小さなコードにおいては、AndroidStudioでブレークポイントもかかったし、高速にビルド出来た
そのため、今回はAndroidStudioでもいけるんじゃないかと 期待している