• スポンサードリンク

Androidのカメラ関係のメソッドを非同期で実行したい

Android Camera 非同期

AndroidのAPIレファレンスのCamera#openには、「注意:このメソッドの呼び出しには時間がかかる端末があるので、メインスレッドをブロックしてしまわないようにAsyncTask等のworkerスレッドから呼び出すのがベストだ」なんてことが書いてあります。
でも、非同期でカメラ関係のメソッドを扱っているサンプルはほとんどありません。SDKについてくるサンプルでもメインスレッドからopenを呼び出しています。言ってることとやってることが違うやん、ってことで、今回は非同期でカメラを扱うサンプルを作ってみました。まぁ今どきの端末なら同期呼び出ししても大抵は問題ないんですけど(^_^;)
ということで非同期で呼び出せれば何でもいいんですけど、普通ならAPIレファレンスにしたがってAsyncTask・・・いまならAsyncTaskLoaderかな?を使うところ。でもいろんな理由で個人的にどちらも好きじゃありません。
かと言ってJavaのThreadクラスを直接使うのもちょっと面倒。そこでThreadとHandler(Looper)の合わせ技で行きたいと思います。

とりあえず非同期処理のコア部分はこんな感じにしてみました。大した処理をするわけでも無いのでメッセージの処理は省略して、Runnableだけを非同期で処理できるようにします。自分で実装したいならこんな感じ。

※ handler.postやhandler.postDelayedする前にhandler.removeCallbacksを呼んでいるのは、Camera#autoFocusメソッドなども非同期で呼び出すことを想定しているためです。autoFocusのコールバック(AutoFocusCallbackインターフェース)のメソッドはautoFocusの呼び出し1回につき1回しか呼び出されないのかと思いきや実は複数回呼び出される端末があるのです(APIレファレンスには1回しか呼び出されないとは書いてないけど)。このような端末で周期的にautoFocusを呼びだそうすると思った通りの周期にならないことがあるので、ここでは未実行の同じRunnableインスタンスがあればremoveCallbacksで除去するようにしています。

ちなみに、名前はCameraThreadにしてみましたが、このクラス自体はカメラの処理とは何の関係も無く他の非同期処理にも使いまわせます。アプリ内の他のクラスの非同期処理でも使うのであれば、個別のファイル(CameraThread.java)として作成し、カメラの処理でしか使わないのであれば、カメラ処理を行っているクラスの静的インナークラスとして定義すれば良いと思います。

また、APIにあるHandlerThreadを使ってもOK。この場合にはActivity#onCreateやカスタムViewのコンストラクタで

として別スレッド実行用のHandlerインスタンスを取得しておいて、非同期で実行したいRunnableをHandler#post(またはpostDelay/postAtTime)でセットする。

さっきのasyncOpenCameraを呼び出すならこんな感じ。

HandlerThreadだと使う時にHandlerってのが表に出てきてしまうのが個人的にはいまいちかな〜って思います。見える必要のない実装はカプセル化して隠蔽したくないですか?。

で、肝心のカメラ処理。先日のCameraViewでのカメラのopen処理はこんなでした。

これを非同期処理にすると、例えばこんな感じ。

これで、カメラ関係のメソッド呼び出しによって生じるコールバックは全てCameraThreadのコンテキスト内で実行されるようになります\(^o^)/
でも先日のCameraViewだとこれだけでは不十分で、setupCameraParamsも非同期にしないとだめ。
でないと、asyncOpenCameraが実行されてカメラがopenされるよりも前にsetupCameraParamsが呼び出されちゃうかもしれない。
というわけで、こんな感じ。

何でかって?Handler(CameraThreadも内部でHandlerを使用)を使って非同期処理をする場合には、原則的にFIFO(First In First Out)で処理されるので、先にqueueEventしたopenCameraが処理された後にsetupCameraParamsが実行されることが保証されるためです。ってのでわかるかな?(順番をすっ飛ばして実行するようにもできるけど、今回のCameraThreadには実装していないので大丈夫)

これで、Camera#autoFocusやCamera#setOneShotPreviewCallbackなどのメソッドをUIスレッドから呼び出しても、もちろんCameraThread経由で呼び出しても、コールバックは必ずCameraThreadのコンテキスト内で実行されるようになります。ちょっとぐらい重い画像処理をonAutoFocusやonPreviewFrameで行ってもANRになったりしないので大丈夫(^^)v
あっでも本当に重い処理なら更に別スレッドに処理を投げてくださいね。

« »

  • スポンサードリンク

%d人のブロガーが「いいね」をつけました。