サービス概要

Serviceは、バックグラウンドで長時間実行することができるアプリケーション・コンポーネントです。 ユーザーインターフェースは提供しない。 そのため、ユーザーが他のアプリケーションに切り替えた後でも、サービスはしばらくの間実行され続けることがあります。 さらに、コンポーネントはサービスにバインドして、サービスと対話したり、プロセス間通信(IPC)を実行したりすることができます。 たとえば、サービスはネットワーク トランザクションの処理、音楽の再生、ファイル I/O の実行、コンテンツ プロバイダーとのやり取りをすべてバックグラウンドで行うことができます。 サービスは独自のスレッドを作成せず、特に指定しない限り、別のプロセスで実行されません。 ApplicationNot Responding (ANR) エラーを回避するため、サービス内の別のスレッドでブロック操作を実行する必要があります。

サービスの種類

これら 3 種類のサービスを紹介します:

フォアグラウンド

フォアグラウンド サービスは、ユーザーにとって目に付く何らかの処理を実行します。 たとえば、オーディオ アプリは、オーディオ トラックを再生するためにフォアグラウンド サービスを使用します。 フォアグラウンド サービスは、通知を表示する必要があります。

フォアグラウンド サービスを使用する場合、サービスが実行されていることをユーザーが積極的に認識できるように、通知を表示する必要があります。

アプリでフォアグラウンド サービスを構成する方法の詳細については、こちらを参照してください。 多くの場合、WorkManagerの使用はフォアグラウンド・サービスを直接使用するよりも望ましいです。

バックグラウンド バックグラウンドサービスは、ユーザーには直接気づかれない操作を実行する。 たとえば、アプリケーションがストレージをコンパクトにするサービスを使用する場合、これは通常バックグラウンド サービスになります。

注意: アプリが API レベル 26 以上をターゲットにしている場合、アプリ自体がフォアグラウンドにないときにバックグラウンド サービスを実行することはシステムによって制限されています。 たとえば、ほとんどの状況で、バックグラウンドから位置情報にアクセスするべきではありません。 代わりに、WorkManagerを使用してタスクをスケジュールします。

バインド アプリケーションコンポーネントがbindService()を呼び出してサービスにバインドすると、サービスがバインドされます。 バインドされたサービスは、コンポーネントがサービスと対話し、リクエストを送信し、結果を受信し、さらにプロセス間通信 (IPC) を使用してプロセス間で実行できるクライアント/サーバー インターフェイスを提供します。 バインドされたサービスは、他のアプリケーションコンポーネントがバインドされている間だけ実行されます。 複数のコンポーネントが同時にサービスにバインドすることもできますが、すべてのコンポーネントがバインドを解除すると、サービスは破棄されます。

このドキュメントでは一般に、開始されたサービスとバインドされたサービスを別々に説明しますが、サービスは両方の方法で動作させることができます。 それは単に、いくつかのコールバック メソッドを実装するかどうかの問題です。 onStartCommand() はコンポーネントに開始を許可し、onBind() はバインドを許可します。

サービスが開始、バインド、またはその両方に関係なく、どのコンポーネントも Intent で開始することによりアクティビティを使用できるのと同じ方法でサービスを使用できます(別のアプリケーションからでも可能です)。 これについては、マニフェストでのサービスの宣言のセクションで詳しく説明します。

サービスとスレッドの選択

サービスは、ユーザーがアプリケーションと通信していない場合でもバックグラウンドで実行できる単なるコンポーネントなので、それが必要な場合にのみサービスを作成する必要があります。

メイン スレッドの外側で、ユーザーがアプリケーションとやり取りしている間だけ作業を行う必要がある場合、代わりに別のアプリケーション コンポーネントのコンテキストで新しいスレッドを作成する必要があります。 また、従来のThreadクラスの代わりに、java.util.concurrentパッケージやKotlinコルーチンからスレッドプールやエグゼキュータを使用することを検討してください。

サービスを使用する場合、デフォルトではアプリケーションのメイン スレッドで実行されるので、集中的な操作やブロック操作を行う場合は、サービス内に新しいスレッドを作成する必要があることを覚えておいてください。 実装では、サービスのライフサイクルの重要な側面を処理するいくつかのコールバック メソッドをオーバーライドし、必要に応じてコンポーネントをサービスにバインドできるようにするメカニズムを提供する必要があります。 このメソッドが実行されると、サービスが開始され、バックグラウンドで無期限に実行できるようになります。 このメソッドを実装した場合、作業が完了したらstopSelf()またはstopService()を呼び出してサービスを停止させるのはあなたの責任です。 バインディングを提供したいだけであれば、このメソッドを実装する必要はありません。 onBind() システムは、他のコンポーネントがサービスとバインドしたい場合(RPCの実行など)、bindService()を呼び出してこのメソッドを起動します。このメソッドの実装では、IBinderを返すことによって、クライアントがサービスと通信するために使用するインターフェイスを提供する必要があります。 このメソッドは常に実装する必要がありますが、バインドを許可しない場合はnullを返す必要があります。 onCreate() システムがこのメソッドを呼び出して、サービスが最初に作成されたとき(onStartCommand()またはonBind()を呼び出す前)に1回限りの設定手順を実行します。 サービスがすでに実行されている場合、このメソッドは呼び出されません。 onDestroy() システムがこのメソッドを呼び出すのは、サービスがもはや使用されず、破棄されるときです。あなたのサービスは、スレッド、登録リスナー、またはレシーバーなどのリソースをクリーンアップするために、これを実装する必要があります。 これは、サービスが受ける最後の呼び出しです。

コンポーネントが startService() を呼び出してサービスを開始すると (その結果 onStartCommand() が呼び出される)、サービスは stopSelf() で停止するか、他のコンポーネントが stopService() を呼び出して停止するまで実行しつづけます。

Android システムでは、メモリが少なくなり、ユーザーフォーカスを持つアクティビティのシステムリソースを回復しなければならない場合にのみ、サービスを停止します。 サービスが開始され、長時間実行される場合、システムは時間の経過とともにバックグラウンド タスクのリスト内のその位置を下げ、サービスは非常に殺されやすくなります – サービスを開始する場合、システムによる再起動を優雅に処理するように設計する必要があります。 システムがサービスを停止させた場合、リソースが利用可能になるとすぐにサービスを再開しますが、これは onStartCommand() から返される値にも依存します。 システムがサービスを破棄する場合の詳細については、「プロセスとスレッド」のドキュメントを参照してください。

以下のセクションでは、startService() および bindService() サービス・メソッドを作成する方法、および他のアプリケーション・コンポーネントからそれらを使用する方法について説明します。

マニフェストでのサービスの宣言

アクティビティや他のコンポーネントに対して行うのと同様に、アプリケーションのマニフェスト ファイルですべてのサービスを宣言する必要があります。 以下はその例です。

<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application></manifest>

マニフェストでのサービスの宣言の詳細については、<service> 要素のリファレンスを参照してください。

サービスの起動に必要な権限やサービスを実行するプロセスなどのプロパティを定義するために <service> 要素に含めることができる他の属性もあります。 android:name 属性は唯一の必須属性で、サービスのクラス名を指定します。 アプリケーションを公開したら、この名前を変更しないようにして、サービスを開始またはバインドする明示的なインテントに依存することによるコードの破損のリスクを回避します (ブログ投稿「変更できないもの」をお読みください)。 注意: アプリの安全性を確保するために、Service を開始するときは常に明示的なインテントを使用し、サービスに対してインテント フィルターを宣言しないでください。 暗黙のインテントを使用してサービスを開始すると、インテントに応答するサービスを特定できず、ユーザーはどのサービスが開始されたかを確認できないため、セキュリティ上の危険があります。 Android 5.0 (API レベル 21) 以降、暗黙のインテントを使用して bindService() を呼び出すと、システムが例外をスローします。

android:exported 属性を含め、それを false に設定すると、サービスが自分のアプリだけで利用できることを確実にすることができます。

Note: ユーザーは、自分のデバイスで実行されているサービスを確認できます。 ユーザーが認識または信頼しないサービスを見つけた場合、そのサービスを停止することができます。 ユーザーによってサービスが誤って停止されないようにするには、アプリ マニフェストの <service> 要素に android:description 属性を追加する必要があります。 説明では、サービスが何を行い、どのようなメリットを提供するかを説明する短い文を提供します。

開始済みサービスの作成

開始済みサービスとは、他のコンポーネントが startService() を呼び出すことにより開始し、サービスのonStartCommand()メソッドが呼び出されるサービスのことです。 サービスは、それを開始したコンポーネントが破壊されたとしても、バックグラウンドで無期限に実行できます。

アクティビティなどのアプリケーション コンポーネントは、startService() を呼び出し、サービスを指定し、サービスが使用するデータを含む Intent を渡すことによって、サービスを開始することができます。 サービスは、onStartCommand() メソッドでこの Intent を受け取ります。

例えば、アクティビティがオンライン データベースにいくつかのデータを保存する必要があるとします。 アクティビティはコンパニオンサービスを開始し、startService()にインテントを渡すことで保存するデータを渡します。 サービスはonStartCommand()でインテントを受信し、インターネットに接続し、データベーストランザクションを実行する。 トランザクションが完了すると、サービスは自身を停止し、破棄されます。

注意。 注意: サービスは、それが宣言されているアプリケーションと同じプロセスで、デフォルトでそのアプリケーションのメインスレッドで実行されます。 ユーザーが同じアプリケーションのアクティビティとやり取りしている間に、サービスが集中処理またはブロック処理を実行すると、アクティビティパフォーマンスが低下します。 アプリケーションのパフォーマンスに影響を与えないようにするには、サービス内で新しいスレッドを開始します。

Service クラスは、すべてのサービスのベースクラスです。 このクラスを拡張する場合、サービスがすべての作業を完了できる新しいスレッドを作成することが重要です。サービスはデフォルトでアプリケーションのメインスレッドを使用し、アプリケーションが実行している任意のアクティビティのパフォーマンスを低下させる可能性があります。 このクラスの使用は、Background 実行制限の導入により Android 8 Oreo からうまく機能しないため、新しいアプリには推奨されません。

以下のセクションでは、独自のカスタム サービスを実装する方法を説明しますが、ほとんどの使用例では、代わりに WorkManager の使用を強く考慮すべきです。

Extending the Service class

Service クラスを拡張して、各入力意図を処理することができます。

Kotlin

class HelloService : Service() { private var serviceLooper: Looper? = null private var serviceHandler: ServiceHandler? = null // Handler that receives messages from the thread private inner class ServiceHandler(looper: Looper) : Handler(looper) { override fun handleMessage(msg: Message) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000) } catch (e: InterruptedException) { // Restore interrupt status. Thread.currentThread().interrupt() } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1) } } override fun onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply { start() // Get the HandlerThread's Looper and use it for our Handler serviceLooper = looper serviceHandler = ServiceHandler(looper) } } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show() // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job serviceHandler?.obtainMessage()?.also { msg -> msg.arg1 = startId serviceHandler?.sendMessage(msg) } // If we get killed, after returning from here, restart return START_STICKY } override fun onBind(intent: Intent): IBinder? { // We don't provide binding, so return null return null } override fun onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show() }}

Java

public class HelloService extends Service { private Looper serviceLooper; private ServiceHandler serviceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore interrupt status. Thread.currentThread().interrupt(); } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work doesn't disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler serviceLooper = thread.getLooper(); serviceHandler = new ServiceHandler(serviceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = serviceHandler.obtainMessage(); msg.arg1 = startId; serviceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); }}

サンプル コードはすべての着信を onStartCommand() で処理して、バックグラウンド スレッドで実行する Handler に作業を投稿します。 これは IntentService と同じように動作し、すべてのリクエストを順番に処理します。

onStartCommand() メソッドは整数を返さなければならないことに注意してください。 この整数は、システムがサービスを停止した場合に、システムがサービスを継続する方法を記述する値です。 onStartCommand() の戻り値は、以下の定数のいずれかである必要があります:

START_NOT_STICKYonStartCommand()が戻った後にシステムがサービスを停止した場合、配信すべき保留中のコンテンツがない限り、サービスを再作成しないでください。 これは、必要でないときにサービスを実行しないようにするための最も安全なオプションで、アプリケーションが未完成のジョブを単に再起動することができる場合です。START_STICKYonStartCommand()が戻った後にシステムがサービスを停止した場合、サービスを再作成してonStartCommand()を呼び出しますが、最後のインテントを再送信しません。 その場合、それらのインテントが配信されます。 これは、コマンドを実行しないが、無期限に実行され、ジョブを待っているメディアプレーヤー (または同様のサービス) に適しています。START_REDELIVER_INTENTonStartCommand()が戻った後、システムがサービスを終了した場合、サービスを再作成し、サービスに配信された最後のインテントを使用してonStartCommand()を呼び出します。 保留中のインテントがあれば、順番に配信されます。 これは、ファイルのダウンロードなど、すぐに再開されるべきジョブをアクティブに実行しているサービスに適しています。

サービスの開始

startService()またはstartForegroundService()Intentを渡すことにより、アクティビティまたは他のアプリケーションコンポーネントからサービスを開始することができます。 AndroidシステムはサービスのonStartCommand()メソッドを呼び出し、開始するサービスを指定するIntentを渡します。

注意: APIレベル26以上のアプリの場合、アプリ自体がフォアグラウンドでなければバックグラウンドサービスの使用または作成に制限を課しています。 アプリがフォアグラウンドサービスを作成する必要がある場合、アプリはstartForegroundService()を呼び出す必要があります。 このメソッドはバックグラウンドのサービスを作成しますが、このメソッドはサービスがフォアグラウンドに昇格することをシステムに通知します。 サービスが作成されたら、サービスは5秒以内にそのstartForeground()メソッドを呼び出さなければならない。

たとえば、アクティビティは前のセクション(HelloService)でstartService()を使用した明示的なインテントを使用して、次のように例のサービスを開始できます:

Kotlin

Intent(this, HelloService::class.java).also { intent -> startService(intent)}

Java

Intent intent = new Intent(this, HelloService.class);startService(intent);

メソッドはすぐに戻り、Android システムはサービスの onStartCommand()メソッドを呼び出します。

サービスがバインディングも提供しない場合、startService()で配信されるインテントが、アプリケーションコンポーネントとサービス間の唯一の通信手段となります。 しかし、サービスに結果を返させたい場合、サービスを開始するクライアントはブロードキャスト用のPendingIntentを作成し(getBroadcast()で)、サービスを開始するIntentでサービスに配信することができます。

サービスを開始するための複数のリクエストは、サービスの onStartCommand() への複数の対応する呼び出しになります。

サービスを停止する

開始したサービスは、それ自身のライフサイクルを管理する必要があります。 つまり、システムメモリを回復する必要がない限り、システムはサービスを停止したり破壊したりせず、onStartCommand()が戻った後もサービスは実行され続けます。

stopSelf()またはstopService()で停止を要求された場合、システムは可能な限り早くサービスを破壊する。

サービスがonStartCommand()への複数の要求を同時に処理する場合、開始要求の処理が終了した時点でサービスを停止してはいけません。 この問題を回避するには、stopSelf(int)を使用して、サービスを停止する要求が常に最新の開始要求に基づいていることを確認します。 つまり、stopSelf(int)を呼ぶときに、停止要求に対応する開始要求のID(onStartCommand()に配送されたstartId)を渡すのである。

注意:stopSelf(int)を呼び出す前にサービスが新しい開始要求を受け取った場合、IDが一致せず、サービスは停止しません。 注意: システムリソースの浪費やバッテリーの消費を防ぐため、アプリケーションが作業を終えたらサービスを停止するようにしてください。必要であれば、他のコンポーネントがstopService()を呼び出してサービスを停止することができます。 サービスのバインドを有効にしても、onStartCommand() への呼び出しを受けた場合は、常にサービスを自分で停止する必要があります。

サービスのライフサイクルの詳細については、以下の「サービスのライフサイクルの管理」のセクションを参照してください。

アプリケーションのアクティビティや他のコンポーネントからサービスと対話したり、プロセス間通信 (IPC) を通じてアプリケーションの機能の一部を他のアプリケーションに公開したりする場合は、バインド サービスを作成します。 他のアプリケーション・コンポーネントはbindService()を呼び出してインターフェイスを取得し、サービスのメソッドを呼び出すことを開始できます。 サービスは、バインドされているアプリケーション コンポーネントにサービスを提供するためだけに存続するので、サービスにバインドされているコンポーネントがない場合、システムはサービスを破棄します。

バインド サービスを作成するには、クライアントがサービスと通信する方法を特定するインターフェイスを定義する必要があります。 サービスとクライアント間のこのインターフェイスは IBinder の実装でなければならず、サービスが onBind() コールバック・メソッドから返さなければならないものです。

複数のクライアントが同時にサービスにバインドすることができます。 サービスにバインドされているクライアントがいなくなると、システムはサービスを破棄します。

バインドされたサービスを実装する方法は複数あり、その実装は開始されたサービスよりも複雑です。 これらの理由から、バインド サービスの説明はバインド サービスに関する別のドキュメントに記載されています。

ユーザーへの通知の送信

サービスが実行中の場合、トースト通知またはステータス バー通知を使用して、ユーザーにイベントを通知できます。 ステータス バー通知は、メッセージと共にステータス バーにアイコンを提供し、ユーザーがアクション (アクティビティの開始など) を実行するために選択できます。

通常、ステータス バー通知は、ファイルのダウンロードなどのバックグラウンド処理が完了し、ユーザーがそれに対処できる場合に使用する最良の手法です。

詳細については、Toast Notifications または Status Bar Notifications の開発者ガイドを参照してください。 しかし、サービスがどのように作成され、破棄されるかに細心の注意を払うことはさらに重要です。

サービスのライフサイクル (作成時から破棄時まで) は、次の 2 つのパスのどちらかをたどります。 その後、サービスは無期限に実行され、stopSelf()を呼び出すことで自身を停止させる必要があります。 別のコンポーネントはstopService()を呼び出してサービスを停止させることもできる。

  • バインドされたサービス

    他のコンポーネント(クライアント)がbindService()を呼び出すと、サービスが作成される。 クライアントはIBinderインタフェースを通してサービスと通信する。 クライアントはunbindService()を呼び出すことで接続を終了することができる。 複数のクライアントが同じサービスにバインドすることができ、 すべてのクライアントがバインドを解除すると、システムはそのサービスを破棄する。

  • これらの 2 つの経路は完全に分離されているわけではありません。 startService() で既に開始されているサービスにバインドすることができる。 例えば、再生する音楽を特定する Intent を指定して startService() を呼び出すと、BGM サービスを開始することができる。 その後、ユーザーがプレーヤーを制御したり、現在の曲に関する情報を取得したい場合、アクティビティはbindService()を呼び出してサービスにバインドすることができます。 このような場合、すべてのクライアントがバインドを解除するまで、stopService() または stopSelf() は実際にサービスを停止しません。

    ライフサイクル コールバックの実装

    アクティビティのように、サービスにはライフサイクル コールバック メソッドがあり、サービスの状態の変化を監視し、適切なタイミングで作業を実行できるよう実装することができます。 次の skeletonservice は、各ライフサイクル メソッドを示しています。

    Kotlin

    class ExampleService : Service() { private var startMode: Int = 0 // indicates how to behave if the service is killed private var binder: IBinder? = null // interface for clients that bind private var allowRebind: Boolean = false // indicates whether onRebind should be used override fun () { // The service is being created } override fun (intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override fun (intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override fun (intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override fun (intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override fun () { // The service is no longer used and is being destroyed }}

    Java

    public class ExampleService extends Service { int startMode; // indicates how to behave if the service is killed IBinder binder; // interface for clients that bind boolean allowRebind; // indicates whether onRebind should be used @Override public void () { // The service is being created } @Override public int (Intent intent, int flags, int startId) { // The service is starting, due to a call to  return startMode; } @Override public IBinder (Intent intent) { // A client is binding to the service with  return binder; } @Override public boolean (Intent intent) { // All clients have unbound with  return allowRebind; } @Override public void (Intent intent) { // A client is binding to the service with , // after onUnbind() has already been called } @Override public void () { // The service is no longer used and is being destroyed }}

    注意:アクティビティのライフサイクル コールバックメソッドと異なり、これらのコールバックメソッドのスーパークラスの実装をコールする必要はありません。 サービスのライフサイクル。 左側の図は、サービスが startService() で作成された場合のライフサイクルを示し、右側の図は、サービスが bindService() で作成された場合のライフサイクルを示しています。

    図 2 は、サービスの典型的なコールバック メソッドを示しています。 図では、startService() によって作成されたサービスと bindService() によって作成されたサービスを分けていますが、どのサービスも、それがどのように開始されたかに関係なく、クライアントがそれにバインドすることを潜在的に許可することができることに留意してください。

    これらのメソッドを実装することにより、サービスのライフサイクルのこれら 2 つのネストされたループを監視することができます。 アクティビティと同様に、サービスはonCreate()で初期設定を行い、onDestroy()で残りのリソースをすべて解放する。

    注意:onCreate()onDestroy()のメソッドは、startService()またはbindService()で作成したサービスに関わらず、すべてのサービスに対して呼び出されます。

  • サービスの active lifetime は onStartCommand() または onBind() の呼び出しから始まり、それぞれのメソッドは startService() または bindService() に渡された Intent を受け取ります。

    サービスが開始された場合、 active lifetime は lifetime 全体の終了と同時に終了します (onStartCommand() が戻った後もサービスはアクティブです)。

  • 注意: 開始されたサービスは stopSelf() または stopService() の呼び出しによって停止するが、そのサービスに対するそれぞれのコールバックはない (onStop() コールバックもない)。 サービスがクライアントにバインドされていない限り、サービスが停止されるとシステムはそれを破棄します (onDestroy() が唯一のコールバックです)。

    コメントを残す

    メールアドレスが公開されることはありません。