Thread、Handler、HandlerThreadの関係の詳細

4221 ワード

前言
先日、Thread、Handler、HandlerThreadの違いを見ました.このテーマは少し面白いですが、多くの人にとって、ThreadとHandlerに詳しいかもしれませんが、主にAndroidのメッセージメカニズム(Handler、Message、Looper、MessageQueue)に関連しています.詳しくは「Handler.post(Runnable r)からAndroidのメッセージメカニズム(およびhandlerのメモリ漏洩)をもう一度整理する」を参照してください.
しかし、このHandlerThreadは何をしに来たのでしょうか.HandlerかThreadか?Handlerは非同期でUIを更新するために使用されることを知っています.より詳細にはスレッド間の通信に使用され、UIを更新するときはサブスレッドとUIマスタースレッド間の通信です.では、サブスレッドとサブスレッドの間の通信をどうすればいいのでしょうか.もちろん、あくまでHandler+Threadで完成しています(お勧めではありませんが、自分でLooperを操作する必要があります)、Google公式は親切にクラスをパッケージしてくれました.それはさっき言ったように、HandlerThreadです.(類似のパッケージはマルチスレッドのシーンに対してAsyncTaskもある)
使用方法
まずHandlerThreadの使い方を見てみましょう.まずHandlerThreadを新規作成しstart()を実行します.

private HandlerThread mHandlerThread;
......
mHandlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

Handlerを作成し、mHandlerThreadを使用します.getLooper()生成Looper:

 final Handler handler = new Handler(mHandlerThread.getLooper()){
   @Override
   public void handleMessage(Message msg) {
    System.out.println("    ");
   }
  };

次に、メッセージを送信するサブスレッドを新規作成します.

 new Thread(new Runnable() {
   @Override
   public void run() {
    try {
     Thread.sleep(1000);//      
     handler.sendEmptyMessage(0);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }).start();

最後に、メモリの漏洩を避けるためにonDestroyで解放することを忘れないでください.

 @Override
 protected void onDestroy() {
  super.onDestroy();
  mHandlerThread.quit();
 }

実行結果は簡単です.コンソールで文字列を印刷します.メッセージを受信します.
げんり
Handler関連のものに関心を持つ必要はありません.メッセージを送信したり、メッセージを処理したり、Looper関連のものを自分で処理したりする必要があります.ソースコードがどのように実現されているかを見てみましょう.まず、構造方法を見てみましょう.
public class HandlerThread extends Thread {}
HandlerThreadは実際にはスレッドですが、通常のスレッドと何が違いますか?

public class HandlerThread extends Thread {
 int mPriority;
 int mTid = -1;
 Looper mLooper;

 public HandlerThread(String name) {
  super(name);
  mPriority = Process.THREAD_PRIORITY_DEFAULT;
 }
 ......
}


答えは1つのLooperが追加されました.これはサブスレッド独自のLooperで、メッセージの取り出しと処理に使用されます.HandlerThreadというスレッドのrunメソッドを見てみましょう.

 protected void onLooperPrepared() {
 }
 @Override
 public void run() {
  mTid = Process.myTid();
  Looper.prepare();
  synchronized (this) {
   mLooper = Looper.myLooper();//  Looper
   notifyAll();
  }
  Process.setThreadPriority(mPriority);
  onLooperPrepared();//   , Looper       ,        
  Looper.loop();//   ,   MessageQueue         Handler  
  mTid = -1;
 }

主にいくつかのLooperの操作を行い、私たち自身がHandler+Threadを使って実現すれば、この操作も行い、getLooper()の方法を見てみましょう.

public Looper getLooper() {
  if (!isAlive()) {
   return null;
  }

  // If the thread has been started, wait until the looper has been created.
  synchronized (this) {
   while (isAlive() && mLooper == null) {
    try {
     wait();
    } catch (InterruptedException e) {
    }
   }
  }
  return mLooper;
 }


方法は簡単です.同期ロックを追加します.(isAlive()はtrueを返します)が作成されていますが、mLooperが空の場合は、mLooperの作成に成功するまで待ち続けます.最後にquitメソッドを見てみると、2つあります.

 public boolean quit() {
  Looper looper = getLooper();
  if (looper != null) {
   looper.quit();
   return true;
  }
  return false;
 }
 public boolean quitSafely() {
  Looper looper = getLooper();
  if (looper != null) {
   looper.quitSafely();
   return true;
  }
  return false;
 }

quitSafelyは,メッセージキューにメッセージがある場合や,送信遅延メッセージが処理されていない場合に,このメソッドを呼び出すと停止する.
まとめ
HandlerThreadの使用方法は簡単ですが、スレッドがメッセージを処理する場合は、Handlerがどこで作成したのではなく、どこでメッセージを処理できるのかを理解する必要があります.
HandlerThreadを使わない場合は、手動でLooperを呼び出す必要があります.prepare()とLooper.loop()これらの方法.
以上、Thread、HandlerとHandlerThreadの関係に関する資料を整理し、その後も関連資料を補充し続けます.