簡約!? λカ娘 Rock!の紹介とHaskell製Androidアプリの解説

Posted on December 17, 2013 / Tags: haskell, book, android, ajhc

Table of contents


この記事は Android Advent Calendar 2013 - Qiita [キータ] の12/17(火曜)分じゃなイカ。

だいぶ息切れしてきた関数型プログラミングの本 簡約!? λカ娘 Rock! がコミックマーケット85 3日目 西地区 す-03a で出るでゲソ。みんな買ってほしいでゲソ!

がもくじで、 @master_q は第2章を書いたらしいでゲソ。 この記事では Android NDK に付属しているnative-activityサンプルアプリケーションをHaskell化するでゲソ。 当然native-activityはC言語で書かれているので、いきなり全部をHaskell化できないじゃなイカ。 そこで スナッチ設計 という手法を使って動作可能なまま少しずつHaskellで設計置換していくんでゲソ。

記事が気になったら是非 サンプル版pdf を読んでみてほしいでゲッソ!

Haskellで書いたAndroid NDKアプリってどんな感じ?

Android NDKに対するスナッチ設計の詳細は先の同人誌に書いたので、 この記事ではHaskellで書いたAndroid NDKアプリの中身を見てみようと思うでゲソ。 題材はGoogle Playからダウンロードできる Cube というアプリでゲソ。

このアプリは単なるOpenGL ESのデモアプリで使い方はイカの動画を見ればすぐわかると思うでゲソ。

このCubeアプリのソースコードは https://github.com/ajhc/demo-android-ndk/tree/master/cube にあり、以下のようなディレクトリツリーを持っているでゲソ。

$ pwd
/home/kiwamu/src/demo-android-ndk/cube
$ tree
.
|-- AndroidManifest.xml
|-- Makefile
|-- cube.xcf
|-- hs_src
|   |-- CubeVertices.hs
|   `-- Main.hs
|-- jni
|   |-- Android.mk
|   |-- Application.mk
|   |-- c_extern.h
|   |-- dummy4jhc.c
|   `-- main.c
`-- res
    |-- drawable-hdpi
    |   `-- ic_launcher.png
    |-- drawable-ldpi
    |   `-- ic_launcher.png
    |-- drawable-mdpi
    |   `-- ic_launcher.png
    |-- drawable-xhdpi
    |   `-- ic_launcher.png
    `-- values
        `-- strings.xml

C言語とHaskellの界面の話題や、ビルド手順は先の同人誌が詳しいでゲソ。 この記事では上記ファイルの内、Haskellで書かれているCubeVertices.hsとMain.hsについて解説するでゲソ。

CubeVertices.hsファイルについて

まずこの立方体のデータがどこにあるかでゲソ。 それは簡単でイカのモジュールでゲソ。

https://github.com/ajhc/demo-android-ndk/blob/master/cube/hs_src/CubeVertices.hs

verticesとcolorsという名前のリストが入っているだけでゲソ。 このリストはMain.hsで使用されて、それぞれglVertexPointerとglColorPointerに渡されるだけじゃなイカ。 あとはHaskellじゃなくてOpenGLの知識でゲソ。

glVertexPointerとglColorPointerの呼び出し

そのglVertexPointerとglColorPointerはどこから呼び出されるかというとMain.hsのengineDrawFrame関数でゲソ。

https://github.com/ajhc/demo-android-ndk/blob/master/cube/hs_src/Main.hs#L80

このコード、前半はAndroidEngine型から現在の状態を引き出しているようじゃなイカ。 その後ディスプレイが初期化されていたら“c_gl”ではじまる名前の関数群、 つまりOpenGL ESの関数群を呼び出して画面描画をするでゲソ。 このOpenGL ESの関数群は以下のファイルで定義されているでゲソ。 HaskellからC言語の関数が呼び出せて便利でゲソ!

https://github.com/ajhc/demo-android-ndk/blob/master/lib/android-ndk/AndroidNdk/OpenGLES.hs

AndroidEngine型の状態変更

engineDrawFrame関数の実装を見ていると、AndroidEngine型に変更がないと立方体は微塵とも動かないことがわかるでゲソ。 誰かがAndroidEngine型の状態を変更していないとつじつまが合わないんじゃなイカ? この状態を変更する犯人は二人いるんでゲソ。

まず一人目はeHandleInput関数でゲソ。 この関数はタッチパネルのドラッグ動作を検出して、 AndroidEngine型に格納されている以下4つの状態を変更するでゲソ。

https://github.com/ajhc/demo-android-ndk/blob/master/cube/hs_src/Main.hs#L38

二人目はeHandleCmd関数じゃなイカ。 この関数のパターンマッチは長いでゲソがほぼ 元にしたC言語のnative-activityサンプルコード のままでゲソ。 この関数でAndroidのアクティビティの状態管理をしているでゲソ。

https://github.com/ajhc/demo-android-ndk/blob/master/cube/hs_src/Main.hs#L53

全てをつなげる簡易フレームワーク

engineDrawFrame、eHandleInput、eHandleCmdという3つの関数が出てきたでゲソが、 これらは誰が呼び出すんでゲソ? 呼び出す人が誰もいないなら動作するはずないじゃなイカ。 ここらへんの呼び出しはめんどうなのでフレームワークで包んでみたでゲソ。

https://github.com/ajhc/demo-android-ndk/blob/master/cube/hs_src/Main.hs#L13

このコード少しわかりにくいので図にしてみたでゲソ。 Androidアプリが起動するとまず最初にandroidMain関数が実行されるんでゲソ。 つまりandroidMain関数はこのアプリでのエントリポイントでゲソ。 この関数はC言語から呼び出せるengineHandleInput、engineHandleCmdという関数と共に4つの関数を内包したAndroidNdkActs型をHaskellで作られたAndroidフレームワークの初期化関数androidMainHsに渡すんじゃなイカ。 このフレームワーク側ではAndroid本体からイベントがあると、 いいかんじな処理をした後にAndroidNdkActs型の中の適切な関数を呼び出してアプリを動作させるんでゲソ。 例えば、タッチパネルが操作されてたらイカのような手順で関数が呼び出されることになるでゲソ。

  1. C言語がengineHandleInput関数を呼び出す
  2. engineHandleInput関数がフレームワークのhandleCmdHs関数を呼び出す
  3. いいかんじの処理が走る
  4. フレームワークがAndroidNdkActs型の中からeHandleInput関数を選択して呼び出す
  5. eHandleInput関数がAndroidEngine型の中の状態を書き換える

じゃぁ親玉であるandroidMain関数はどこから呼ばれるかというと、 C言語のエントリポイントが呼び出すんでゲソ。

https://github.com/ajhc/demo-android-ndk/blob/master/cube/jni/main.c#L31

なんかわかったような気になったじゃなイカ!

blog comments powered by Disqus