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に対して処理するって使い方っすね。
すぐ死ぬからメンバー変数とか作るのはやめてね。