[Android] サービス、スレッドはどの場面でどれを使用すべきか

Android
スポンサーリンク

後で必要になるはずなので、自分用のまとめです。現在、各リンクを読み込みながらまとめ中です。

以下にサービス、スレッドの違いを、Android公式を引用しながら、まとめて行きます。元がC#の人なので、それに関する感想とともに書いてます。そのほうが自分が覚えやすい。

スポンサーリンク

サービスとは

C#(Windows)では、昔に一つだけサービスを作成したことがあるのですが、Androidのサービスは、Windowsのサービスよりもっとアプリケーションの中で動作する、スレッドに近い感じを受けました。でも、スレッドとも違う。

Service は、バックグラウンドで長時間動作して作業を行い、ユーザー インターフェースを表示しないアプリケーション コンポーネントです。 別のアプリケーション コンポーネントがサービスを開始し、ユーザーが他のアプリケーションに切り替えた場合でも、サービスはバックグラウンドで実行し続けることができます。(Android Developers>Docs>ガイド>サービスより)

たとえば、サービスはネットワーク トランザクションの処理、音楽の再生、ファイルの I/O の実行、コンテンツ プロバイダとのやり取りなどのすべてをバックグラウンドで行うことができます。(Android Developers>Docs>ガイド>サービスより)

プロセスの文脈の中で説明されているサービスの内容は以下の通りです。やはりWindows文化の人からするとスレッド的なイメージを拭えませんが、ここはAndroid文化の世界。慣れていかないといけません。

重要度の階層には 5 つのレベルがあります。…(1 つ目のプロセスが最も重要度が高く、最後に強制終了されます)

フォアグラウンドプロセス > 可視プロセス > サービスプロセス > バックグラウンドプロセス > 空のプロセス

サービスプロセスは重要度で3番目。

サービスプロセス
startService() メソッドで開始されたサービスを実行するプロセスで、フォアグラウンドプロセスと可視プロセスの2 つのカテゴリに分類されないものです。 サービスプロセスは、ユーザーに表示される内容には直接関係ありませんが、ユーザーにとって必要な操作を実行している場合が多いため(バックグラウンドで音楽を再生したり、ネットワーク経由でデータをダウンロードしたりなど)、フォアグラウンドプロセスと可視プロセスのすべてと合わせて、それらを継続するのにメモリが不足した場合のみ強制終了されます。(Android デベロッパー>Docs>ガイド>プロセスとスレッドより)

スレッドとは

Thread,Handler,Looper,HandlerThreadなど

C#(Windows)だと、スレッド内からもろにUIに触らないように、 BeginInvokeで触るようにしていた記憶があります。重い処理を行う際はスレッドで処理しないといけない。UIとの処理を分けたり、Windwosでもいろいろと工夫は必要でした。Androidでもそのようで、特に、CPUなどのリソースがWindowsより限られているので、より注意が必要な感じを受けます。(ここはプロセスもでしょうけど。)

下記のように、Androidのプロセスはシングルスレッドで、メインスレッドをUIスレッドといいます。

アプリケーション コンポーネントが開始し、アプリケーションに他に実行中のコンポーネントがない場合、Android システムは実行用のシングル スレッドを持つアプリケーション用の新しい Linux プロセスを開始します。 デフォルトでは、同じアプリケーションのすべてのコンポーネントは同じプロセスとスレッド(「メイン」 スレッドと呼ばれます)で実行します。

このスレッドは、イベント(描画イベントを含む)を適切なユーザー インターフェース ウィジェットに送信する役割を担うため非常に重要です。 また、これはアプリが Android UI ツールキットのコンポーネント(android.widget と android.view パッケージのコンポーネント)とやり取りをするスレッドでもあります。 そのため、メイン スレッドは UI スレッドと呼ばれることもあります。

ANR(Appication Not Responding)状態にならないように、正しくスレッドを実装する必要あり。

アプリがユーザー操作に応答して集中的な動作を実行する場合、アプリケーションを正しく実装していないと、このシングル スレッド モデルではパフォーマンスの低下につながる可能性があります。 具体的には、すべてが UI スレッドで行われている場合、ネットワークへのアクセスやデータベースへの問い合わせといった時間のかかる操作を実行すると UI 全体をブロックしてしまいます。 スレッドがブロックされると、描画イベントを含むすべてのイベントを送信できなくなります。 ユーザー側には、アプリケーションがハングしたように見えます。 さらには、UI スレッドが数秒以上(現時点では 5 秒以上)ブロックされると、ユーザーに「アプリケーションが応答していません」のダイアログが表示されます。 ユーザーはアプリケーションを停止するか、不快な場合はアンインストールしてしまう可能性があります。

Android UI ツールキットはスレッド セーフではありません。そのため、ワーカー スレッドから UI を操作できません。すべての操作は、UI スレッドから行う必要があります。 そのため、Android のシングル スレッド モデルには 2 つの明快なルールがあります。

1.UI スレッドをブロックしない
2.UI スレッド以外から Android UI ツールキットにアクセスしない

UIスレッド以外のスレッド(ワーカースレッド)から、UIにアクセスするにはお作法を守る必要あり。以下がそのお作法(Runable)を使うというもの。これでスレッドセーフになるとのこと。

Android には UI スレッド以外からのアクセス方法がいくつか用意されています。 使用できるメソッドは次のとおりです。

1.Activity.runOnUiThread(Runnable)
2.View.post(Runnable)
3.View.postDelayed(Runnable, long)

Runnable使用してもできるけど、あまりスレッドを立てすぎると、面倒になってくる。Handlerを使うとよい、最善なのは AsyncTask。

ただし、操作が複雑になるにつれて、この種のコードも複雑化してメンテナンスも難しくなります。 ワーカー スレッドとのより複雑なやり取りを処理するため、ワーカー スレッドで Handler を使うと、UI スレッドから配信されたメッセージを処理できます。 ただし、最善なのは AsyncTask クラスを拡張することであり、これにより UI を操作する必要のあるワーカー スレッドのタスクの実行を簡素化できます。

(以上の引用は、Android デベロッパー>Docs>ガイド>プロセスとスレッドより)

その他、補助的な説明として、以下のリンクの説明も参考になります。LooperとHandlerを簡単に扱えるようにしたHandlerThread。など。

他のスレッドからメインスレッドと通信するために、LooperとHandlerを使用することができます。AndroidはJavaのThreadをより簡単に扱えるようにラッピングしたHandlerThreadや、ThreadまたはMessage Loopなどの動作原理を熟知していなくても使えるAsyncTaskなどのクラスを提供します。
Androidのバックグラウンドを使いこなす Thread, Looper, Handler

いやぁ、怖い。Activity/Service等のライフサイクルについては正しく学んでおくべきだと思いました。AsyncTaskがいつでもキャンセルできるのに対しての、ワーカースレッドの注意点として、AsyncTask内の書き込みで書かれていましたが、ここに書いておきます。

警告: ワーカー スレッドの使用時に発生する可能性のあるもう 1 つの問題として、実行時の設定が変更された(ユーザーが画面の向きを変えた場合など)ことによってアクティビティが予期せず再起動され、ワーカー スレッドが破棄されてしまうことがあります。 このような再起動の間タスクを維持する方法、アクティビティが破棄されたときの正しいタスクのキャンセル方法については、Shelves のサンプル アプリケーションのソース コードをご覧ください。
(Android デベロッパー>Docs>ガイド>プロセスとスレッドより)

AsyncTask

スレッドより簡単に、非同期での処理を行いやすくしたもの。これが最善。これはこれで注意点はあるのでしょうが、これを使って行きたいと思います。結局、ライフサイクルの中で、スレッドは停止とか、キャンセルとかをうまく矢ならないとエラーを発生するので少し厄介ってことでしょうか。読んでると、C#のBackgroundWorker(古。。)を思い出した。今のフレームワークなら、async-taskを使うようにしてるけど。

  • AsyncTask では、ユーザー インターフェースに非同期の処理を実行できます。 スレッドやハンドラを自身で処理する必要なく、ワーカー スレッドの操作をブロックし、結果を UI スレッドに発行します。
  • ワーカー スレッドで処理される作業と、UI スレッドで処理される作業が分けられたため、UI は安全に、コードはシンプルになりました。
  • いつでも、どのスレッドからでもタスクをキャンセルできます

(Android デベロッパー>Docs>ガイド>プロセスとスレッドより)

じゃあ、使い分けは? 注意しながらまとめ

サービス(Service)

Serviceは画面がなくても走らせたいような処理や、後ろで常駐させたいような処理を行う際に使用。ただし2種類あり停止のさせ方には注意が必要(バインドタイプ)。またシステムから破棄されにくくするためには、Notificationを実装して、フォアグラウンドサービスとする必要あり。またサービスはContextをもっているため、Activityが終了してもContextを利用するような場合はService必須。UIスレッド上で動くため、重い処理をじかにするとアプリが落ちる。

Intent Serviceは、Serviceの中でも、順次にする処理が必要なものに対して用いると良。しかも実装が簡単になる。内部にHandlerスレッドを持っているので、Activityに依存せず非同期を行える。

Broadcast Recieverは、Intent Serviceに似ているが、常駐しない。暗黙インテントを処理するためにある。また処理の時間制限(5秒)あるため、ここで長い時間をかけるようなら、サービス化すること。

スレッド

以下の順にいろいろと簡単になるように工夫されている。

AsyncTask > HandlerThread(Handler,Looper) > Theard (Runable)

ただし、いろいろ読んでいると、5秒以上かかるような処理はサービス・インテントサービス内でThread(独自スレッド)を使用するのが良。またスレッドだと、Activityのライフサイクルで、画面が破棄されるとちゃんとキャンセルしないといけないので注意が必要。(Androdid公式の警告必読)

まとめ

Service, Intent Service, Broadcast Reciever, AsyncTask, LooperとHandler, HandlerThreadといろいろいろあったけど。

  • 後ろで常駐させたいとき → Service
  • 順次処理するような常駐処理 → Intent Service
  • 重い処理をしたいとき → Intent Service
  • 暗黙的インテントを処理するとき → Broadcast Reciever
  • 5秒以下の重い処理 → AsyncTask, HandlerThread (5秒はたぶん機種(CPU)によるので、サービス内でやるのが吉かも)
  • 5秒以上の重い処理 → Thread(サービス内)
  • 適用には注意をもって自己責任でお願いします
  • まだまだ実践が足らんので実践して勉強したら、加筆していきます。

参考リンクと書籍

Android Developers>Docs>ガイド>サービス

Android デベロッパー>Docs>ガイド>プロセスとスレッド

Android 非同期処理についてまとめてみた

Androidのバックグラウンドを使いこなす Thread, Looper, Handler

Qiita-Androidのライフサイクルからアプリ設計を見直してみる

Qiita-Android 非同期処理についてまとめる メモ

絶対落ちないアプリの作り方

ほんきで学ぶAndroidアプリ開発入門」(寺園 聖文さん著)のChapter05 全般

Qiita-Androidアプリ制作のためのリンク集(初心者用)

これから読んでみる参考リンク

Qiita-Androidの非同期処理の話

タイトルとURLをコピーしました