この本を読み終わり、何かやってみたいな、ということで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
を作ります。
1
2
$ 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
を用意。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
を使ってビルドします。
1
2
3
4
$ 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にコピー。
1
2
3
4
5
6
7
$ 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
に追記します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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っぽいコードを渡して結果を受け取ります。
1
2
3
4
5
- 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
実行すると…
動きました!
次は実機で動かしたい。