Processes and Threads

Quickview

  • Every application runs in its own process and all components of application run in thatprocess, by default
  • Any slow, blocking operations in an activity should be done in new thread.All rights reserved, ユーザー インターフェイスが遅くならないようにするため

このドキュメントでは

  1. プロセス
    1. プロセス ライフサイクル
  2. スレッド
    1. ワーカスレッド
    2. Thread->
    3. TM

      TM安全なメソッド

  • プロセス間通信
  • アプリケーションコンポーネントが起動し、アプリケーションに他のコンポーネントが動作していない場合。 Androidシステムは、そのアプリケーションのために、シングルスレッドで新しいLinuxプロセスを開始します。 デフォルトでは、同じアプリケーションのすべてのコンポーネントは、同じプロセスおよびスレッド(「メイン」スレッドと呼ばれます)で実行されます。 アプリケーション・コンポーネントが起動し、そのアプリケーションのプロセスがすでに存在する場合(そのアプリケーションの別のコンポーネントが存在するため)、コンポーネントはそのプロセス内で起動され、同じスレッドを使用して実行されます。

    このドキュメントでは、Android アプリケーションでプロセスとスレッドがどのように機能するかについて説明します。

    各タイプのコンポーネント要素 (<activity><service><receiver>、および <provider>) のマニフェスト エントリは、コンポーネントを実行するプロセスを指定できる android:process 属性をサポートします。 この属性は、各コンポーネントが独自のプロセスで実行されるようにしたり、あるコンポーネントはプロセスを共有し、他のコンポーネントは共有しないように設定することが可能です。 また、アプリケーションが同じ Linux ユーザー ID を共有し、同じ証明書で署名されている場合、android:process を設定して、異なるアプリケーションのコンポーネントを同じプロセスで実行することもできます。

    また、<application>要素は、すべてのコンポーネントに適用するデフォルト値を設定する android:process 属性をサポートしています。 殺されたプロセスで実行されているアプリケーションコンポーネントは、結果的に破壊されます。

    どのプロセスを殺すかを決定するとき、Android システムは、ユーザーに対するそれらの相対的な重要性を秤にかけます。 たとえば、画面に表示されないアクティビティをホストするプロセスは、表示されるアクティビティをホストするプロセスと比較して、より容易にシャットダウンされます。 つまり、あるプロセスを終了させるかどうかは、そのプロセスで動作しているコンポーネントの状態に依存するのです。 どのプロセスを終了させるかを決定するために使用されるルールについては、後述します。

    プロセス ライフサイクル

    Androidシステムは、できるだけ長くアプリケーション プロセスを維持しようとしますが、最終的には、新しいプロセスやより重要なプロセスのためにメモリを確保するために古いプロセスを削除する必要があります。 どのプロセスを維持し、どのプロセスを破棄するかを決定するために、システムは、プロセスで実行されているコンポーネントとそれらのコンポーネントの状態に基づいて、各プロセスを「重要性階層」に配置します。 重要性の最も低いプロセスが最初に削除され、次に重要性の最も低いプロセスが削除されるというように、システム リソースを回復するために必要な処理が行われます。 次のリストでは、さまざまな種類のプロセスを重要度の順に示します (最初のプロセスが最も重要であり、最後に削除されます)。

    1. フォアグラウンドプロセス

      ユーザーが現在行っている処理に必要なプロセスです。

      • ユーザーが操作しているActivityをホストしている(ActivityonResume()メソッドが呼び出されている)場合、プロセスはフォアグラウンドであるとみなされる。
      • ユーザーが操作しているアクティビティにバインドされているServiceをホストしています。
      • 「フォアグラウンドで」実行されているServiceをホストしており、そのサービスはstartForeground()を呼び出しています。
      • ライフサイクル・コールバック (onCreate()onStart()onDestroy()) のいずれかを実行している Service をホストしています。
      • その onReceive() メソッドを実行している BroadcastReceiver をホストしています。 それらは、最後の手段として、メモリが非常に少なくなり、すべてを実行し続けることができなくなった場合にのみ強制終了されます。
      • 可視プロセス

        前面のコンポーネントを持たないが、ユーザーが画面上で見るものに影響を与えることができるプロセス。

        • 前景にない Activity をホストしているが、ユーザーからはまだ見えている (onPause() メソッドが呼び出されている) 場合、プロセスは可視とみなされる。 これは、たとえば、フォアグラウンドのアクティビティがダイアログを開始し、その背後にある前のアクティビティを見ることができる場合に発生します。

        表示可能なプロセスは非常に重要と見なされ、すべてのフォアグラウンド プロセスの実行を維持するために必要でない限り終了しません。

      • サービス・プロセス

        startService()メソッドで開始されたサービスを実行しているプロセスで、上位2つのカテゴリには該当しない。 サービス・プロセスはユーザーが見るものと直接結びついているわけではないが、一般にユーザーが気にすること(バックグラウンドでの音楽再生やネットワーク上のデータのダウンロードなど)を行っているため、システムは十分なメモリがない限り、フォアグラウンドおよび可視プロセスすべてと一緒にそれらを保持するために実行を継続する。

      • バックグラウンド・プロセス

        現在ユーザーから見えないアクティビティを保持しているプロセス (アクティビティのonStop()メソッドが呼び出されている)。 これらのプロセスはユーザー・エクスペリエンスに直接影響を与えることはなく、システムはこれらのプロセスをいつでも停止して、前景、可視、またはサービス・プロセスのためにメモリを再利用することができます。 通常、多くのバックグラウンド・プロセスが実行されているので、ユーザーが最も最近見たアクティビティを持つプロセスが最後に殺されることを保証するために、それらはLRU(least recently used)リストに保持されます。 アクティビティがそのライフサイクルメソッドを正しく実装し、その現在の状態を保存する場合、そのプロセスを終了しても、ユーザーがアクティビティにナビゲートして戻ったときに、アクティビティがその可視状態のすべてを復元するため、ユーザー体験に目に見える影響を与えることはありません。 状態の保存と復元に関する情報については、アクティビティに関するドキュメントを参照してください。

      • Empty process

        アクティブなアプリケーション コンポーネントを保持しないプロセス。 この種のプロセスを存続させる唯一の理由は、キャッシュ目的で、次にその中でコンポーネントを実行する必要があるときの起動時間を改善するためです。

    Android は、プロセスで現在アクティブなコンポーネントの重要性に基づいて、それができる最も高いレベルでプロセスをランク付けします。 たとえば、プロセスがサービスと可視アクティビティをホストする場合、プロセスはサービスプロセスではなく、可視プロセスとしてランク付けされます。

    さらに、他のプロセスがそれに依存しているため、プロセスのランクが上がることがあります。 たとえば、プロセス A のコンテンツ プロバイダーがプロセス B のクライアントにサービスを提供している場合、あるいは、プロセス A のサービスがプロセス B のコンポーネントにバインドされている場合、プロセス A は常にプロセス B と同じくらい重要であると見なされます。たとえば、Webサイトに画像をアップロードしているアクティビティは、ユーザーがアクティビティを離れてもアップロードをバックグラウンドで継続できるように、アップロードを実行するサービスを開始するべきです。サービスを使用すると、アクティビティに何が起こっても、操作が少なくとも「サービスプロセス」優先権を持つことが保証されます。 これは、ブロードキャスト受信者が、時間のかかる操作を単にスレッドに入れるのではなく、サービスを採用すべき理由と同じです。

    Threads

    アプリケーションが起動すると、システムはアプリケーションの実行スレッドを作成し、「メイン」と呼びます。 このスレッドは、描画イベントを含む適切なユーザーインターフェイスウィジェットにイベントをディスパッチすることを担当するので、非常に重要である。 また、アプリケーションが Android UI ツールキットのコンポーネント (android.widget および android.view パッケージのコンポーネント) と相互作用するスレッドでもあります。 そのため、メイン スレッドは UI スレッドと呼ばれることもあります。

    システムは、コンポーネントのインスタンスごとに個別のスレッドを作成するわけではありません。 同じプロセスで実行されるすべてのコンポーネントは UI スレッドでインスタンス化され、各コンポーネントへのシステムコールはそのスレッドからディスパッチされます。 その結果、システム コールバックに応答するメソッド (ユーザー アクションやライフサイクル コールバック メソッドを報告する onKeyDown() など) は、常にプロセスの UI スレッドで実行されます。

    たとえば、ユーザーが画面上のボタンに触れると、アプリの UI スレッドはタッチ イベントをウィジェットにディスパッチし、ウィジェットはその押した状態を設定してイベント キューに無効化リクエストをポストします。 UI スレッドは要求を待ち行列から外し、ウィジェットに自分自身を再描画するよう通知します。

    アプリケーションがユーザーの操作に応答して集中的に作業を行う場合、アプリケーションを適切に実装しないと、この単一スレッド モデルが悪いパフォーマンスを引き起こすことがあります。 具体的には、すべてが UI スレッドで起こっている場合、ネットワーク アクセスやデータベース クエリなどの長い操作を実行すると、UI 全体でブロックされます。 スレッドがブロックされると、描画イベントを含むすべてのイベントがディスパッチされなくなります。 ユーザーの視点からは、アプリケーションがハングアップしているように見えます。 さらに悪いことに、UIスレッドが数秒(現在約5秒)以上ブロックされると、ユーザーに悪名高い「アプリケーションは応答していません」(ANR)ダイアログが表示されます。

    さらに、Andoid UI ツールキットはスレッドセーフではありません。 したがって、ワーカー スレッドから UI を操作してはなりません – UI スレッドからユーザー インターフェイスへのすべての操作を行う必要があります。 UI スレッドをブロックしない

  • UI スレッドの外側から Android UI ツールキットにアクセスしない
  • Worker threads

    上記のシングルスレッド モデルのため、アプリケーションの UI の応答性には、UI スレッドをブロックしないことが不可欠となります。

    たとえば、以下は、分離したスレッドから画像をダウンロードし、それを ImageView:

    public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start();}

    ネットワーク操作を処理するために新しいスレッドを作成するので、最初は、これはうまくいくように思われます。 しかし、シングルスレッド モデルの 2 番目のルールである「UI スレッドの外から Android UI ツールキットにアクセスしない」に違反しています。このサンプルは、UI スレッドではなくワーカスレッドから ImageView を変更します。 この問題を解決するために、Android では、他のスレッドから UI スレッドにアクセスする方法をいくつか提供しています。 以下は、役立つメソッドのリストです。

    • Activity.runOnUiThread(Runnable)
    • View.post(Runnable)
    • View.postDelayed(Runnable,long)

    たとえば、View.post(Runnable)メソッドを使用すると、上記のコードを修正することができます。

    public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start();}

    さて、この実装はスレッドセーフです。ネットワーク操作は別のスレッドから行われ、ImageView は UI スレッドから操作されます。

    しかし、操作が複雑になると、この種のコードは複雑になり、保守が困難になります。 ワーカー スレッドとのより複雑な相互作用を処理するには、UI スレッドから配信されたメッセージを処理するために、ワーカー スレッドで Handler を使用することを考慮するとよいでしょう。 しかし、おそらく最良の解決策は、UI と対話する必要があるワーカー スレッド タスクの実行を単純化する AsyncTask クラスを拡張することです。

    Using AsyncTask

    AsyncTask によって、ユーザー インターフェイスで非同期の仕事を実行することができます。 これを使用するには、AsyncTask をサブクラス化し、バックグラウンド スレッドのプールで実行される doInBackground() コールバック メソッドを実装する必要があります。 UI を更新するには、onPostExecute() を実装する必要があります。これは、doInBackground() からの結果を配信し、UI スレッドで実行されるので、安全に UI を更新することができます。

    たとえば、AsyncTask を使用して前の例をこのように実装できます。

    public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png");}private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); }}

    これで UI は安全になり、コードはよりシンプルになります。

    このクラスの使用方法について完全に理解するには、AsyncTask リファレンスを読む必要がありますが、ここでは、それがどのように動作するかの概要を簡単に説明します。

    • ジェネリックスを使用して、パラメータの型、進捗値、タスクの最終値を指定できる
    • メソッド doInBackground() はワーカスレッドで自動的に実行される
    • onPreExecute()onPostExecute(), とonProgressUpdate()はすべてUIスレッド上で起動される
    • doInBackground()が返す値はonPostExecute()に送られる
    • doInBackground()内でいつでもpublishProgress()を呼び出し、UIスレッドのonProgressUpdate()を実行できる
    • タスクをいつでも、どのスレッドからもキャンセルできる

    注意事項。 ワーカースレッドを使用する際に遭遇する可能性のあるもう 1 つの問題は、ランタイム構成の変更(ユーザーが画面の向きを変更した場合など)によりアクティビティが予期せず再起動し、ワーカースレッドが破壊される可能性があることです。 これらの再起動中にタスクを持続させる方法と、アクティビティが破棄されたときにタスクを適切にキャンセルする方法については、Shelves サンプル アプリケーションのソース コードを参照してください。

    これは主にリモートで呼び出されるメソッド (バインドされたサービス内のメソッドなど) に当てはまります。 しかし、呼び出しが別のプロセスで発生した場合、メソッドは IBinder と同じプロセスでシステムが保持するスレッドのプールから選択されたスレッドで実行されます (プロセスの UI スレッドでは実行されません)。 例えば、サービスのonBind()メソッドがサービスプロセスのUIスレッドから呼び出されるのに対し、onBind()が返すオブジェクトに実装されたメソッド(例えば、RPCメソッドを実装したサブクラス)は、プールのスレッドから呼び出されることになります。 サービスには複数のクライアントが存在するため、複数のプールスレッドが同時に同じ IBinder メソッドにアクセスすることができます。 したがって、IBinder メソッドはスレッドセーフに実装されなければなりません。

    同様に、コンテンツ プロバイダーは他のプロセスから発生するデータ要求を受け取ることができます。ContentResolverContentProvider クラスはプロセス間通信がどのように管理されるかの詳細を隠していますが、 これらの要求に応答する ContentProvider メソッド (メソッド query()insert()delete()update() および getType()) はプロセスの UIthread でなくコンテンツプロバイダーのプロセス内のスレッドのプールから呼ばれるようになっています。 これらのメソッドは同時に任意の数のスレッドから呼び出される可能性があるため、これらもスレッドセーフに実装する必要があります。

    Interprocess Communication

    Android は、リモート プロシージャ コール (RPC) を使用したプロセス間通信 (IPC) のメカニズムを提供します。これは、アクティビティまたは他のアプリケーション コンポーネントによってメソッドが呼び出されますが、リモートで (別のプロセスで) 実行されて、結果が呼び出し元に戻されるものです。 これには、メソッド呼び出しとそのデータをオペレーティングシステムが理解できるレベルまで分解し、ローカルプロセスとアドレス空間からリモートプロセスとアドレス空間へ転送し、そこで呼び出しを組み立て直して再実行することが必要です。 戻り値は、その逆方向で転送されます。 Androidは、これらのIPCトランザクションを実行するためのすべてのコードを提供するので、RPCプログラミングインターフェイスの定義と実装に集中することができます。

    IPC を実行するには、bindService() を使用して、アプリケーションをサービスにバインドする必要があります。 詳細については、「サービス」開発者ガイドを参照してください。

    コメントする