non vorrei lavorare

ブログ名の通りです。javascript three.js mruby rust OCaml golang julialang blender

cordovaのプラグインを作ってkindle fireでmrubyを動かしてみた

おはようございます。次男のトイレトレーニングが順調に進んで、保育園でもパンツで過ごしています。kjunichiです。

4千円しないAndroidタブレットを活用しないのはもったいない

Amazonプライムデーで4千円ださずに購入したkindle file。長男が操作をいつのまにか覚え、車のゲームを あれこれDLして遊んでるところを奥さんに見つかり、大目玉を喰らってます。

今回はそんなkindleにmrubyを動かそうとした記録です。

準備編

Androidの開発はプライベートでは既に奥さんの仕事向けにWebViewをつかったやつを大昔のEclipseで 作成したことがあるくらいなので、ぼんやりとは把握してるつもり。そして、イマドキはEclipseではなく、 Android Studioで行うのが一般的らしいというのは予習済み。

Android Studioの環境作成

OSXで主に今回の作業を進めた。Windows 10でも一部やったが、OracleJDKを入れて、 Android Studioをインストールすれば、すぐに動く感じ。 (Android SDK、NDKのDLや仮想マシンのDLでかなり時間は掛かけど。)

SDKやNDKもAndroid Studioからインストール出来、これを使って作業をしている。

Android Studioを離れての作業でも、これらのパスを押さえておけば、大丈夫そうだった。

mrubyの組み込み

素のmrubyをAndroid向けにビルドするには簡単。

mrubyをAndroid Studioのエミュレーターで動かすためのlibmruby.aを作る - Qiita」に書いたようにlibmruby.aを作って、 これをJNIな.soファイルにリンクして、呼び出せばOK。

挫折編

素のmrubyが簡単に組み込めたので、mruby-cliでのクロスコンパイルに対応してあるmruby-webcamを 組み込めば、

cam = Webcam.new
cam.capture {|img|
  # img : JPEG format
  puts img.length
}
cam.snap

と言った具合に簡単にkindle fireのカメラにアクセス出来ると思い、作業を始めた。

  • NDKって出来ること思ったより少なかった
  • mruby-webcamAndroid向けにビルド可能にしたが、kindleのカメラ使えない問題

現行のFireOSですら、カメラのアクセスはJavaを経由しないと難しい模様。 Nでは許可を得るのにJava必須な模様なので、FireOSがアップデートされたら、こちらもJavaが必須となりそう。

Cordova(Android)編

当初はNDKを使えば、C言語で頑張れば、色々出来るから、それらを上手いこと mrbgemにすれば楽しそう!と考えていましたが、基本Javaを介さないとAndoridのサービス?等を 利用することができなそうなことが分かってきたので、方針転換

  • C->Javaしなきゃ行けないなら、Java->JavaScriptも使ってしまえ! -> Cordovaがあるのか!

putsとgetsの実装

RubyPicoが目標だが

puts,getsが実装できれば、最低限mrubyを動かして、プログラムの実験が出来るのではと思い始めた 。すでにこの段階で、mrubyには2.7系ではあるが、opencvがリンクしてあり、mruby-webcamを 少し改造すれば、カメラで撮影した画像を顔検出位まではmrubyで出来そうな見込みであり、 早々にputs,getsを実装したいことろだった。

先人であるRubyPicoのコードが大いに参考になった。 Rubyはクラスの書換えも自由自在なので、puts、getsを書き換えることが簡単にでき、

  • putsではDOMにdivタグで囲まれたテキストを追加する実装に変更
  • getsではalertをwindow.promptで入力を取得する実装に変更

といった実装で進めた。

躓きポイント(puts実装時)

JavaからJavaScriptを呼ぶには

事前にAndroidでは昔からWebView#loadUrlはあまり望ましくないとの情報を 得ており、WebView#evaluateJavascriptがKitKat以降は使えるとの事だったが、

CordovaのAndroidでは簡単には使えない模様。loadUrlは使えたので、こちらでとりあえず実装を進めた。

といった具合にputsですら苦戦して、getsなどは、スレッド絡んでホントに大変だった。

躓きポイント(gets実装時)

WebView#evaluateJavascriptが使えれば、window.promptへの入力後に呼んでもらうコールバックを設定できて 苦労がなかったはずだが、それが使えなかったので、プラグインのコードでwindow.promptの入力を待つのに苦戦

CordovaのプラグインはUIスレッドで動いているのを理解しておらず、 Thread.sleepして固まった。 これに対応する為に、別スレッドで動かし、window.prompt入力後に Js側のコールバックを実行するように非同期処理にして、getsが出来た。

final CallbackContext cb = callbackContext;
        if (action.equals("mrbLoadString")) {
          final String script = args.getString(0);
            Thread th = new Thread(new Runnable(){
              @Override
              public void run() {
                String jniString = MrubyJni.mrbLoadString(script);
                Mruby.setResult(jniString);
                String message = ". JNI says: " + _jniString;
                Mruby.mrbLoadString(message, cb);
              }
            });
            th.start();

            return true;
}

public static String gets() {
      Log.d("Mruby", "gets");
      waitInput = true;
      inputString = "dummy";
      final String js = "console.log(mruby.__jsGets())";
      stdout.getView().post(new Runnable() {
        public void run(){
          //stdout.sendJavascript(js);
          Log.d("Mruby", "gets/exec js = " + js);
          stdout.loadUrl("javascript:" + js);
          Log.d("Mruby", "gets/exec js done");
        }
      });
      Log.d("Mruby", "gets/wait");
      while (waitInput) {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      }
      Log.d("Mruby", "gets/rtn = "+inputString);
      return inputString;
}

成果物

役に立ったこと

デバッグ文代わりのLogとCのマクロが役に立った。 コマンドラインでcordova runコマンドで動かして、AndroidStudioで適当なプロジェクト開いておくと、 出力したログが勝手に表示されて便利だった。

import android.util.Log;

Log.d("Mruby", "gets/wait");
__android_log_print(ANDROID_LOG_DEBUG,"Init", "Java_com_hatenablog_abrakatabura_mruby_MrubyJni_initialize");

IDE環境でのCordovaのプラグイン作成方法が不明なので、improt文含めコピペ。

まとめ

去年書いた記事の「外部のライブラリに依存するmrbgemを使ってもmruby-cliでワンバイナリを作成できるようにした」の作業を通して、クロスビルドの経験が出来ているので、あまり臆せずNDK絡んでの開発が行えた気がする。経験大事。

また、HT-03Aの頃と違い、NDK関連も検索すれば、やりたいことに近い事例や、エラーメッセージへの 対応等サクサク出てて来る。ただ、kindle fireだとそもそもAndroidのバージョンいくつと同等なのか とか、それなりに取り組まないと把握が難しいと感じた。やり出せば、楽。ちなみに現時点では5.1のlollipop相当らしい。

ndk-buildコマンド or Android Studioプラグイン

コマンドラインで生活してる場合、ndk-buildコマンドが楽な知見。

プラグインの設定が面倒。過去になんどか書き方が変更になっているので、検索に注意が必要。

Cordovaの感想

Cordova使ってみて、さくっと子供たちのゲーム教育用アプリを作ってkindleAndroid端末で動かせるので、 お父さんはIT系の仕事やってる感を前面に押し出せそうで、心強いw。

ドキュメントがググると日本語で見つかるが、ザ機械翻訳なので、Androidが人造人間と訳されているなど、 読みづらい。とはいえ、無いよりずっと良い。

また、頻繁にAPIも変更なっているようで、既に出回っているプラグインのソースを呼んで 使い方を理解する等が出来ないと辛いが、それが出来れば、良い感じで楽できそうだった。

今後の課題

現状ではmrubyをEmscriptenでJsに変換して動かすWebrubyと比べて、なんらメリットがなさそうなので、 当初のカメラを使って簡単な画像処理あたりまでの実装。

参考資料

関連記事


あと、CordovaでiOSも試した。去年から無料で実機にデプロイ出来るようになったので、 実機で動かせた。iOS10も出たばかりで、こちらの新しい気のも試したい感はある。