2014年5月21日水曜日

iアプリをAndroidで動かしたい

追記:iアプリを動かすAndroidアプリ、公開しました。
http://android.develga.com/2014/07/androidi.html


iアプリは米国Sun Microsystems社(現在は米国Oracle社)により開発された家電製品・組み込みデバイス向けJavaプラットフォームであるJ2ME CLDCで規定されているAPIと、ユーザインターフェースやHTTP通信などiモード対応機種用に作成されたiアプリAPIにより構成されています。
https://www.nttdocomo.co.jp/service/developer/make/content/iappli/architecture/index.html

iアプリ

iAppli is Dead (Seriously)

iアプリを知らない奴がいるのか。

iアプリはiPhoneで動くアプリのことではない。

iアプリは docomoのガラケーで動くアプリ のこと。
iOSアプリとかAndroidアプリとかのガラケー版な。
(そういう言い方は気に食わないがな)

私が今回の記事で扱うのはゲームのみな。

iアプリをAndroidでやるか

記事タイトルをみて、

せっかくスマホにしたっていうのに、Androidでiアプリやらないよ。
いまは、パズドラ、モンストだろ。

と思ったかもしれない。
Androidでiアプリはやらない理由として以下を挙げておく。

  • iアプリやるより、スマホのゲームやるよね
  • UIが変わっちゃうよね(スマホにキーパッドはない)

iアプリはしょぼい。それほんと?

iアプリしょぼいとかいうキミの記憶は 間違っている
確かにファミコン以上、スーファミよりしょぼいクオリティのゲームが多かった。

しかし、短絡的に判断してはならない。

なぜなら、iアプリの全盛期には、スーファミも
いや!プレステも、
いやや!Wiiも
発売された世界で作られたのである。
それゆえアイディアとして尖ったもの、洗練されたものが(なかには)あった。

リッチなゲームが溢れる世界で膨らんだそういったアイディアは、
非常に(いやまじで非常に)限られたリソースをいかに効率的に使うか、
という技術的な問題を乗り越えて作成された。

iアプリは超面白い。それほんと?

一方でiアプリ超おもしろいというキミの記憶も 間違っている
iアプリはあくまでレトロゲームであり、
個人小規模ゲームが台頭した時代の歴史的資料に過ぎない。
思い出補正というやつだろう。

Androidで動かすに足るか。

iアプリは歴史的な資料。アーカイブだ。
遊ぶのではなく、歴史を閲覧するのだ。

ファミコンや初代プレステのゲームは今も社会に残っていると言える。
なぜなら、今も遊べるからだ。

Wiiやプレステで昔の古いゲームが販売され続けている。

カセットをデータ化すれば
ファミコンというハードなしで、
パソコンやスマホ上で動かすことができる。

ゲームは遊べないと社会から消えるのだ。

だから

  • iアプリやるより、スマホのゲームやるよね
  • UIが変わっちゃうよね(スマホにキーパッドはない)

とかは、まー、その、どうでもいいんだ!!!!!

Androidで動かす

というわけでAndroidでiアプリを動かすことを考える。

iアプリは何語か。

幸運なことにJavaだ。
しかももっと幸運なことにこのブログが対象としているAndroidアプリもJavaだ。
しかしiアプリはDocomo専用のAPIを含んだDoja(ドコモジャバ)だ。
(Dojaをなんて読むのかは知らない。今となっては知る由もない。)

技術的な話。

やることは以下だ。

  • Docomo専用のAPIをAndroidで動くように実装する
  • ガラケーの物理キーを画面に表示して押せるようにする
  • iアプリをAndroid(DalvikVM)上で動作可能な形に変換する

できそうだ。

先行した、関連したもの

iアプリ変換機。

iアプリをAndroidアプリに変換しようという人はすでにいた。

iアプリをスマホ向けに変換する「iappli Publisher」、アプリックスのJBlendで実現

iアプリのAndroid移植サービス renacentia

iアプリ、S!アプリをAndroidアプリに自動変換――NSW、無料の試用サイトを公開

とか。

どれもアーカイブ目的じゃない。ガチで売る気だ。
なにより私が気になるのは「ソースコード」が必要ってところ。
それじゃあ製作者しかAndroidアプリに変換できない。

開発用エミュレータ。

iアプリの開発環境を入れたWindowsでは、iアプリを動かすことができる。
(どうやってアプリを入手するかは別の話)

最後に

つくります。
音と3Dは諦めそう。。。

Javaでメモリリークする件

Java プログラムでメモリー・リークが発生するのでしょうか?
その通りです。
http://www.ibm.com/developerworks/jp/java/library/j-leaks/

メモリはリークするか

まとめ

相互参照は問題ない。
問題なのはライフサイクルの不一致。

Javaは「メモリリーク」しない

Javaはすごい。

ガーベッジ・コレクション(GC)という仕組みによって、
参照できなくなったメモリ領域を自動的に開放してくれる。
だからC/C++のようなメモリリークは起こらない。

Javaがメモリリークしない理由

なにを解放するか。

参照できなくなったメモリ領域とは、どのようなものだろう。
Javaではうまいことなっているのだが、まずはすごーく素朴なものを考えてみよう。

たとえば、そのオブジェクトの参照されている数を記録しておき、0になったら破棄するというのはどうだろう。これをリファレスカウンタ方式という。

http://ja.wikipedia.org/wiki/参照カウント

すごーく素朴な方式で軽量ではあるが、これには 相互参照 の問題がある。
理解しやすくするため、
3つのオブジェクト「キミ」と「ゴリラ」と「バナナ」を考えよう。

解放が上手くいく場合

キミはメインクラスなので被参照数が1ある。(キミの被参照数=1)
キミはゴリラを見ている。(ゴリラの被参照数=1)
ゴリラはバナナを見ている。(バナナの被参照数=1)

これですべてのオブジェクトは解放されない。

ここで、ゴリラが要らなくなったとしよう。
キミはゴリラから視線を外す。
すると、誰にも見られていないゴリラが解放される。
続いて、誰にも見られていないバナナが解放される。

ゴリラとバナナは正しく解放できた。

解放が失敗する場合

さて、ここで問題だ。
バナナがゴリラを見ていたら、何が起こるだろう。

キミがゴリラへの視線を外しても、
ゴリラとバナナの被参照数は1となり、解放されないのだ!

もうゴリラはいらないのに、ゴリラは居座り続けるし、
なにより、バナナも残ってしまう。

相互参照とは、バナナとゴリラが残ること、である。

JavaのGC

さて、JavaのGCはいかにして上記の問題を解決するのだろうか。
それはルートオブジェクトの導入である。

http://ja.wikipedia.org/wiki/マーク・アンド・スイープ

まずキミに色を塗る。
そしてキミの見ているもの、つまりゴリラに色を塗る。
そしてゴリラの見ているバナナに色を塗る。

で、適当なタイミングで色が塗られていないものを解放する。

これならバナナがゴリラを見ていても
ゴリラが二度塗りされるだけだ。いわゆる二度塗りゴリラだ。

キミがゴリラから視線を外せば
ゴリラもバナナも色塗りされずに解放される。

純粋なキミは、「え、じゃあみんなこれやればいいじゃん」と思うだろう。
しかし、リファレスカウンタ方式に比べて処理が重いとか
ダメなところもあるので一概には言えない。

じゃあJavaで起こるメモリリークってなに

端的に言えば、ライフサイクルの不一致だ。
メンバー間の音楽性の不一致によって解散するバンドも数多い。

キミは80年生きる。
そのなかでもう使わないのに参照し続けているものがあるのではないか。
たとえば、部屋の隅にある小5のときに書いた文集だ。
当時は確かに使った。
友達のやつは読んだし、自分のも読んだし、あの子のも読んだ。
中学の時に読み返して、なんでこんなこと書いたんだと不思議に思った。
しかし小6ならまだしも、小5である。もう読むことはないだろう。

  • 子どもに見せるため?
  • 親の小5の文集を見たことがあるか?
  • 結婚相手はいるのか?
  • 頭は大丈夫か?

それはいわばゴミなのだ。Garbageなのだ。Collectされるべきなのだ。
しかし、キミから参照されているために
このゴミはお母さんによって片付けられることはない。
(なお、お母さんがマーク・アンド・スイープ方式を採用しているかは議論の余地が残る)

Androidでは

AndroidではAndroidの用意してくれたフレームワークという舞台上で
キミのイカしたActivityやServiceその他を踊らせていくことになる。
このフレームワークの生存期間は(たいてい)キミのアプリが起動している期間と等しい。
やっかいなことにもっと長いものもある。たとえばタイマー処理だ。
たとえばタイマー処理にゴリラを参照させると、そのタイマーが解放されるまでゴリラはメモリ上に残る。

ゴリラだったらいい。

これがもしActivityだとすると、ActivityはViewをたくさん持っているし、画像も持ってるかもしれないしメモリが大きいので問題になる。
あと、タイマーの時間にもよる。タイマーが24時間後だったらやばい。
24時間ゴリラがメモリ上にいるのだ!!!危ない!!!

Androidでは何がどれくらいの長さ保持されているかが不明瞭なため、
こういったメモリリークが起こりやすい。

ライフサイクルを一致させろ。

Activityで使うもの(ゴリラ)はActivityに持たせる。
そうすればActivityが解放されるタイミングで
それ(ゴリラ)も解放される。

ライフサイクル、つまり

いつ作られて、いつ要らなくなるか

という世界で閉じた作りになっていれば、メモリリークは怖くないのだ。

付録

面接で質問された人もいるという
http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java