![]() |
この本を読み終わり、何かやってみたいな、ということでAndroidで動かしてみることにしました。
iOSは結構やっている人がいるようだったし、僕自身iOSにはそこそこ慣れてることもあり学びが少なそうだったので、 あえてAndroidで挑戦です。
プロジェクト作成
Phone and Tablet
のNative C++
を選択Language
はKotlin
Minumu SDK
はAPI 29
C++ Standard
はToolchain Default
後は適当。
プロジェクト準備ができるまでしばらく待ってから実行すると、Hello from C++
と表示される。
この文字列はnative-lib.cpp
というファイルで実装されたプログラムから返された文字列をkotlinで表示している。
このnative-lib.cpp
でmrubyを実行して結果を返してあげれば目標は達成。
mrubyのbuild
native-lib.cppからmrubyを呼び出すため、Androidエミュレーターで動くlibmruby.a
を作ります。
$ cd ~/Documents/AndroidStudio/Hellomruby $ git clone --depth 1 https://github.com/mruby/mruby.git -b 2.1.2 mruby-2.1.2
https://github.com/mruby/mruby/issues/4563#issuecomment-510281362 を参考にcustom_build_config.rb
を用意。
MRuby::Build.new do |conf| conf.toolchain :clang conf.gem core: 'mruby-bin-mrbc' end MRuby::CrossBuild.new('android_x86') do |conf| api = 29 conf.toolchain :android, arch: 'x86', platform: "android-#{api}" Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| g = File.basename File.dirname x conf.gem :core => g unless g =~ /^mruby-(test|bin-.*)$/ end conf.cc do |cc| cc.defines << "__ANDROID_API__=#{api}" cc.flags << "--sysroot=#{ENV['ANDROID_NDK_HOME']}/sysroot" end end
custom_build_config.rb
を使ってビルドします。
$ cp -r ~/Library/Android/sdk/ndk/21.1.6352462/sysroot/usr/include/android ~/Library/Android/sdk/ndk/21.1.6352462/platforms/android-29/arch-x86/usr/include $ cd mruby-2.1.2 $ rake MRUBY_CONFIG=../custom_build_config.rb ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/21.1.6352462/
ビルドしたファイルを含め、mruby関連のファイルをAndroidのProjectにコピー。
$ cd .. $ mkdir -p app/src/main/cpp/imported-lib/x86/ $ cp mruby-2.1.2/build/android_x86/lib/libmruby.a app/src/main/cpp/imported-lib/x86/ $ cp -r mruby-2.1.2/include/mruby app/src/main/cpp/mruby $ cp mruby-2.1.2/include/mruby.h mruby-2.1.2/include/mrbconf.h app/src/main/cpp/
https://github.com/PierceLBrooks/MobiBot-Android/blob/50cbcdba1380f460418215427d0ceba7fd1f72d0/app/CMakeLists.txt を参考にCMakeLists.txt
に追記します。
add_library( mruby STATIC IMPORTED ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set_target_properties( mruby PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/imported-lib/${ANDROID_ABI}/libmruby.a ) target_link_libraries( native-lib mruby)
以下を参考に native-lib.cpp
で、引数を受け取って、それをmrubyで実行した結果を返すように変更します。
- https://sear-azazel.github.io/2020/02/14/learning-14-01/
- https://github.com/udzura/mruby-syspro-book-src/blob/9a1280a935f62429429260f1d55f21965fc07ae2/projects/mod_minimruby/ver3/mod_minimruby.c
#include <jni.h> #include <string> #include <mruby.h> #include <mruby/compile.h> #include <mruby/string.h> extern "C" JNIEXPORT jstring JNICALL Java_com_tnantoka_hellomruby_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */, jstring arg) { const char *source = env->GetStringUTFChars(arg, 0); mrb_state *mrb = mrb_open(); mrb_value v; v = mrb_load_string(mrb, source); env->ReleaseStringUTFChars(arg, source); if (mrb->exc) { std::string error = std::string(mrb_string_cstr(mrb, mrb_inspect(mrb, mrb_obj_value(mrb->exc)))); return env->NewStringUTF(std::string("error: " + error).c_str()); } const char *ret; if (mrb_string_p(v)) { ret = mrb_string_cstr(mrb, v); } else { ret = mrb_string_cstr(mrb, mrb_inspect(mrb, v)); } mrb_close(mrb); return env->NewStringUTF(std::string(ret).c_str()); }
最後にMainActivity.kt
からRubyっぽいコードを渡して結果を受け取ります。
- findViewById<TextView>(R.id.sample_text).text = stringFromJNI() + findViewById<TextView>(R.id.sample_text).text = stringFromJNI("'hello! ' * 3") - external fun stringFromJNI(): String + external fun stringFromJNI(arg: String): String
実行すると…
動きました!
次は実機で動かしたい。