103

LooperHandlerおよびの公式Androidドキュメント/ガイドを確認しましたMessageQueue。しかし、私はそれを得ることができませんでした。私はAndroidを初めて使用し、これらの概念に非常に混乱しました。

4

5 に答える 5

109

ALooperはメッセージ処理ループです。からアイテムを読み取って処理しますMessageQueue。このクラスは通常、 (のサブクラス)Looperと組み合わせて使用​​されます。HandlerThreadThread

Aは、主にスレッドのにメッセージとオブジェクトを投稿することによってHandler、との対話を容易にするユーティリティクラスです。aが作成されると、特定の(および関連するスレッドとメッセージキュー)にバインドされます。LooperRunnableMessageQueueHandlerLooper

通常の使用法では、を作成して開始してから、他のスレッドがインスタンスと対話できる1つまたは複数のオブジェクトHandlerThreadを作成します。で実行中に作成する必要がありますが、一度作成すると、のスケジューリング方法(など)を使用できるスレッドに制限はありません。HandlerHandlerThreadHandlerHandlerThreadHandlerpost(Runnable)

Androidアプリケーションのメインスレッド(別名UIスレッド)は、アプリケーションインスタンスが作成される前に、ハンドラースレッドとして設定されます。

クラスのドキュメントとは別に、ここでこれらすべてについての素晴らしい議論があります。

PS上記のすべてのクラスはパッケージに含まれていますandroid.os

于 2012-10-13T23:52:14.960 に答える
96

Androidのメインスレッド以外のスレッドからUIコンポーネントを直接更新することは違法であることが広く知られています。このAndroidドキュメント(UIスレッドでの高価な操作の処理)では、別のスレッドを開始して高価な作業を実行し、完了後にUIを更新する必要がある場合の手順を示しています。アイデアは、メインスレッドに関連付けられたHandlerオブジェクトを作成し、適切なタイミングでそれにRunnableを投稿することです。これはメインスレッドで呼び出されます。このメカニズムは、LooperクラスとHandlerクラスで実装されます。Runnable

このLooperクラスは、リストメッセージを含むMessageQueueを維持します。Looperの重要な特徴は、が作成されスレッドに関連付けられていることです。この関連付けは永久に保持され、壊したり変更したりすることはできません。また、スレッドを複数のスレッドに関連付けることはできないことに注意してください。この関連付けを保証するために、はスレッドローカルストレージに格納され、コンストラクターを介して直接作成することはできません。それを作成する唯一の方法は、でpreparestaticメソッドを呼び出すことです。prepareメソッドは最初にThreadLocalを調べますLooperLooperLooperLooperスレッドに関連付けられたルーパーがまだ存在しないことを確認するために、現在のスレッドの 検査後、新しいLooperものが作成され、に保存されThreadLocalます。を準備したら、ループLooperメソッドを呼び出して新しいメッセージをチェックし、それらを処理する必要があります。Handler

名前が示すように、Handlerクラスは主に現在のスレッドのメッセージの処理(追加、削除、ディスパッチ)を担当しますMessageQueueHandlerインスタンスもスレッドにバインドされます。ハンドラーとスレッド間のバインディングは、とを介して実現さLooperMessageQueueます。AHandler常ににバインドされLooperその後、に関連付けられたスレッドにバインドされますLooper。とは異なりLooper、複数のHandlerインスタンスを同じスレッドにバインドできます。でpostまたは同様のメソッドを呼び出すたびHandlerに、関連付けられたに新しいメッセージが追加されますMessageQueue。メッセージのターゲットフィールドは現在のHandlerインスタンスに設定されます。いつLooperこのメッセージを受信すると、メッセージのターゲットフィールドでdispatchMessageが呼び出されるため、メッセージは処理対象のHandlerインスタンスに戻りますが、正しいスレッド上にあります。Looperとの間の関係をHandler以下MessageQueueに示します。

ここに画像の説明を入力してください

于 2012-10-13T23:55:19.283 に答える
89

ルーパーから始めましょう。Looperとは何かを理解すると、Looper、Handler、MessageQueueの関係をより簡単に理解できます。また、GUIフレームワークのコンテキストでLooperが何であるかをよりよく理解できます。ルーパーは2つのことをするように作られています。

1)Looperは、メソッドが戻ると終了する通常のスレッドを、 Androidアプリが実行されるまで継続的に実行されるものに変換します。これはGUIフレームワークで必要です(技術的には、メソッドが戻ると終了します。しかし、私が何を意味するのかを明確にしましょう。下)。run()run()

2)Looperは、実行するジョブがキューに入れられるキューを提供します。これは、GUIフレームワークでも必要です。

ご存知かもしれませんが、アプリケーションが起動されると、システムは「メイン」と呼ばれるアプリケーションの実行スレッドを作成します。Androidアプリケーションは通常、デフォルトで「メインスレッド」と呼ばれる単一のスレッドで完全に実行されます。しかし、メインスレッドは秘密の特別なスレッドではありませんnew Thread()これは、コードを使用して作成することもできる通常のスレッドです。つまり、run()メソッドが返されると終了します。以下の例を考えてみてください。

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

それでは、この簡単な原則をAndroidアプリに適用してみましょう。Androidアプリを通常のスレッドで実行するとどうなりますか?「メイン」または「UI」またはアプリケーションを起動するものと呼ばれるスレッドで、すべてのUIを描画します。したがって、最初の画面がユーザーに表示されます。ならどうしよう?メインスレッドは終了しますか?いいえ、すべきではありません。ユーザーが何かをするまで待つべきですよね?しかし、どうすればこの動作を実現できますか?さて、私たちはObject.wait()またはで試すことができますThread.sleep()。たとえば、メインスレッドは最初の画面を表示するための最初のジョブを終了し、スリープします。新しいジョブがフェッチされると、起動します。つまり、中断されます。これまでのところ良好ですが、現時点では、複数のジョブを保持するためのキューのようなデータ構造が必要です。ユーザーが画面を連続してタッチし、タスクの完了に時間がかかる場合を考えてみてください。したがって、先入れ先出し方式で実行されるジョブを保持するためのデータ構造が必要です。また、割り込みを使用して、常に実行され、プロセスジョブが到着したときにスレッドを実装することは簡単ではなく、複雑で、多くの場合、保守不可能なコードにつながることを想像してみてください。そのような目的のために新しいメカニズムを作成したいのですが、それがLooperのすべてですルーパークラスの公式文書「スレッドにはデフォルトでメッセージループが関連付けられていません」と述べており、Looperは「スレッドのメッセージループを実行するために使用される」クラスです。これで、それが何を意味するのかを理解できます。

ハンドラーとメッセージキューに移動しましょう。まず、MessageQueueは前述のキューです。それはルーパーの中にあり、それだけです。Looperクラスのソースコードで確認できます。Looperクラスには、MessageQueueのメンバー変数があります。

では、ハンドラーとは何ですか?キューがある場合は、新しいタスクをキューにエンキューできるようにするメソッドがあるはずですよね?それがハンドラーが行うことです。さまざまな方法を使用して、新しいタスクをキュー(MessageQueue)にエンキューできpost(Runnable r)ます。それでおしまい。これはすべて、ルーパー、ハンドラー、およびメッセージキューに関するものです。

私の最後の言葉は、基本的にLooperは、GUIフレームワークで発生する問題に対処するために作成されたクラスです。しかし、この種のニーズは他の状況でも発生する可能性があります。実際、これはマルチスレッドアプリケーションで非常に有名なパターンであり、Doug Leaによる「Javaでの同時プログラミング」で詳しく知ることができます(特に、4.1.4章「ワーカースレッド」が役立ちます)。また、この種のメカニズムはAndroidフレームワークに固有のものではないことを想像できますが、すべてのGUIフレームワークはこれにいくらか類似している必要があります。JavaSwingフレームワークにもほぼ同じメカニズムがあります。

于 2015-12-30T07:02:07.700 に答える
28

MessageQueue:これは、によってディスパッチされるメッセージのリストを保持する低レベルのクラスLooperです。メッセージはに直接追加されるのMessageQueueではなく、 。[ 3 ]Handlerに関連付けられたオブジェクトを介して追加されます。Looper

LooperMessageQueue:ディスパッチされるメッセージを含むをループします。キューを管理する実際のタスクはHandler、メッセージキュー内のメッセージの処理(追加、削除、ディスパッチ)を担当するによって行われます。[ 2 ]

Handler:スレッドのに関連付けられたオブジェクトを送信および処理できMessageます。各Handlerインスタンスは、単一のスレッドとそのスレッドのメッセージキューに関連付けられています。[ 4 ]RunnableMessageQueue

新しいを作成するとHandler、それを作成しているスレッドのスレッド/メッセージキューにバインドされます-その時点から、メッセージとランナブルをそのメッセージキューに配信し、メッセージキューから出てきたときにそれらを実行します。

理解を深めるために、下の画像[ 2 ]をご覧ください。

ここに画像の説明を入力してください

于 2016-05-01T13:16:59.693 に答える
0

答えを拡張する、@ K_Anasによる、例を挙げて、それが述べたように

Androidのメインスレッド以外のスレッドからUIコンポーネントを直接更新することは違法であることが広く知られています。

たとえば、スレッドを使用してUIを更新しようとした場合です。

    int count = 0;
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                while(true) {
                    sleep(1000);
                    count++;
                    textView.setText(String.valueOf(count));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


   ).start();

アプリは例外でクラッシュします。

android.view.ViewRoot $ CalledFromWrongThreadException:ビュー階層を作成した元のスレッドのみがそのビューにアクセスできます。

言い換えると、 ieまたはHandlerパスタスクへの参照を保持するを使用する必要があります。MainLooperMain ThreadUI ThreadRunnable

  Handler handler = new Handler(getApplicationContext().getMainLooper);
        int count = 0;
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    while(true) {
                        sleep(1000);
                        count++;
                        handler.post(new Runnable() {
                           @Override
                           public void run() {
                                 textView.setText(String.valueOf(count));
                           }
                         });

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    ).start() ;
于 2020-01-17T15:31:21.903 に答える