私はスレッドに頭を回そうとしていますが、Handler
を使用してメッセージ/実行可能ファイルをMessageQueue
に投稿し、Looper
として処理のためにハンドラーに送り返されます。
アクティビティのハンドラーに投稿した場合、Activity
、Handler
、MessageQueue
、Looper
はすべてUIスレッドで実行されていますか?そうでない場合、誰かがこれがすべて一緒になる方法を説明できますか? :)
短い答え:それらはすべて同じスレッドで実行されます。 Activity
ライフサイクルコールバックからインスタンス化される場合、それらはすべてメインUIスレッドで実行されます。
長い答え:
スレッドには、Looper
を含むMessageQueue
がある場合があります。この機能を使用するには、現在のスレッドにLooper
を呼び出す必要があります。 (静的)Looper.prepare()
を呼び出し、次に(これも静的)Looper.loop()
を呼び出してループを開始します。スレッドごとにLooper
が1つしかないため、これらは静的です。
loop()
の呼び出しは、通常、しばらくは戻りませんが、メッセージ( "タスク"、 "コマンド"など)を受け取り続けますMessageQueue
からそれらを呼び出す)ようにして、それらを個別に処理します(たとえば、メッセージに含まれるRunnable
を呼び出すことによって)。キューにメッセージが残っていない場合、スレッドは新しいメッセージがあるまでブロックされます。 Looper
を停止するには、quit()
を呼び出す必要があります(ループをすぐに停止するのではなく、ループから定期的にチェックされるプライベートフラグを設定し、停止するように通知します)。
ただし、メッセージをキューに直接追加することはできません。代わりに、MessageQueue.IdleHandler
を登録してqueueIdle()
コールバックを待機します。コールバックでは、何かをしたいかどうかを決定できます。すべてのハンドラが順番に呼び出されます。 (つまり「キュー」は実際にはキューではなく、定期的に呼び出されるコールバックのコレクションです。)
前の段落に関する注意:これは私が実際に推測したことです。それに関するドキュメントは見つかりませんでしたが、それは理にかなっています。
update:参照してください ahcox 'コメント および 彼の答え 。
これは多くの作業なので、フレームワークはHandler
クラスを提供して、物事を簡略化します。 Handler
インスタンスを作成すると、(デフォルトで)現在のスレッドにすでにアタッチされているLooper
にバインドされます。 (Handler
は、先にprepare()
を呼び出したため、Looper
に何をアタッチするかを認識しています。これにより、Looper
への参照がThreadLocal
に格納された可能性があります。)
Handler
を使用すると、post()
を呼び出すだけで、「スレッドのメッセージキューにメッセージを書き込む」ことができます。 Handler
はすべてのIdleHandler
コールバックを処理し、ポストされたRunnable
が実行されることを確認します。 (遅延して投稿した場合、タイミングが既に正しいかどうかもチェックする場合があります。)
明確にする必要があります。実際にループスレッドdoを作成する唯一の方法は、メッセージをループに投稿することです。これは、ルーパーでquit()を呼び出すまで有効です
Android UIスレッドについて:について(おそらくアクティビティなどが作成される前に)フレームワークはLooper
(MessageQueue
を含む)を設定して開始します。この時点から、UIスレッドで発生するすべてのことはそのループを介して行われます。これにはアクティビティのライフサイクル管理などが含まれます。オーバーライドするすべてのコールバック(onCreate()
、onDestroy()
...)は少なくともそのループから間接的にディスパッチされます。たとえば、例外のスタックトレースでそれを確認できます([int a = 1 / 0;
]をonCreate()
...)
これが理にかなっているといいのですが。以前は不明確だったため申し訳ありません。
質問の「すべてがどのように統合されるか」の部分のフォローアップ。 user634618が書いたように、ルーパーはスレッド(アプリケーションのメインLooper
の場合はメインUIスレッド)を引き継ぎます。
Looper.loop()
は、メッセージキューからメッセージを引き出します。各メッセージには、それが(ターゲットメンバー)に返される関連付けられたハンドラへの参照があります。Looper.loop()
の内部キューから取得した各メッセージ:loop()
は、メッセージに格納されているハンドラーをターゲットメンバーとして使用してpublic void Handler.dispatchMessage(Message msg)
を呼び出します。handleMessage()
は、メッセージを引数として呼び出されます。 (AsyncTaskのようにHandlerをサブクラス化する場合は、handleMessage()
をオーバーライドすることができます)。同じUIスレッド上にあるすべての共同オブジェクトについての質問では、Handler
は、メッセージの送信先であるLooper
と同じスレッド上に作成する必要があります。そのコンストラクターは、現在のLooper
を検索してメンバーとして格納し、Handler
をそのLooper
に結び付けます。また、Looper
のメッセージキューを自身のメンバーで直接参照します。 Handler
は、任意のスレッドからLooper
に作業を送信するために使用できますが、メッセージキューのこのIDは、Looper
のスレッドで実行される作業をルーティングします。
別のスレッドでいくつかのコードを実行していて、UIスレッドで実行されるRunnableを送信したい場合は、次のようにします。
// 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();
}
コンセプトを理解するために、私は自分でこれらのインターフェースを実装しようとしています。簡単に言うと、必要に応じてインターフェースを使うだけです。これが私のテストコードです:
import Java.util.concurrent.BlockingQueue;
import Java.util.concurrent.LinkedBlockingQueue;
public class TestLooper {
public static void main(String[] args) {
UIThread thread = new UIThread();
thread.start();
Handler mHandler = new Handler(thread.looper);
new WorkThread(mHandler, "out thread").run();
}
}
class Looper {
private BlockingQueue<Message> message_list = new LinkedBlockingQueue<Message>();
public void loop() {
try {
while (!Thread.interrupted()) {
Message m = message_list.take();
m.exeute();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void insertMessage(Message msg) {
message_list.add(msg);
}
}
class Message {
String data;
Handler handler;
public Message(Handler handler) {
this.handler = handler;
}
public void setData(String data) {
this.data = data;
}
public void exeute() {
handler.handleMessage(this);
}
}
class Handler {
Looper looper;
public Handler(Looper looper) {
this.looper = looper;
}
public void dispatchMessage(Message msg) {
System.out.println("Handler dispatchMessage" + Thread.currentThread());
looper.insertMessage(msg);
}
public Message obtainMessage() {
return new Message(this);
}
public void handleMessage(Message m) {
System.out.println("handleMessage:" + m.data + Thread.currentThread());
}
}
class WorkThread extends Thread {
Handler handler;
String tag;
public WorkThread(Handler handler, String tag) {
this.handler = handler;
this.tag = tag;
}
public void run() {
System.out.println("WorkThread run" + Thread.currentThread());
Message m = handler.obtainMessage();
m.setData("message " + tag);
handler.dispatchMessage(m);
}
}
class UIThread extends Thread {
public Looper looper = new Looper();
public void run() {
//create handler in ui thread
Handler mHandler = new Handler(looper);
new WorkThread(mHandler, "inter thread").run();
System.out.println("thead run" + Thread.currentThread());
looper.loop();
}
}
アクティビティのハンドラーに投稿した場合、アクティビティ、ハンドラー、メッセージキュー、ルーパーはすべてUIスレッドで実行されていますか?そうでない場合、誰かがこれがすべて一緒になる方法を説明できますか? :)
作成方法によって異なります Handler
ケース1:
Handler()
デフォルトのコンストラクターは、このハンドラーを現在のスレッドの Looper に関連付けます。
UIスレッドでこのようにHandler
を作成すると、UIスレッドのHandler
にLooper
が関連付けられます。 MessageQueue
は、UIスレッドでLooper
にも関連付けられています。
ケース2:
Handler (Looper looper)
デフォルトのものの代わりに提供されたルーパーを使用してください。
HandlerThread を作成し、HandlerThreadのルーパーをハンドラーに渡すと、ハンドラーとルーパーはUIスレッドではなくHandlerThreadに関連付けられます。 Handler
、MessageQueue
およびLooper
はHandlerThread
に関連付けられています。
ユースケース:ネットワークを実行したいOR IOオペレーション。UIスレッドでは実行できないため、HandlerThread
が便利あなたのために。
HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
HandlerThreadからUIスレッドにデータを返す場合は、UIスレッドからLooper
を使用してハンドラーを1つ作成し(例:responseHandler)、sendMessage
を呼び出します。 UIスレッドresponseHandler
はhandleMessage
をオーバーライドする必要があります
詳細については、これらの投稿を参照してください。
ルーパーの目的と使用方法は? (概念について)
Android:スレッドでトースト (これらの概念すべてをリンクするコード例)