1. ホーム
  2. Android

Androidスレッドの詳細

2022-02-17 23:35:56

ほとんどのモバイル端末はどんどん高速化していますが、実際にはそれほど速くはありません。ユーザーエクスペリエンスに影響を与えることなく、アプリで重い作業を処理したい場合は、タスクを並列に実行する必要があります。Androidでは、スレッドを使用します。

コーヒーでも飲みながら、この記事をじっくり読んでください。スレッドの概念と、Javaでの使い方、スレッドでのHandlerの使い方などを紹介します。

非同期処理や並列タスクが必要な場合は、必ずスレッドを使用することになります。

スレッドとは何ですか?

スレッドまたはスレッド実行は、基本的にコマンドの文字列(プログラムコードでもある)で、それをオペレーティングシステムに送信して実行させるものです。

一般に、私たちのCPUは、1つのコアにつき常に1つのスレッドしか処理することができません。マルチコア・プロセッサ(現在、ほとんどのAndroidデバイスはすでにマルチコアになっています)は、その名の通り、同時に複数のスレッドを処理することができます(平たく言えば、複数のことを同時に処理できるということです)。

マルチコア処理とシングルコアマルチタスクの本質について

上で述べたことは一般的なケースであり、必ずしもすべての記述が正しいとは限りません。なぜなら、シングルコアでもマルチタスクでマルチスレッドをエミュレートすることは可能だからです。

スレッドで実行される各タスクは複数の命令に分解でき、それらの命令は同時に実行される必要はないのです。つまり、シングルコアのデバイスでは、まずスレッド1に切り替えて命令1Aを実行し、次にスレッド2に切り替えて命令2Aを実行し、再びスレッド1に戻って1B、1C、1Dを実行し、続けてスレッド2に切り替えて2B、2Cを実行し、といった具合に、複数の命令を同時に実行することができるのです。

このスレッド間の切り替えは、シングルコアのデバイスでも起こるほど高速です。ほとんどすべてのスレッドが同じ時間でタスクをこなしているのです。実は、映画「マトリックス」のブラウン捜査官のように、たくさんの頭や手に変化することができるのは、すべて速すぎるがゆえの錯覚なのだ。

次に、いくつかのコードを見てみましょう。

Javaコアのスレッド

Javaでは、並列タスク処理を行う場合、Runnableの中でコードを実行することになります。Threadクラスを継承するか、Runnableインターフェイスを

// Version 1
public
class
 IAmAThread 
extends
Thread {
public
IAmAThread() {
super
(
"IAmAThread"
);
}
@Override
public
void
 run() {
// your code (sequence of instructions)
}
}
// to execute this sequence of instructions in a separate thread.
new
IAmAThread().start();
// Version 2
public
class
 IAmARunnable 
implements
Runnable {
@Override
public
void
 run() {
// your code (sequence of instructions)
}
}
// to execute this sequence of instructions in a separate thread.
IAmARunnable myRunnable = 
new
IAmARunnable();
new
Thread(myRunnable).start();

この2つのメソッドは基本的に同じです。最初のバージョンは Thread クラスを作成し、2番目のバージョンは Runnable オブジェクトを作成し、それを同様に呼び出す Thread クラスを必要とします。

2番目のバージョンは、通常推奨される方法です。これもまた、この記事の範囲を超えた大きなトピックであり、後ほど説明します。

Androidのスレッド

アプリを起動すると、すべてのコンポーネントは(デフォルトでは)メインスレッドと呼ばれる1つのスレッドで実行されます。このスレッドは主に、ビューコンポーネントやウィジェットなどのUI操作やイベント配信に使用されるため、メインスレッドのことをUIスレッドと呼んでいます。

UIスレッドで時間のかかる操作を実行すると、時間のかかる操作が終了するまでUIがロックされます。ユーザーエクスペリエンスにとって、これは非常に悪いことです。そのため、Androidのスレッド機構を理解することが重要です。これらのメカニズムを理解することで、複雑な作業を他のスレッドに移動して実行することができます。UIスレッドで時間のかかるタスクを実行すると、ANR(app not responding)が発生する可能性が高いので、ユーザーはすぐにアプリを終了してしまうでしょう。

Androidは、Javaと同様に、Java内部でThreadクラスを使用してワンステップのタスク処理をサポートしています。そのため、上記のJavaの例のようにAndroidでスレッドを使うことは簡単ですが、それでも少し難しいようです。

Androidで標準的なJavaのスレッドを使用するのはなぜ難しいのですか?

実は並列タスク処理は、皆さんが思っているほど簡単ではありません。複数のスレッドでの同時実行を保証する必要があり、偉大なTim Brayが言ったように、普通の人間は大規模な同時実行を行うことができません(というか全くできません).

特にAndroidの場合、以下の機能が若干肥大化しています。

  1. 非同期は、UIスレッドにとって大きなPITAです(バックグラウンドスレッドでメインスレッドのインターフェイスを更新する必要がある場合)。
  2. 画面の向きや画面構成が変わると、さらに奇妙なことが起こります。なぜなら、画面の向きを変えるとActivityが再構築されるからです(したがって、バックグラウンドスレッドは破壊されたActivityの状態を変更しに行く必要があり、バックグラウンドスレッドがUIスレッドの上にない場合は、条件1のような理由で、状況はさらに複雑になります)。
  3. スレッドプールに対するデフォルトの処理はありません。
  4. スレッド操作のキャンセルには、カスタムコードの実装が必要です。

<スパン では、Androidでタスク並行処理を行うにはどうすればよいのでしょうか?

Androidでよく使われる用語を聞いたことがあるかもしれません。

1. ハンドラ
これが今日お話しする詳細なトピックです。

2. AsyncTask
AsyncTaskを使用することは、Androidでスレッドを操作する最も簡単な方法であり、物事を間違えやすい方法でもあります。

3. IntentService
この方法は、書くべきコードが増えますが、時間のかかる作業をバックグラウンドに移すには良い方法であり、私のお気に入りの方法です。のようなEventBus機構を使用するフレームワークと組み合わせると、より効果的です。 オットー これにより、IntentServiceの実装は非常にシンプルになります。

4. ローダー
データベースやコンテンツプロバイダからのデータを処理するような非同期タスクの処理については、まだやることがたくさんあります。

5. サービス
もしあなたがサービスを使ったことがあるなら、ここでちょっとした誤解があることを知るべきです。よくある誤解の1つは、サービスがバックグラウンドスレッドで動作しているというものです。実は、そうではありません! UIコンポーネントと関連付けられていないためバックグラウンドで実行されているように見えますが、(デフォルトでは)UIスレッドで実行されます ...... デフォルトでは、上にUIウィジェットがなくても、UIスレッドで実行されるのです。

バックグラウンドスレッドでサービスを実行したい場合は、スレッドをカスタマイズして、そのスレッドですべてのアクションコードを実行する必要があります(上記のアプローチに非常に似ています)。実際にはIntentServiceの実装を使用すべきですが、それはこの記事のトピックではありません。

Androidでのハンドラ

から、Handlerの概要を説明します。  ハンドラに関するAndroid開発者向けドキュメントです。 からの抜粋です。

> ハンドラは、スレッドの MessageQueue に関連付けられた Message と Runnable オブジェクトを送信し、処理することができます。新しいHandlerを作成すると、そのHandlerを作成しているスレッドのスレッド/メッセージキューにバインドされます - その時点から、そのメッセージキューにメッセージやrunnableを配信し、メッセージキューから出てきたときにそれらを実行します。

この概念をよりよく理解するために、メッセージ・キューとは何かについて見てみる必要があるかもしれません。

メッセージキュー

スレッドには基本的にメッセージキューと呼ばれるものが存在し、スレッド間通信を担っています。これは、すべての制御命令やコンテンツがスレッド間で受け渡しされるデザインパターンです。

メッセージキューは、その名の通り、スレッドに対する指示の待ち行列である。ここでは、さらにクールなものをいくつか紹介します。

  • メッセージやスレッドをある時点だけ実行するタイミングを計る。
  • このスレッドではなく、別のスレッドで受信アクションを追加しに行く必要がある。

注意 ここでの"message"の概念は、Runnableオブジェクトやコマンドキューと同じです。

AndroidのHandlerに話を戻すと......。よく読むと、ドキュメントによると

> Handler は、スレッドの MessageQueue に関連付けられた Message と Runnable オブジェクトを送信し、処理することができます。

つまり、Handlerを使えば、スレッドのキューにメッセージを送ることができるわけだ。

各ハンドラのインスタンスは、1つのスレッドとそのスレッドのメッセージキューに関連付けられます。

ハンドラオブジェクトは、1つのスレッドにのみ関連付けることができます。