Handlerの話

ここの所dumpsysの記事ばかり書いていたので、気分転換にHandlerの話。
Androidで画面を操作する際はUIスレッドで操作しなければならない。
でも非同期処理を行いたい、そんなときに使用するのがHandler。

HandlerについてはAndroidでアプリを作ったことがある人であれば常識なのでいまさら使い方は説明しない。
今回の記事ではよく使うHandlerは実際どんなコードで動いているか確認していきたい。

というわけでHandler.post()から順にソースコードを追ってみる。
ソースコードはAndroid4.2.2-r1.2を使用する。

まずはpost()

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

ここで引数のRunnableをgetPostMessage()に渡して、その戻り値と0をsendMessageDelayed()に渡している。

getPostMessage()の中身はこんな感じ

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

Messageを作って引数のRunnableをMessageのcallback変数に代入している。

次はsendMessageDelayed()

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

ここは引数で受け取ったMessageとディレイ時間をsendMessageAtTime()に渡している。
SystemClockから取得した時間にディレイ時間を足しているので0の場合は即時実行になるのでは。

sendMessageAtTime()

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

ここはMessageQueueと引数のMessage、実行時間をenqueueMessage()に渡すだけ。

enqueueMessage()

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

ここでMessageのtarget変数に自身を格納しMessageQueueにenqueueMessage()してる。
Handler内の処理はここまで。

次はMessageQueueにenqueueMessage()

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }
        boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }
            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

ここでは変数mMessagesがnullの場合は引数のmsgを代入するだけ。
mMessagesが存在する場合は次のメッセージが無くなるまでループを回し、mMessagesの最後に引数で渡されたmsgを追加している。
mMessagesはこのクラス内でQueueの役目をしているのかな。

これでHandlerのpost()で行っている処理は終了となる。

ここまで見てきたが実際にHandlerへ渡したRunnableを実行する処理は出てこなかった。
次回はそこら辺を探していきたい。