Android UIスレッド

Androidアプリケーションコードのほとんどは、Activity、Service、ContentProvider、BroadcastReceiverなどのコンポーネントのコンテキストで実行されます。 これらのコンポーネントとスレッドの相互作用がAndroidシステムでどのように編成されるかを検討してください。



アプリケーションが起動すると、システムはいくつかの操作を実行します。アプリケーションパッケージの名前と一致する名前でOSプロセスを作成し、作成されたプロセスに一意のユーザー識別子を割り当てます。これは基本的にLinux OSのユーザー名です。 システムは、メインアプリケーションスレッド(「UIスレッド」とも呼ばれます)が作成されるDalvik VMを起動します。 このスレッドでは、Androidアプリケーションの4つのコンポーネント(Activity、Service、ContentProvider、BroadcastReceiver)がすべて実行されます。 ユーザーインターフェイススレッドでのコード実行は、「イベントループ」とメッセージキューによって編成されます。



Androidシステムとアプリケーションコンポーネントの相互作用を考慮してください。



アクティビティ ユーザーがメニュー項目を選択するか、画面上のボタンを押すと、システムはこのアクションをメッセージとして指定し、UIスレッドキューに配置します。



サービス 名前に基づいて、多くの人は、サービス(Service)が別のスレッド(Thread)で動作すると誤って信じています。 実際、サービスはUIスレッドのアクティビティと同じように機能します。 startServiceコマンドでローカルサービスを開始すると、新しいメッセージがメインスレッドキューに配置され、サービスコードが実行されます。



BroadcastReceiver ブロードキャストメッセージを作成すると、システムはそれをメインアプリケーションスレッドのキューに入れます。 メインスレッドは、後でこのタイプのメッセージに登録されているBroadcastReceiverコードをダウンロードし、実行を開始します。



ContentProvider ローカルContentProviderの呼び出しはわずかに異なります。 ContentProviderもメインスレッドで実行されますが、その呼び出しは同期であり、ContentProviderはコードの実行にメッセージキューを使用しません。



上記に基づいて、メインスレッドが現在ユーザー入力を処理中または別のアクションを実行している場合、新しいメッセージで受信したコードの実行は、現在の操作が完了した後にのみ開始されることに注意できます。 コンポーネントの1つでの操作にかなりの実行時間が必要な場合、ユーザーは、動きが急なアニメーション、応答しないインターフェイス要素、またはシステムからのメッセージ「アプリケーションが応答しません」(ANR)に遭遇します。



この問題を解決するために、並列プログラミングパラダイムが使用されます。 Javaでは、その実装は実行のスレッド(スレッド)の概念を使用します。



スレッド :スレッド、実行スレッド(スレッドとも呼ばれる)は、独立した一連の命令が実行される個別のタスクと見なすことができます。 システムにプロセッサが1つしかない場合、スレッドは交互に実行されます(ただし、それらの間でシステムをすばやく切り替えると、並列または同時操作の印象が生じます)。 この図は、3つの実行スレッドを持つアプリケーションを示しています。







しかし、残念なことに、ユーザーとの対話には、ストリームはほとんど役に立ちません。 実際、上の図を注意深く見ると、スレッドがそれに含まれるすべての命令を実行するとすぐに、ユーザーアクションの追跡を停止および停止することがわかります。 これを回避するには、命令セットに無限ループを実装する必要があります。 しかし、別のストリームから画面に何かを表示するなど、何らかのアクションを実行する方法、つまり、無限ループに侵入する方法の問題が発生します。 これには、AndroidでAndroid Message Systemを使用できます。 次の部分で構成されています。



ルーパー :「イベントループ」とも呼ばれることがあり、タスクを受信できる無限ループを実装するために使用されます。 Looperクラスを使用すると、繰り返しアクションを処理するためのスレッドを準備できます。 このようなスレッドは、下図に示すように、しばしばルーパースレッドと呼ばれます。 Androidのメインスレッドは、実際にはルーパースレッドです。 ルーパーはスレッドごとに一意であり、 TLSまたはスレッドローカルストレージのデザインパターンとして実装されます (好奇心urious盛な人はJavaドキュメントまたはAndroidのThreadLocalクラスを参照できます)。







メッセージ :メッセージは、別のスレッドで実行される一連の命令のコンテナです。



ハンドラー :このクラスは、ルーパースレッドとの相互運用性を提供します。 実装されたRunnableを含むメッセージをLooperに送信できるのは、ハンドラーの助けを借りて、ハンドラーが接続されているスレッドによって(即時または指定された時間に)実行されるからです。 以下のコードは、ハンドラーの使用方法を示しています。 このコードは、一定期間後に完了するアクティビティを作成します。



public class LaunchActivity extends Activity { // time to display the splash screen in ms private final static long SPLASH_DELAY = 4000 * 0; private final Handler handler = new Handler(); private final Runnable splashTask = new Runnable() { @Override public void run() { finish(); } }; @Override protected void onResume() { super.onResume(); handler.removeCallbacks(splashTask); handler.postDelayed(splashTask, SPLASH_DELAY); } @Override protected void onPause() { handler.removeCallbacks(splashTask); super.onPause(); } }
      
      







HandlerThread :Looperを実装するスレッドコードを記述するのは、AndroidシステムにHandlerThreadクラスが含まれる同じ間違いを繰り返さないために、簡単な作業ではない場合があります。 名前に反して、このクラスはハンドラーとルーパーを処理しません。



このアプローチの実際の実装は、 IntentServiceクラスコードを例として使用して調べることができます。このクラスは、現在のプロセスが完了するのを待たずにタスクを1つずつ受信でき、独自に作業を完了するため、非同期ネットワークまたは他の要求の実行に適しています。



別のスレッドで操作を実行しても、システムのパフォーマンスに影響を与えることなく何でもできるというわけではありません。 あなたが書いたコードが、通常あまり強力ではないマシンで動作することを決して忘れないでください。 したがって、システムが提供する最適化の機会を常に使用する必要があります。



AndroidDevBlogに基づく



All Articles