この記事では、Android Looper、Handler、およびHandlerThreadについて説明します。 これらは、Android OSのビルディングブロックの一つです。
私自身の経験では、私は最近まで非常に限られた文脈でそれらを使用してきました。 私のユースケースでは、主に他のスレッドからUIを更新するために、main/uiスレッドにタスクを送信することが含まれていました。 マルチスレッド操作の他の側面は、ThreadPoolExecutor、IntentService、AsyncTaskなどの別の方法で処理されました。
マルチスレッドとタスクの実行は古い科目です。 Java自体にはjavaがあります。ユーティルそれを容易にするための並行パッケージとFork/Joinフレームワーク。 いくつかのライブラリは、非同期操作を合理化するために書かれています。 RxJavaは、リアクティブプログラミングと非同期アプリケーションの設計のための今日の最も人気のあるラ
だから、なぜ私は古い学校について書いていますか?
Looper
, Handler
, そしてHandlerThread
は、非同期プログラミングの問題を解決するAndroidの方法です。 彼らは古い学校ではなく、複雑なandroidフレームワークが構築されているきちんとした構造です。
新しい開発者にとっては、その背後にある原則を理解し、経験豊富な人はこのトピックを再訪して小さな詳細を思い出すことを強くお勧めします。
私はまた、この主題のためのビデオチュートリアルを作成している、と私は非常にそれを見ることをお勧めします。 今見るにはここをクリック。
ユースケース:
- Androidのメインスレッドは
Looper
とHandlers
で構築されています。 そのため、ブロックされていない応答性の高いUIを作成することが不可欠です。 - ライブラリを書いている開発者は、ライブラリのサイズのために第三者のライブラリを使用する余裕がありません。 だから、彼らのために、最良の選択肢は、既存の利用可能なリソースを利用することです。 それのための独自のソリューションを書くことは、常にそのレベルの効率と最適化を得るとは限りません。
- Sdkを出荷する企業/個人に対しても同じ議論ができます。 クライアントは様々な実装を持つことができますが、それらのすべてが共通のandroidフレームワークApiを共有します。
- それらを完全に理解することで、一般的にAndroid SDKとパッケージクラスに従う能力が向上します。
アンケートで探査/改訂を始めましょう。
私は読者がjavaスレッドの基本的な理解を持っていることを期待しています。 必要に応じて、java ThreadとRunnableの概要を入手してください。
javaスレッドの問題は何ですか?
Javaスレッドは一度だけ使用され、runメソッドを実行した後に終了します。
改善できますか?
糸は諸刃の剣である。 実行のスレッド間でタスクを配布することで実行を高速化できますが、スレッドが過剰になったときにも実行を遅くすることができます。 スレッドの作成自体はオーバーヘッドです。 したがって、最良の選択肢は、最適な数のスレッドを持ち、タスクの実行に再利用することです。
糸の再利用性のためのモデル:
- スレッドは、
run()
メソッドを介してループ内で生きています。 - タスクはそのスレッドによって連続的に実行され、キュー(MessageQueue)に維持されます。
- 完了したらスレッドを終了する必要があります。
それを行うAndroidの方法は何ですか?上記のモデルは、Looper
、Handler
、HandlerThread
を介してAndroidに実装されています。 システムは記事のカバーのように車であるために視覚化することができる。
-
MessageQueue
処理する必要があるメッセージと呼ばれるタスクを持つキューです。 Handler
はLooper
を使用してMessageQueue
内のタスクをキューに入れ、タスクがMessageQueue
から出てくるときにもそれらを実行します。Looper
は、スレッドを生きたままにし、MessageQueue
をループし、対応するhandler
にメッセージを送信して処理するワーカーです。- 最後に
Thread
はLooperのquit()
メソッドを呼び出して終了します。
一つのスレッドは一つの一意のルーパーのみを持つことができ、それに関連付けられた多くの一意のハンドラを持つことができます。
スレッドのLooperとMessageQueueを作成する:
スレッドは、実行後にLooper.prepare()
を呼び出すことによってLooper
とMessageQueue
を取得します。 Looper.prepare()
は呼び出し元スレッドを識別し、Looper
およびMessageQueue
オブジェクトを作成し、thread
をThreadLocal
ストレージクラスで関連付けます。 関連するルーパーを開始するには、Looper.loop()
を呼び出す必要があります。 同様に、looper
はlooper.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 // this will run in non-ui/background thread } }; Looper.loop(); } }
スレッドのハンドラを作成する:
AHandler
は、スレッドのLooper
を介してインスタンス化するスレッドに暗黙的に関連付けられますが、Handler
のコンストラクタ
handler = new Handler() {@Overridepublic void handleMessage(Message msg) { // process incoming messages here // this will run in the thread, which instantiates it }};
Handler
を介してMessageQueue
にメッセージを送信するには、二つのモードで行うことができます:
-
Message
: これは、メッセージデータを処理するための様々な有用なメソッドを定義するクラスです。 オブジェクトを送信するには、obj変数を設定します。
Message msg = new Message();msg.obj = "Ali send message";handler.sendMessage(msg);
Message
クラスの詳細な概要はここにあります: https://developer.android.com/reference/android/os/Message.html
Runnable
: runnableはMessageQueue
に投稿することもできます。 例:メインスレッドでタスクを投稿して実行する。
new Handler(Looper.getMainLooper()).post(new Runnable() {@Overridepublic void run() { // this will run in the main thread }});
上記の例では、Handler
を作成し、メインスレッドに関連付けられたLooper
を提供します。 これは、このハンドラをメインスレッドに関連付けます。 Runnable
を投稿すると、メインスレッドのMessageQueue
にキューに入れられ、メインスレッドで実行されます。
ハンドラは、ここで見つけることができるさまざまな方法でメッセージ操作が可能です: https://developer.android.com/reference/android/os/Handler.html
独自のスレッドを作成し、Lopper
とMessageQueue
を提供することは、問題に対処する正しい方法ではありません。 そのため、Androidはプロセスを合理化するためにHandlerThread
(Thread
のサブクラス)を提供しています。 内部的には、私たちが行ったのと同じことを行いますが、堅牢な方法で行います。 そのため、常にHandlerThread
を使用してください。
HandlerThreadを作成する方法の一つは、それをサブクラス化することであり、ほとんどの場合、このメソッドを使用します。
private class MyHandlerThread extends HandlerThread { Handler handler; public MyHandlerThread(String name) { super(name); } @Override protected void onLooperPrepared() { handler = new Handler(getLooper()) { @Override public void handleMessage(Message msg) { // process incoming messages here // this will run in non-ui/background thread } }; }}
注:onLooperPrepared()
が呼び出されたときにハンドラをインスタンス化しました。 したがって、そのHandler
はそのLooper
に関連付けることができます。
-
Looper
HandlerThreadのstart()
が呼び出された後、つまりスレッドが実行された後にのみ準備されます。 - A
Handler
はHandlerThread
に関連付けることができますが、それはLooper
が準備された後でなければなりません。
HandlerThreadを作成する他の方法:
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");handlerThread.start();Handler handler = new Handler(handlerThread.getLooper());
メモ: HandlerThreadは、リソースを解放してスレッドの実行を停止するためにmyHandlerThread.quit()
を呼び出す必要があります。
私は上記のコードを練習することをお勧めしますので、あなたは彼らの小さな詳細を把握することができます。
郵便局シミュレーションのサンプルプロジェクトを作成しました。 郵便局はHandlerThreadに造られ、顧客は郵便局の助けによって伝達し合う。 Simulatorクラスは、少数のクライアントボットを作成し、その通信をMainActivityに委任します。
この例へのリンク
私はまた、この主題のためのビデオチュートリアルを作成している、と私は非常にそれを見ることをお勧めします。 今見るにはここをクリック。
また、Twitter、Linkedin、Github、Facebookで友達になりましょう。
学習は旅です、一緒に学びましょう!