2011年04月07日

AndroidのLooperとHandlerの実装

今回はAndroidのLooperとHandlerの話。

LooperはActivityのメインスレッドでも使われています。それはstacktraceを見るとわかります。(こちら)

スレッド間のメッセージの受け渡しに使われています。こちらのページがわかりやすいです。

今回はこの実装を見ていきます。



JavaDocに載っているLooperとHandlerの使い方の例です。

class LooperThread extends Thread {
    public Handler mHandler;
    
    public void run() {
        Looper.prepare();
        
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        
        Looper.loop();
    }

私はこれを最初に見たときに、とても違和感を感じました。

このコードのここだけを見ると、HandlerとLooperが無関係のもののように見えます。

HandlerとLooperはどのようにして対応づけられているのでしょうか?

中のコードを追いかけてみます。まずは Looper.prepare()です。

    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

このstatic methodの中でLooperのインスタンスを作成し、それをsThreadLocalのセットしていました。ひとつのスレッドには複数のLooperをセットできないようになっています。

sThreadLocalは何かと言えば、クラスのロード時に初期化されるクラス変数で、その型はjava.lang.ThreadLocalです。

public class Looper {
    ...
    // sThreadLocal.get() will return null unless you've called prepare().
    private static final ThreadLocal sThreadLocal = new ThreadLocal();

    ...

そして、これのgetterメソッドは以下の通り。

    public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }

一方、Handlerの方は

Handler.java

    public Handler() {

        ...

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

コンストラクタの中で、Looper.myLooper()を呼んでいます。これによってHandlerを生成したときのスレッドのLooperと対応がつきました。

最後にLooper.loopです。

Looper.java

    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {
            Message msg = queue.next(); // might block

            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                ...
                msg.target.dispatchMessage(msg);
                ...
                msg.recycle();
            }
        }
    }

ループの中で、queueからメッセージを取り出して、それを実行しています。

quereにメッセージを入れるのはHandlerのインスタンスを使います。

つまり、LooperとHandlerを同じスレッドで生成しておけば、それで対応がつくので、後はHandlerのインスタンスをどこでも好きなスレッドに渡せば、どのスレッドからでも対応するLooperのスレッドにメッセージを送ることができるわけです。

Androidのframeworkのソースコードを見ると、いろいろなところでLooperとHandlerが使われています。

ActivityのonStartなどの状態遷移のときには、ActivityMananagerからのプロセス間メソッド呼び出しをActivity内のBinderThreadが実行しますが、ここでメインスレッドのHandlerのインスタンスを使ってLooperにメッセージを送信することで、メインスレッドでonStartのメソッドが実行されることになります。



トラックバックURL

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード