2014年7月17日木曜日

Androidでiアプリができるようになったよ

iアプリ・アーカイブス

ScreenShot1

ScreenShot2

Download

これを使えばAndroid端末でiアプリを動かすことができるよ。
2Dゲームのみ。音は出ないよ。

諸事情により、このへんのゲームは動くよ。

  • もののふの宇宙
  • 海中ピラミッド
  • 海中ピラミッド2

他もだいたい動くかんじ。
動かないアプリがあったら教えてね。

Download

2014年6月3日火曜日

文字化ける

文字を化けさせる

iアプリをAndrod上で動作させるため、
SJIS(Shift_JS)のテキストファイルを読み込もうとしている。

しかしこれには文字化けの問題がある。

Androidは一旦置いとく。

実験

SJISのテキストファイルをつくる

まず元となるテキストファイルを用意する。
文字コードはutf-8とした。

hoge.txt

アンドロイド☆イズ☆デッド 

このファイルの文字コードをSJISに変更する。

% iconv -f utf-8 -t sjis hoge.txt > hoge.sjis.txt

UTF-8で読み込む

InputStreamReaderに文字コードを指定する。
指定しないとデフォルトの文字コードを使う。
Androidも(キミのmacも)デフォルトはutf-8だ。

FileInputStream is = new FileInputStream("hoge.sjis.txt");
InputStreamReader isr = new InputStreamReader(is/*, "utf-8"*/);
BufferedReader reader = new BufferedReader(isr);
String line = reader.readLine();
System.out.println("line: "+bytesToHex(line.getBytes(/*"utf-8"*/)));

bytesToHex(byte[]) http://stackoverflow.com/a/9855338

実験結果

line: EFBFBD41EFBFBDEFBFBDEFBFBD68EFBFBDEFBFBDEFBFBD43EFBFBD68EFBFBDEFBFBDEFBFBD43EFBFBD59EFBFBDEFBFBDEFBFBD66EFBFBD62EFBFBD68

もちろんこんなString lineをそのままUTF-8文字として出力しても
文字化けして読めないんだけど、
そのバイト列としても、なんだかおおよそ文字とは思えないものがでてくる。
なんだこれは。

もとの文字列のバイト列とくらべてみよう。

% hexdump hoge.sjis.txt

の出力は

0000000 83 41 83 93 83 68 83 8d 83 43 83 68 81 99 83 43
0000010 83 59 81 99 83 66 83 62 83 68 0a               
000001b

である。やっぱぜんぜん違う感じする。

utf-8として読み込んだ文字列のバイト列には
EFBFBDというものが繰り返し出現することがわかる。
これを**に置き換えて整形すると……

0000000 ** 41 ** ** ** 68 ** ** ** 43 ** 68 ** ** ** 43
0000010 ** 59 ** ** ** 66 ** 62 ** 68

似ている。

考察

InputStreamReaderhoge.sjis.txtの内容を読むときに、
utf-8の文字が来ると期待するため、
その範疇にない文字を「表現できない文字」として
EFBFBDに置き換えてしまっている。

JavaのStringになった時点で、元の情報を失う。

結論

InputStreamReaderで文字化けしたStringを、元のbyte列に戻すことはできない。
それゆえ、他の文字コードに変換することもできない。

iアプリとAndroid

iアプリではデフォルトの文字コードがSJISだった。
Androidではutf-8
だから、

new InputStreamReader(is);

とか、

"いやまずiアプリって何".getBytes();

とかやったときに使われる文字コードが
iアプリとAndroidで異なる。

2014年6月2日月曜日

Classファイルの文字コード

30億のデバイスで化けないJava。

Shift_JISとSJISは必ずしも一致しなかった。
文字コードはUTF-8に統一されつつあるが、未だに悩ましい問題の一つだ。

JavaのClassファイルとAndroid

.javaファイルはコンパイルすると.classファイルになる。
AndroidのDalvikVMではこのClassファイルをDxにより、
1つのclasses.dexなるファイルにまとめる。

Classファイルの文字コード

Javaファイルを書くときはその文字コードを気にすることもあるだろう。
(とはいえUTF-8一択だ。「気にする」というのはUTF-8になっているかどうかを気にするのだ)

しかしClassファイルの文字コードを気にしたとこはあるだろうか?

Shift_JISでエンコードされたJavaファイルをClassファイルにコンパイルしたとしよう。
クラスローダーはこのClassの文字コードをどのように判断しているのだろうか。

答えは簡潔である。
Classファイルにおいて文字列の文字コードは統一されているのだ。

UTF-8っぽいやつ

Classファイルのなかで文字列は「UTF-8っぽい」文字コードで表現される。
それゆえ、Javaはコンパイル後に文字化けすることがない。

指定するタイミング

JavaファイルをClassファイルにコンパイルするときに、Javaファイルの文字コードを指定してあげる。
javacのオプションに-encodingがある。

http://www.glamenv-septzen.net/view/86

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

2014年2月10日月曜日

AndroidでOpenCVを使う

「これがあったら便利だよね」ってのを、どっかのおっさんが用意してくれる。
それがOpenCVだ。
http://gori-naru.blogspot.jp/2012/11/opencv.html

OpenCV

画像をいじりたい人が使うライブラリ。
だいたい無料で使い放題。
Androidで使うのはマジカンタンだからあ、やったほうがよくね?笑

AndroidでOpenCVを使う

とりまダウンロード。

http://opencv.org/downloads.html へ赴く。
OpenCVはいろんなOS用で動くのだ!wa-i
もちろんここではAndroid版をダウンロードする。

でいい感じのトコに解凍する。
ダウンロードディレクトリとかじゃないほうがいい。

さっそくインポート。

Eclipse起動してくれ。

OpenCV-2.4.8-android-sdk/sdk/java をインポートする。
これAndroidライブラリプロジェクトだよ。

これでおしまい。

やっぱサンプル動かす。

せっかくなのでサンプルを動かしてうぇーいしてみる。

OpenCV-2.4.8-android-sdk/samples/tutorial-1-camerapreview
をインポートして。起動する。
うぇーい

うぇーいできない??
なんかダイアログでた??
そしたら、ナウい日本人丸出しでYes!Yes!って答えておけばおk。

OpenCVの本体が、全部のOpenCVを使ったアプリに1つずつ入っていたら、
端末の容量すげー食って嫌だよね。
まるで、別荘にもワイフがいるようなものさ。
(OpenCV開発者談)

OpenCVはアプリの中に組み込むこともできるけど、
マネージャアプリを通して端末にインストールされたOpenCVを
利用することもできるんだ。
このどっちの方法をとるかは、アプリをつくる側が決められる。

サンプルアプリはマネージャアプリを使っている。
とはいっても、キミの超イケてるCoolなアプリを起動させるために、
件のダイアログを出して、この超ださいアイコンのアプリを
インストールさせるのは気がひけるよね。

NDKで使う。

たいていはJavaでいじっていればいい。
OpenCVの内部はC/C++で書かれているので十分速いためだ。

しかしNDKから使えないと不便なときもある。
たとえば、JNIとJavaの橋渡しは少し時間がかかる。
非常に頻繁に(それがどれくらいかはキミの想像に任せる)
JavaからOpenCVのメソッドを呼び出していると、
やがてその少しの時間がうざったくなる時が来るのだ。

C/C++でOpenCVをいじったことのある人は、
画像配列をJNIでやりとりして、
実装をC++で書いてしまったほうがラクだ。

NDKを使うサンプルな。

jniディレクトリがあるものが、NDKを使うサンプルだ。

OpenCV-2.4.8-android-sdk/samples/tutorial-2-mixedprocessing
が該当する。

このプロジェクトをインポートすると
以下の様なエラーがコンソールに出力されちゃったりしちゃうのだ。

error: Program “/ndk-build.cmd” is not found in PATH

まず、NDKを使えるようにしよう。
NDKをダウンロードとか、
Eclipseの設定からAndroidの項目を選び、NDKを選択して、
NDKのパスを設定するとか、
NDKのディレクトリにパスを通すとかはやってある?

それだったら、このサンプルプロジェクトの設定を開き、

C/C++ Build > Tool Chain Editor > Current Builder

の項目をAndroid Builderにする。
そうしたらビルドできて、動くよ。
うぇーい

2014年2月8日土曜日

Broadcast

概要

ブロードキャストとは不特定多数に同じ情報を同時に送ること。
ここでいう「情報」とはなにか?AndroidではIntentを用いる。
IntentをAndroid端末内に投げるイメージでよい。
Androidのプロセス間通信に用いることができる。

シャットダウンするよ〜とか
アプリがインストールされたよ〜とか
そういう系のactionをもつIntent
Androidがブロードキャストしている。

そういうのをBroadcastReceiverで拾うことができる。

いや、拾うだけじゃない。
僕らも自由にブロードキャスっていいんだぜ?

PendingIntent

A description of an Intent and target action to perform with it.
android.app.PendingIntent

概要

他のアプリケーションが、あとから実行できるIntent。

Intentを他のアプリに渡す。

他のアプリケーション(VM)に、
あとで呼び出して欲しいIntentを作って渡す。

といってもIntentは自分のアプリ内に対する、明示的なものじゃないとダメな。

Intentの投げかたを指定する。

Intentを

  • Context.startActivity()
  • Context.startService()
  • Context.sendBroadcast()

などに渡すとそのまま実行されるが、

  • PendingIntent.getActivity()
  • PendingIntent.getService()
  • PendingIntent.getBroadcast()

に渡すとそれぞれの保留Intentを取得できる。
これを他のアプリケーションに渡してあげよう。

どうやって実現されてるの。

他のアプリ(VM)からIntent実行できるとかキモいよな。
しかもPendingIntentは親のアプリが死んでも使える。ヤバい。

果たしてその実体は!?という感じだけど、
PendingIntentはただのトークン。

トークンとデータを紐付けているのはAndroidのシステム。

つまり大元のシステム側にデータを保存してるだけ。

使いまわされろ。

もしも同じようなPendingIntentがget(getActivityとか)されたら、
使いまわされるような仕組みになっている。これ大事。

同じようなやつかどうかはIntent.filterEqualsによって判定されるんだけど、
これ、extraをみていない。
(action, data, type, class, categoriesが同じかどうかを判定している)

だからextraかえたPendingIntentを2つ作ると、実体は1つだけになる。

この使いまわしの方法はgetするときにflagで指定できる。
で使いまわされたくないなら、getするときのrequestCodeを別々の値にする。

つまり、この設計思想はアレに似てる。そう、
Context.startActivityForResult() だ。

他のActivityに対して返り値を1つだけ要求する。
他のアプリに対してIntentを1つだけ要求する。感じ。

まあそれだけだとアレだから、ってのでrequestCodeが用意してある。
requestCodeアプリ内でユニークにしておけば、上書きされることは無い。

しかし果たしてほんとにぜったいにPendingIntentが複数必要なのか、
自分の胸に手を当てて確認して欲しい。

Androidがプロセスを殺す順番

When deciding which processes to kill, the Android system weighs their relative importance to the user.
Processes and Threads

Androidは 殺人鬼 である。
生きている価値がないと(一方的に、偏見をもって)みなされたアプリは
片っ端から殺されてしまう。

一方、Androidは O MO TE NA SHI 上手 でもある。
必要なアプリは丁重に扱われ、少ない、そして限られた、
すごーく貴重な リソースを提供し、
たとえ画面から見えなくなっても紅茶飲み放題は当たり前、
マフィンも食べ放題であるかは電脳世界の中に入ったことがないのでわからない。

概要

なぜ殺されるのだろう。

まず、たいていのアプリは1つのプロセス内で動いている。

Androidはバックグラウンドにまわったプロセスも保持している。
だって一旦別のアプリ使い始めたっていっても、油断ならないじゃん。
またすぐ戻って使うかもしれないじゃん、キミたちは。

でもメモリが少なくなると、んな悠長なこと言っていられないから、
そういうプロセスは終了させてしまう。
これはLow Memory Killerによる挙動だから詳しくはそれでググって。

O MO TE NA SHI。

無差別に殺すわけではなくて、優先順をつけている。
このアンドロイド、一応の理性は有るようだ。

  1. 活きActivity createなService 活きBroadcastReceiver
  2. 中断Activity bindなService
  3. 他のService
  4. Others

だいたいこれ。
優先順が高いほど、殺されにくい。

プロセスを復元せよ。

殺されたプロセスは復元される。
別にLow Memory Killerに殺されたときだけではない。
ランタイムエラーで殺されたプロセスも復元されるんだよ。

そのときActivityは1つだけ復元される。
殺される前、最後に開いていた画面だ。
他のActivityはまだCreateされない。
画面を戻ると順々に復元されていく。

ActivityはActivity.onCreate()の引数として
Activity.onSavedInstanceState()で保存したデータを取得できる。
だからプロセスごと殺されたときに復元できるよう、
十分なデータを保存しておくべきだ。
けどめんどくさいから重要なやつだけでいいよ。
staticな変数とか。

もしくは嘘。

ブログでは メモリ不足だとActivityは死ぬ! と書かれているのを見る。

たしかにActivityは殺される。
しかし、Activityが殺されるのは プロセス が殺されるからだ。

ずーっと同じアプリを使っている限り、
メモリが足りなくなった時に、1つ前のActivityが死んだりはしない。

もしメモリ不足であるActivityが死んだのなら、
そのアプリの他のActivityも死んでいる。

Intent

An intent is an abstract description of an operation to be performed.
android.content.Intent

概要

俺の意思。

まずインテントは「意思」という意味だ、念のため。

Intentはactiondataという2つの情報で
やりたい操作を抽象化したもの だといえる。

例えばactionが「編集する(ACTION_EDIT)」で、
datacontent://contacts/people/1だったら、
電話帳のIDが1のデータを編集したい!
ってことだろう。

こんな感じの「やりたい!」を、
あるActivityに渡したり、Android端末中に撒き散らしたりできる。

意思の情報。

Intentが持つのはactiondataだけではない。

  • category
  • type
  • component
  • extras

この中で重要なのはextrasだ。
基本的には相手に渡したい情報をextrasへ突っ込んでいくことになる。

たとえば「メールを送りたいんです!」というインテントだったら、
extrasにはメールの題名や本文が入っているのだろうね。

渡せ。

渡す方法も大切。

Activityを立ち上げたかったら、
startActivityを使って渡す。

ブロードキャストりたかったら、
sendBroadcastを使って渡す。

例えばブロキャスればレシーバが勝手に反応してくれる。
actionの値を見て、自分が処理すべき情報かを見極めている。
「それだったら俺やりまっせー」
みたいなことだ。

干渉を防げ。

actionは文字列なんです。
だから他のプロセス(VM)でも比較できるわけ。

で、Intentを撒き散らしちゃう時に
actionの値としてACTION_WRITEとか設定しちゃうと、
他のアプリも同じ値のactionを投げてきそうじゃん。
そしたら間違って受け取っちゃう。

だから自分の アプリオリジナル☆ なインテントを作るときは、
com.oreno.package.name.ACTION_WRITEみたいな名前にしようぜ、
ってGoogleは言ってる。

はっきりせい

  • 暗黙的なインテント「メール送りたいんです!」
  • 明示的なインテント「Gmailさん!メール送りたいんです!」

暗黙的なインテント はアニメタイトルっぽいけどアニメタイトルではない(随筆時)
Implicitが暗黙的、Explicitが明示的だ。

ここでは 明示的=相手を指定している と言い換えられる。
中二病的な名前が付いているだけなのでGoogleにも
そっち系の人がいるということだろう。よかったね。

IntentFilter

マニフェストに書いとくと、そのIntentを受け取りやがる。

BroadcastReceiver

Base class for code that will receive intents sent by sendBroadcast().
android.content.BroadcastReceiver

概要

イメージせよ。

BroadcastをReceiveする当番。

Android端末内にはブロードキャストがアホみたいに飛び交っている。
で、このレシーバがそれらを受信する。クールでかっこいいですね〜

レシーバ自体はブロードキャストが来た時に起動して、処理が終わったら消える。

登録が必要。

ただBroadcastReceiverを作っておけばおkwwwという世界観ではなくて、
AndroidManifest.xmlに登録する必要がある。

ActivityAndroidManifest.xmlに定義するんだから仕方あるまい。
と思う人はそれでいい。

納得行かない人は動的にも登録できるよ。

受け取るブロードキャスト。

何も設定しないと全部受け取る。
他のアプリケーションから受け取らない設定にもできる。exported="false"な。

ICSからはブロードキャストを送るときにIntent.setPackageによって、
受信相手のパッケージ名を指定できる。

まあでもアプリケーション間通信とかしたいわけじゃないってときは、
LocalBroadcastReceiverってのがあるから。それ使って。

すぐ死ぬ。

onReceive(Context, intent) が呼ばれると生き、returnした後に殺されうる。
なので非同期処理はできない。なぜならcallbackを待つ間に死にかねないから。

例えばダイアログは出せない。てゆーかさ、
バックグラウンドで何かが起こったことを知らせるのは、Notificationだろ?
NotificationManagerを使おう。

殺されやすさ。

onReceive(Context, intent) 実行中ならforegroundプロセスとして扱われる。
よっぽどメモリがアレにならない限り死なない。
で、問題はreturnした後。
殺されやすさは、そのプロセス内の他のアプリケーションに依存する。
もしApplicationと同じプロセスで動かしていたら、
そのApplicationの殺されやすさと同じになるし、
専用のプロセスで動いていたら、空のプロセスとみなされて速攻殺される。

だから、長時間の処理ならServiceと一緒に使うといいよ。
サービスを起動して、処理が終わったら自殺すればいい。

選べるプロセス。

そういうわけで、実行するプロセスが選べる。
Applicationと同じプロセスで実行するか、別のプロセスで実行するか。

registerReceiverで別スレッドにしない限り、
onReceiveはそのプロセスのMainThreadで実行される。
10秒間のタイムアウトが存在するので長時間の処理ならServiceと一緒に(略)
<receiver>タグで指定されたレシーバはreturnしだい死ぬ。

雑感

onReceive(Context, intent)内しか生きられない儚い命だけど、Contextもらえるからね。
Intentから情報をもらって、Contextに対して処理するって使い方っすね。
すぐ死ぬからメンバー変数とか作るのはやめてね。