web-dev-qa-db-ja.com

Android:新しいスレッドでハンドラーを作成できないのはなぜですか

新しいスレッドでハンドラーを作成する際に問題が発生しました。これは私のコードです:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        public void run() {
            Handler handler = new Handler();
        }
    }).start();
}

しかし、エラーが発生しました!誰かがこれを私に説明してもらえますか?本当にありがとう!

エラーの詳細は次のとおりです。

09-17 18:05:29.484: E/AndroidRuntime(810): FATAL EXCEPTION: Thread-75
09-17 18:05:29.484: E/AndroidRuntime(810): Java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
09-17 18:05:29.484: E/AndroidRuntime(810):  at Android.os.Handler.<init>(Handler.Java:197)
09-17 18:05:29.484: E/AndroidRuntime(810):  at Android.os.Handler.<init>(Handler.Java:111)
09-17 18:05:29.484: E/AndroidRuntime(810):  at com.example.handler.MainActivity$1.run(MainActivity.Java:57)
09-17 18:05:29.484: E/AndroidRuntime(810):  at Java.lang.Thread.run(Thread.Java:856)
26
nguyenbkcse

次のようなHandlerThreadを使用することもできます。

HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
Handler handler = new Handler(thread.getLooper());

HandlerThreadsにはLooperが関連付けられているため、これは例外をスローしません。

77
Alex Lockwood

スレッドのlifecycleは、runメソッドが戻った直後に終了します。ただし、このHandlerthreadを作成しているため、メッセージを受信して​​処理するには、ハンドラーがスレッドを実行している必要があります。

そのため、これを実行するには、runメソッドを終了しないでください。したがって、無期限に待機し、ハンドラーに到着するメッセージを処理するルーパーが必要です。

new Thread(new Runnable() {
        public void run() {
            Looper.prepare();
            Handler handler = new Handler();
            Looper.loop();
        }
    }).start();
21
nandeesh

短い答え:ハンドラーをアタッチしようとしているスレッドにはルーパーがありません。そのため、Handlerクラスのコンストラクターは例外をスローしています。代わりにHandlerThreadクラスを使用することもできます。これはAndroidフレームワークによって提供される便利なクラスです。

フードの下で何が起こっているかについては、以下をお読みください。

最初に、すべての部分を個別に議論してみましょう。

  1. 糸:

a。スレッドは単なる実行フローです。デフォルトでは、スレッドはrunnable(提供されている場合)を実行するか、runメソッドを呼び出すだけです。新しいThread.start()を呼び出したとき。 runメソッドがrun(){----}内に記述されたすべてのステートメントを実行すると、スレッドが停止し、Gcされます。

b。 AndroidにはLooperの概念があります。基本的に、スレッドはブロッキングスレッドになります。単純に言えば、スレッドが死ぬことはありません。ブロック状態になり、さらにメッセージが実行を再開するまで待機します。

以下は、ブロッキングスレッド、つまりルーパー付きのスレッドを設定する方法です。

  new Thread(new Runnable() {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}).start();

ここでは、Looper.loop()ステートメントを実行した後に死ぬだけではないスレッドが作成されます。代わりに、ループしてブロッキング状態になります。ここで、ブロッキング状態の意味、スレッドがブロッキング状態からどのように抜け出すかを尋ねる必要がありますか?最後にこのスレッドをどのようにリリースするのですか? これがハンドラーの出番です

  1. ハンドラ:

a。インスタンスが作成されるスレッドのルーパーに自身を常にアタッチします。

b。次に、接続するスレッドのメッセージを処理します。

スレッドとハンドラーをまとめる

new Thread(new Runnable() {
        public void run() {
            Looper.prepare();
            handler = new Handler();
            Looper.loop();
        }
    }).start();

a。ハンドラーがこの新しく作成されたスレッドにアタッチされる方法。 b。スレッドはブロッキングルーパースレッドとして設定されます。したがって、これは死ぬことはありません。

これで、1。このハンドラーでメッセージを送信できます。 2.このハンドラーでrunnableをポストします。

両方とも、接続されたスレッドで実行されます。

Handlerクラスを拡張し、メソッドhandleMessage(Message msg)を実装するオプションがあります。または、ハンドラクラスのコンストラクタでHandler.Callbackの実装を提供するだけです。どちらの方法でも、接続されたスレッドでhandleMessage(Messages msg)が呼び出されます。

スレッドを終了するには、特定の種類のメッセージを送信し、そのメッセージを受信したらLooper.myLooper()。quit()を呼び出すだけです。

class LooperThread extends Thread {
   public Handler mHandler;

   public void run() {
      Looper.prepare();

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
              if(msg.what == -1){
              Looper.myLooper().quit();
              }
          }
      };

      Looper.loop();
   }
}

全体的なコンセプトを理解するのに役立てば幸いです。

12
Jai Pandit

ハンドラーは、ルーパースレッドで初期化するか、ルーパーを指定する必要があります。

やりたいことに応じて、次のようにルーパーになるようにスレッドを設定できます。

new Thread(new Runnable() {
    public void run() {
        Looper.prepare();
        mHandler = new Handler();
        Looper.loop();
    }
}).start();

ルーパーはバックグラウンドスレッドにあるため、UIを更新できません。ハンドラに別のスレッドからルーパーを交互に与えることができます-この例では、ハンドラーを使用してUIを更新できます。

new Thread(new Runnable() {
    public void run() {
        Handler handler = new Handler(Looper.getMainLooper());
    }
}).start();
5
FunkTheMonk