41

私はスレッド化に頭を悩ませようとしていますが、 を使用してHandlerにメッセージ/ランナブルをポストしMessageQueue、それが によって取得され、処理Looperのために に送り返されることを知っていHandlerます。

Handlerアクティビティでに投稿した場合、 ActivityHandlerMessageQueueおよびLooperすべてが UI スレッドで実行されていますか? そうでない場合、誰かがこれがどのようにまとめられたのか説明してもらえますか? :)

4

4 に答える 4

70

簡単な答え:それらはすべて同じスレッドで実行されます。ライフサイクル コールバックからインスタンス化された場合Activity、それらはすべてメイン UI スレッドで実行されます。

長い答え:

スレッドにはLooper、 を含む がある場合がありますMessageQueueこの機能を使用するにはLooper、 (静的) を呼び出して現在のスレッドで を作成し、 (静的)Looper.prepare()を呼び出してループを開始する必要がありますLooper.loop()Looperこれらは、スレッドごとに 1 つしかないと想定されているため、静的です。

通常、への呼び出しloop()はしばらく戻りませんMessageQueueが、 からメッセージ (「タスク」、「コマンド」、または呼び出したいもの) を取得し続け、それらを個別に処理します (たとえばRunnable、メッセージに含まれる a を呼び戻すことによって)。キューにメッセージが残っていない場合、スレッドは新しいメッセージがあるまでブロックされます。a を停止するLooperには、それを呼び出す必要がありますquit()(これはおそらくループをすぐに停止するのではなく、ループから定期的にチェックされるプライベート フラグを設定し、停止するように通知します)。

ただし、メッセージをキューに直接追加することはできません。代わりにMessageQueue.IdleHandler、コールバックを待機するためにを登録しqueueIdle()ます。このコールバックで、何かを実行するかどうかを決定できます。すべてのハンドラが順番に呼び出されます。(したがって、「キュー」は実際にはキューではなく、代わりに定期的に呼び出されるコールバックのコレクションです。)

前の段落に関する注意:これは私が実際に推測したものです。それに関するドキュメントは見つかりませんでしたが、それは理にかなっています。

更新: ahcoxのコメント彼の回答を参照してください。

これは大変な作業なので、フレームワークはHandler物事を単純化するためのクラスを提供します。インスタンスを作成するHandlerと、(デフォルトで)Looper現在のスレッドにすでにアタッチされている にバインドされます。( は、おそらく への参照を に格納しているため、先に呼び出したので、何にアタッチするかをHandler知っています。)Looperprepare()LooperThreadLocal

を使用すると、 「メッセージをスレッドのメッセージキューに入れる」(いわば)ためにHandler呼び出すことができます。post()HandlerすべてのIdleHandlerコールバックを処理し、投稿されたものが確実にRunnable実行されるようにします。(遅れて投稿した場合は、時間がすでに正しいかどうかも確認する場合があります。)

明確にするために: ループしているスレッドに実際に何かをさせる唯一の方法は、そのループメッセージを投稿することです。これは、ルーパーで quit() を呼び出すまで有効です。


Android UI スレッドについて:Looperある時点 (おそらくアクティビティなどが作成される前) で、フレームワークは(を含む) をセットアップしてMessageQueue開始しました。この時点から、UI スレッドで発生するすべてのことは、そのループを介して行われます。これには、アクティビティのライフサイクル管理などが含まれます。オーバーライドするすべてのコールバック ( onCreate()onDestroy()...) は、少なくともそのループから間接的にディスパッチされます。たとえば、例外のスタック トレースで確認できます。(試してみることができます。int a = 1 / 0;どこかに書いてくださいonCreate()...)


これが理にかなっていることを願っています。以前はわかりづらくてすみません。

于 2011-03-04T12:55:43.947 に答える
11

質問の「すべてがどのように組み合わされるか」の部分をフォローアップします。user634618 が書いたように、ルーパーはアプリケーションの main の場合はメイン UI スレッドであるスレッドを引き継ぎますLooper

  • Looper.loop()メッセージ キューからメッセージを引き出します。各メッセージには、返される関連付けられたハンドラー (ターゲット メンバー) への参照があります。
  • Looper.loop()キューから取得した各メッセージの 内部:
    • loop()public void Handler.dispatchMessage(Message msg)Message に格納されている Handler をターゲット メンバーとして使用して呼び出します。
    • メッセージに Runnable コールバック メンバーがある場合は、それが実行されます。
    • それ以外の場合、Handler に共有コールバック セットがある場合は、それが実行されます。
    • handleMessage()それ以外の場合は、Message を引数としてHandlerが呼び出されます。(注: AsyncTask のように Handler をサブクラス化する場合は、そのようにオーバーライドできますhandleMessage()。)

共同作業するすべてのオブジェクトが同じ UI スレッド上にあるという質問については、メッセージの送信先とHandler同じスレッドで を作成する必要があります。Looperそのコンストラクターは current を検索し、Looperそれをメンバーとして格納して、Handlerを thatに結び付けLooperます。またLooper、そのメッセージ キューを独自のメンバーで直接参照します。は、任意のスレッドからHandlerに作業を送信するために使用できますLooperが、メッセージ キューのこの ID は、 のスレッドで実行される作業をルーティングしLooperます。

別のスレッドでコードを実行していて、Runnable を送信して UI スレッドで実行したい場合は、次のように実行できます。

// h is a Handler that we constructed on the UI thread.
public void run_on_ui_thread(final Handler h, final Runnable r)
{
   // Associate a Message with our Handler and set the Message's
   // callback member to our Runnable:
   final Message message = Message.obtain(h, r);

   // The target is the Handler, so this asks our Handler to put
   // the Message in its message queue, which is the exact same
   // message queue associated with the Looper on the thread on
   // which the Handler was created:
   message.sendToTarget();
}
于 2012-03-09T18:04:39.280 に答える