Locked History Actions

Android/Handler

Handler

Handlerのマニュアルには以下の記述がある。

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

が、Javaを知ってる人間がこれを読めば頭の中が「?」で埋め尽くされることは必至である。Javaのスレッドにこんな機能は存在しない。 つくづく不親切な記述である。結局のところ、Androidのソースコードを読まなければこの疑問は解決しない。

ソースを読んでみればすぐに理解できるのだが、Handlerの動作にはLooperというオブジェクトが必要であり、そのLooperオブジェクトはスレッドごとにThreadLocalを使用して保持されているのである。だから、勝手に作成したThreadに対してHandlerを作成しても例外が発生してしまう。このようなスレッドに対してはあらかじめLooperオブジェクトを作成して登録しておかなければならない。

さらに、HandlerにはLooperオブジェクトを引数として与えることのできるコンストラクタが用意されているのだから「of the thread that is creating it」という部分は間違いである。

また、Looperという名前自体も極めて不適当。これは、一般的なGUI環境で「dispatch loop」と呼ばれているものである。 なぜ、このようなわかりにくい用語を発明してしまうのだろうか?

なぜHandlerは複数作成することができるのか

GUIスレッドとそれ以外のスレッドの同期を行うためなら、Swingにあるような機能(SwingUtilities.invokeLater)で十分であると思われる。 この場合、あらゆる場所からこの同じstaticなメソッドを呼び出すことができる。専用のオブジェクトを作成する必要はない。

Android環境はメモリ量が小さいのに、どういうわけでSwingよりメモリを要求されるアプローチをとっているのだろうか? 考えられることと言えば、「一つのアプリケーション中の異なるアクティビティのGUIスレッドを異なるものになる」ということしかありえないのだが、ほんとうにこのようなことが起こるのだろうか?

しかし、いくらなんでもこのような設計はヘン過ぎるだろう。Applicationオブジェクトの作成やコールバックの呼び出し、アクティビティAの作成やコールバックの呼び出し、アクティビティBの。。。がすべて同じスレッドで無いと不便で仕方がない。未確認だが、これらは同一のスレッドで無ければ設計としてはマトモとは言えない。

したがって、Handlerの仕様は一般的なアプリケーションプログラマに使わせるにしては冗長過ぎることは明らかだ。一つのアプリ側にはただ一つのHandlerがあれば十分なのである。Swingのアプローチの方が正しい。

このアプローチはSwingだけでなくSWTも同様であり、Androidの仕様はヘンすぎる。

ActivityにおけるHandler

しかし、上記の予測に反し、ActivityごとにUIスレッドが実際に異なるか、あるいは将来的に別になることを想定していると思われる。 Androidのソースを見てみると、Activityごとに独自のHandlerを内部的に作成しており、それをActivity#runOnUiThread()メソッド(のみ)で使用している。 あるいは、この部分を記述したアホウがUIスレッド別にしようと画策していたのだろうか?

また、不便なことに、このActivity内部のHandlerはprivateになっており、外部から利用することはできない。

UI Threadはどこまでを実行するのか?

以下のようなQ&Aを発見した。

http://groups.google.co.jp/group/android-developers/browse_thread/thread/17b7a6c8eb9b69ba

この返答ぶりから考えると、すべてが同じ一つのスレッドから呼び出されると考えてよいだろう。

以下の記述を発見した。

http://developer.android.com/guide/appendix/faq/framework.html

-

  • Do all Activities run in the main thread of an application process?

-

  • By default, all of the application code in a single process runs in the main UI thread. This is the same thread that also handles UI events. The only exception is the code that handles IPC calls coming in from other processes. The system maintains a separate pool of transaction threads in each process to dispatch all incoming IPC calls. The developer should create separate threads for any long-running code, to avoid blocking the main UI thread.

-

-

  • すべてのアクティビティはアプリケーションプロセスのメインスレッドで動作するのか?

-

  • デフォルトでは、一つのプロセス中のすべてのアプリケーションコードはメインUIスレッドで動作する。 これは、UIイベントを処理するものと同じものである。 ただ一つの例外としては、他プロセスからのIPC呼び出しを扱うコードである。 システムは、別途容易されたトランザクションスレッドのプールを保持しており、それがすべてのIPC呼び出しをディスパッチする。 また、「長い処理」を行う場合にデベロッパは別スレッドを用意すべきである。メインUIスレッドのブロックを避けるためだ。

-

「デフォルトでは」というところがひっかかるが、勝手にAndroid環境が別スレッドにすることはありえないだろう。 これを勝手にやられてしまうと、多くのアプリケーションが動作しなくなる可能性があるためだ。

IPCの呼び出し(というよりも、呼び出され)処理が別スレッドで行われるのは常識的なところで、これもJava-RMIと同じである。

つまり、これらの考え方はどれもこれもSwingやJava-RMIと全く同様なのである。それならそうと明確に記述しておいてくれれば悩まないで済むものを、不必要にフレキシブルなHandlerの仕様が提示されており、アクティビティごとにHandlerが生成されているところを見れば誰だって混乱するに違いない。