マルチスレッド / コールバック入門

Kotlin を使った Android アプリの開発コースでは、マルチスレッドの概念と用語を理解していることを前提としています。このページは、以前の学習内容を簡単に説明し、参照するためのものです。

モバイル デバイスにはプロセッサがあり、最近は、ほとんどのデバイスが複数のハードウェア プロセッサを備え、それぞれが同時にプロセスを実行するようになっています。これをマルチプロセスといいます。

プロセッサをより効率的に使用するために、オペレーティング システムは、アプリに 1 つのプロセス内で複数の実行スレッドを作成させることができます。これをマルチスレッドといいます。

画像

これは、複数の章を同時に読んだり、ある章を読んだ後に別の書籍に切り替えたりしながら書籍をすべて読み終わることができても、同時に複数の書籍を読むことはできないと考えることができます。

このようにスレッドをすべて管理するには、インフラストラクチャがいくつか必要です。

画像

スケジューラは、優先度などの問題を考慮し、すべてのスレッドを実行して完了することができるようにします。書籍が棚にいつまでも置かれてほこりを集めることはありません。ただし、書籍の内容が非常に長い場合、または後で読むことができる場合には、時間が経ってから送られてくることもあり得ます。

ディスパッチャはスレッドを設定します。つまり、読む必要がある書籍を送信して、処理を行うコンテキストを指定します。コンテキストは、独立した専用の読書ルームと考えることができます。ユーザー インターフェース操作に最適なコンテキストもあれば、入出力操作の処理に特化したコンテキストもあります。

もう 1 つの注意点は、ユーザー向けのアプリには通常、フォアグラウンドで実行されるメインスレッドがあり、メインスレッドは、バックグラウンドで実行される他のスレッドをディスパッチできることです。

Android では、メインスレッドは UI の更新をすべて処理する単一のスレッドです。このメインスレッド(UI スレッドとも呼ばれます)は、すべてのクリック ハンドラとその他の UI とライフサイクル コールバックを呼び出すスレッドでもあります。UI スレッドがデフォルトのスレッドです。アプリが明示的にスレッドを切り替えたり、別のスレッドで実行されるクラスを使用したりしない限り、アプリのすべての操作はメインスレッドで実行されます。

これにより、潜在的な課題が発生します。スムーズなユーザー エクスペリエンスを確保するには、UI スレッドをスムーズに実行する必要があります。ユーザーが視認できるような途切れがなくアプリを表示させるには、メインスレッドは 16 ミリ秒以上ごとに、または 1 秒あたり約 60 フレームで画面を更新する必要があります。この速度では、人間はフレームの変化を滑らかに感じます。この効果を実現するには、多数のフレームを短時間で処理する必要があります。 したがって、Android では UI スレッドのブロックを回避することが重要です。この場合、ブロックとは、データベースの更新などが完了するまで待機している間に、UI スレッドが何も実行していないことを意味します。

画像

インターネットからのデータの取得、サイズの大きいファイルの読み取り、データベースへのデータの書き込みなど、多くの一般的なタスクには 16 ミリ秒を超える時間がかかります。したがって、メインスレッドからこのようなタスクを実行するためにコードを呼び出すと、アプリの一時停止、途切れ、またはフリーズが発生する可能性があります。メインスレッドを長時間ブロックすると、アプリがクラッシュして、「アプリケーション応答なし」(ANR)のダイアログが表示される場合もあります。

コールバック

メインスレッドの外部から作業を完了するには、いくつかの方法があります。

メインスレッドをブロックせずに長時間実行タスクを実行するためのパターンの 1 つがコールバックです。コールバックを使用すると、長時間実行されるタスクをバックグラウンド スレッドで開始できます。タスクが完了すると、コールバック(引数として提供される)が呼び出され、メインスレッドで結果のコードを通知します。

コールバックは優れたパターンですが、デメリットもあります。コールバックを多用するコードは読み取りと推測が困難になる可能性があります。このコードは順次読み取れるようになっていますが、コールバック コードは将来的には非同期で実行されるようになります。また、コールバックでは、一部の言語機能(例外など)を使用できません。

コルーチン

Kotlin では、コルーチンは、長時間実行タスクをスムーズかつ効率的に処理するためのソリューションです。Kotlin コルーチンを使用すると、コールバック ベースのコードを順次コードに変換できます。通常、コードを順番に記述すると読みやすくなり、例外などの言語機能を使用できるようになります。最終的に、コルーチンとコールバックでも同様に同じタスクが実行されます。長時間実行タスクが結果を取得するのを待ってから、タスクの実行を続行します。