5

今日、ハンドラーとルーパーがどのように連携するかについてのブログとソース コードを読みました。

私が学んだことに基づいて、ThreadLocal魔法を使用することで、各スレッドに 1 つのルーパーしか持つことができません。prepare通常、ハンドラーはメイン スレッドで開始されます。そうでない場合は、別のスレッドでルーパーを手動で開始または呼び出してからループする必要があります。

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();
    }
}

私を本当に混乱させたのはloop()、メインスレッドでした。Looperのソースコードでこれを読んだように。メッセージ キューを処理し、コールバックが処理するメッセージをディスパッチするのは無限ループです。

このhttps://stackoverflow.com/a/5193981/2290191によると、Handler とその Looper は同じスレッドで実行されます。

メイン スレッドに無限ループがあると、UI システム全体がブロックされませんか?

何かを見逃すなんて、私はとてもばかげているに違いないことを知っています。しかし、誰かがこの背後にある秘密を明らかにするのは素晴らしいことです.

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}
4

2 に答える 2

6

実際には、メインスレッドのルーパーが描画を可能にします。ビューが無効になると、描画が要求されたことを伝えるメッセージがメインのルーパーに渡されます。ルーパーがそのメッセージを処理すると、実際の描画が行われます。UI スレッドを停止する他のアクティビティが描画を停止する理由は、Looper がその描画メッセージを処理できないためです。

これは、Windows から Mac、Android に至るまで、あらゆるイベント ベースのシステムで描画が機能する方法とほぼ同じです。

メッセージを送信する代わりに、すぐに描画してみませんか? パフォーマンス。描画が遅い。イベントに応じて複数の変更を行う場合、変更ごとに画面を再描画する必要はありません。このようにすることは、1 つのイベントを処理するためのすべての再描画を 1 つの再描画にまとめることを意味します。たとえば、あるビューのテキストと別のビューの画像を設定すると、両方が同時に再描画され、一度だけ再描画されます。

于 2016-03-11T04:19:34.163 に答える