サービスの概要

Service は、長時間実行オペレーションをバックグラウンドで実行できるアプリ コンポーネントです。ユーザー インターフェースは提供されません。サービスは、いったん開始されると、ユーザーが別のアプリケーションに切り替えた後もしばらく実行され続けることがあります。また、コンポーネントはサービスにバインドして操作できます。プロセス間通信(IPC)を実行することもできます。たとえば、ネットワーク トランザクションの処理、音楽の再生、ファイル I/O の実行、コンテンツ プロバイダとのやり取りをすべてバックグラウンドで行うことができます。

注意: サービスは、そのホスティング プロセスのメインスレッドで実行されます。特に指定しない限り、サービスは独自のスレッドを作成せず、別のプロセスで実行することもできません。アプリケーション応答なし(ANR)エラーを回避するには、サービス内の別のスレッドでブロッキング オペレーションを実行する必要があります。

サービスの種類

次の 3 種類のサービスがあります。

前景

フォアグラウンド サービスは、ユーザーが認識できる操作を実行します。たとえば、オーディオ アプリは、フォアグラウンド サービスを使用して音声トラックを再生します。フォアグラウンド サービスは Notification を表示する必要があります。ユーザーがアプリを操作していなくても、フォアグラウンド サービスは実行され続けます。

フォアグラウンド サービスを使用する場合は、サービスが実行されていることをユーザーが積極的に認識できるように、通知を表示する必要があります。この通知は、サービスが停止するか、フォアグラウンドから削除しない限り、閉じることはできません。

詳しくは、アプリでフォアグラウンド サービスを設定する方法をご覧ください。

注: WorkManager API を使用すると、柔軟にタスクのスケジュールを設定できます。また、必要に応じてこれらのジョブをフォアグラウンド サービスとして実行できます。多くの場合、フォアグラウンド サービスを直接使用するよりも、WorkManager を使用することをおすすめします。

背景
バックグラウンド サービスは、ユーザーが直接気付かない処理を実行します。たとえば、アプリがサービスを使用してストレージを圧縮する場合、通常はバックグラウンド サービスになります。

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

バインド
アプリケーションは、アプリ コンポーネントが bindService() を呼び出してサービスにバインドすると、「バインド」されます。バインドされたサービスはクライアント サーバー インターフェースを提供します。これにより、コンポーネントはサービスとやり取りし、リクエストを送信し、結果を受信できます。さらに、プロセス間通信(IPC)を使用してプロセス間でもこれを行うことができます。バインドされたサービスは、別のアプリケーション コンポーネントがバインドされている限り実行されます。複数のコンポーネントを一度にサービスにバインドできますが、すべてのコンポーネントがバインド解除されると、サービスは破棄されます。

このドキュメントでは通常、開始されたサービスとバインドされたサービスを別々に説明しますが、サービスはどちらの方法でも機能します。つまり、開始(無期限に実行)することも、バインドすることもできます。2 つのコールバック メソッドを実装するかどうかが重要です。onStartCommand() でコンポーネントによる開始を許可し、onBind() でバインディングを許可します。

サービスが開始、バインド、またはその両方のいずれであるかに関係なく、すべてのアプリ コンポーネントは、任意のコンポーネントがアクティビティを使用できるように Intent で開始することにより、(別のアプリからでも)サービスを使用できます。ただし、マニフェスト ファイルでサービスを非公開として宣言し、他のアプリからのアクセスをブロックすることは可能です。詳しくは、マニフェストでサービスを宣言するのセクションをご覧ください。

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

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

ユーザーがアプリを操作しているときにのみ、メインスレッドの外部で処理を実行する必要がある場合は、別のアプリ コンポーネントのコンテキストで新しいスレッドを作成する必要があります。たとえば、アクティビティの実行中にのみ音楽を再生する場合は、onCreate() にスレッドを作成し、onStart() でスレッドの実行を開始し、onStop() で停止します。また、従来の Thread クラスではなく、java.util.concurrent パッケージまたは Kotlin コルーチンのスレッドプールとエグゼキュータの使用も検討してください。実行をバックグラウンド スレッドに移行する方法について詳しくは、Android でのスレッド化のドキュメントをご覧ください。

サービスを使用する場合、デフォルトではアプリケーションのメインスレッドで実行されるため、集中的オペレーションやブロッキング オペレーションを実行する場合はサービス内に新しいスレッドを作成する必要があります。

基本情報

サービスを作成するには、Service のサブクラスを作成するか、既存のサブクラスのいずれかを使用する必要があります。実装では、サービス ライフサイクルの主要な側面を処理するコールバック メソッドをオーバーライドし、必要に応じて、コンポーネントをサービスにバインドできるメカニズムを提供する必要があります。オーバーライドすべき最も重要なコールバック メソッドは次のとおりです。

onStartCommand()
別のコンポーネント(アクティビティなど)がサービスの開始をリクエストすると、システムは startService() を呼び出すことでこのメソッドを呼び出します。このメソッドを実行すると、サービスが開始され、バックグラウンドで無期限に実行できます。これを実装する場合は、その処理の完了時に stopSelf() または stopService() を呼び出してサービスを停止する必要があります。バインディングのみを提供する場合は、このメソッドを実装する必要はありません。
onBind()
このメソッドは、別のコンポーネントがサービスとバインドする必要がある場合(RPC を実行するなど)に、bindService() を呼び出してこのメソッドを呼び出します。このメソッドの実装では、クライアントが IBinder を返してサービスと通信するために使用するインターフェースを提供する必要があります。このメソッドは常に実装する必要があります。ただし、バインディングを許可しない場合は、null を返す必要があります。
onCreate()
サービスが最初に作成されたとき(onStartCommand() または onBind() を呼び出す前)に、システムがこのメソッドを呼び出して 1 回限りの設定手順を実行します。サービスがすでに実行されている場合、このメソッドは呼び出されません。
onDestroy()
サービスが使用されなくなり、破棄されると、システムがこのメソッドを呼び出します。サービスは、スレッド、登録済みリスナー、レシーバなどのリソースをクリーンアップするために、これを実装する必要があります。これは、サービスが受け取る最後の呼び出しです。

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

コンポーネントが bindService() を呼び出してサービスを作成し、onStartCommand() が呼び出されない場合、コンポーネントがバインドされている限り、サービスが実行されます。サービスがすべてのクライアントからバインド解除されると、システムによって破棄されます。

Android システムは、メモリが少ない場合にのみサービスを停止し、ユーザーがフォーカスしたアクティビティのシステム リソースを復元する必要があります。ユーザー フォーカスのあるアクティビティにバインドされているサービスは、強制終了される可能性が低くなります。サービスのフォアグラウンドで実行することが宣言されている場合、サービスが強制終了されることはほとんどありません。サービスが開始していて長時間実行されている場合、バックグラウンド タスクのリスト内でのシステムの位置は時間の経過とともに低下し、サービスが強制終了する可能性が高くなります。サービスが開始される場合は、システムによる再起動を適切に処理するようにサービスを設計する必要があります。システムがサービスを強制終了した場合、リソースが使用可能になるとすぐにサービスが再起動されますが、これは onStartCommand() から返される値によっても異なります。システムがサービスを破棄するタイミングの詳細については、プロセスとスレッドのドキュメントをご覧ください。

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

マニフェストでサービスを宣言する

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

サービスを宣言するには、<service> 要素を <application> 要素の子として追加します。次に例を示します。

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

マニフェストでサービスを宣言する方法の詳細については、<service> 要素のリファレンスをご覧ください。

このほかにも、<service> 要素に指定して、サービスの開始に必要な権限やサービスを実行するプロセスなどのプロパティを定義できます。android:name 属性は唯一の必須属性で、サービスのクラス名を指定します。アプリの公開後は、サービスを開始またはバインドする明示的インテントに依存してコードが壊れるリスクを避けるため、この名前を変更しないでください(ブログ投稿、変更できないものをご覧ください)。

注意: アプリの安全性を確保するため、Service の起動時には常に明示的インテントを使用し、サービスのインテント フィルタを宣言しないでください。暗黙的インテントを使用してサービスを開始すると、インテントに応答するサービスを把握できず、ユーザーはどのサービスが開始するのかがわからないため、セキュリティ上の危険が伴います。Android 5.0(API レベル 21)以降では、暗黙的インテントで bindService() を呼び出すと、例外がスローされます。

自分のアプリでのみサービスを使用できるようにするには、android:exported 属性を含めて、false に設定します。これにより、明示的インテントを使用している場合でも、他のアプリによるサービスの開始が実質的に停止します。

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

開始されたサービスの作成

開始されたサービスとは、別のコンポーネントが startService() を呼び出して開始するサービスであり、結果としてサービスの onStartCommand() メソッドを呼び出します。

開始されたサービスは、そのサービスを開始したコンポーネントから独立したライフサイクルを持ちます。サービスは、サービスを開始したコンポーネントが破棄された場合でも、バックグラウンドで無期限に実行できます。そのため、ジョブが完了したら stopSelf() を呼び出してサービス自体を停止するか、別のコンポーネントが stopService() を呼び出してサービスを停止する必要があります。

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

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

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

Service クラスはすべてのサービスの基本クラスです。このクラスを拡張する際は、サービスがすべての作業を完了できる新しいスレッドを作成することが重要です。サービスはデフォルトでアプリのメインスレッドを使用するため、アプリが実行されているアクティビティのパフォーマンスが低下する可能性があります。

Android フレームワークには、ワーカー スレッドを使用してすべての開始リクエストを 1 つずつ処理する ServiceIntentService サブクラスも用意されています。このクラスを新しいアプリではおすすめしません。Android 8 Oreo 以降では、バックグラウンド実行の上限が導入されているために適切に動作しないためです。また、Android 11 以降は非推奨となっています。JobIntentService を、新しいバージョンの Android と互換性のある IntentService の代わりとして使用できます。

以降のセクションでは、独自のカスタム サービスを実装する方法について説明しますが、ほとんどのユースケースでは、代わりに WorkManager を使用することを強くおすすめします。Android でのバックグラウンド処理ガイドを参照して、ニーズに適したソリューションがあるかどうかを確認してください。

Service クラスの拡張

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_STICKY
onStartCommand() が返された後にシステムがサービスを強制終了した場合は、配信する保留中のインテントがない限り、サービスを再作成しないでください。これは、必要でなく、アプリケーションが未完了のジョブを単純に再開できる場合に、サービスの実行を回避できる最も安全なオプションです。
START_STICKY
onStartCommand() が返された後にシステムがサービスを強制終了した場合、サービスを再作成して onStartCommand() を呼び出しますが、最後のインテントは再配信しないでください。代わりに、サービスを開始するペンディング インテントがない限り、システムは null インテントで onStartCommand() を呼び出します。そのインテントが配信されますこれは、コマンドを実行していないが、無期限に実行され、ジョブを待機しているメディア プレーヤー(または同様のサービス)に適しています。
START_REDELIVER_INTENT
onStartCommand() が返された後にシステムがサービスを強制終了した場合は、サービスを再作成し、サービスに最後に配信されたインテントで onStartCommand() を呼び出します。ペンディング インテントはすべて順番に配信されます。これは、ファイルのダウンロードなど、すぐに再開する必要があるジョブをアクティブに実行しているサービスに適しています。

これらの戻り値の詳細については、各定数に関するリンク先のリファレンス ドキュメントをご覧ください。

サービスの開始

アクティビティやその他のアプリ コンポーネントからサービスを開始するには、IntentstartService() または startForegroundService() に渡します。Android システムは、サービスの onStartCommand() メソッドを呼び出して、開始するサービスを指定する Intent を渡します。

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

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

Kotlin

startService(Intent(this, HelloService::class.java))

Java

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

startService() メソッドがすぐに返され、Android システムはサービスの onStartCommand() メソッドを呼び出します。サービスがまだ実行されていない場合、システムはまず onCreate() を呼び出してから、onStartCommand() を呼び出します。

サービスがバインディングも提供しない場合、startService() で配信されるインテントが、アプリ コンポーネントとサービス間の唯一の通信モードです。ただし、サービスから結果を返す場合、サービスを開始するクライアントは、ブロードキャストの PendingIntentgetBroadcast() で作成し、サービスを開始する Intent のサービスに配信できます。その後、サービスはブロードキャストを使用して結果を配信できます。

サービスの開始リクエストを複数回行うと、対応するサービスの onStartCommand() が複数回呼び出されます。ただし、サービスを停止するために必要なリクエスト(stopSelf() または stopService())は 1 つだけです。

サービスの停止

開始されたサービスは、それ自体のライフサイクルを管理する必要があります。つまり、システムメモリを回復する必要がある場合を除き、サービスが停止または破棄されることはありません。また、onStartCommand() が返された後もサービスの実行を継続します。サービスは、stopSelf() を呼び出して自身を停止する必要があります。または、別のコンポーネントが stopService() を呼び出してサービスを停止することもできます。

stopSelf() または stopService() で停止がリクエストされると、システムは可能な限り早くサービスを破棄します。

サービスが onStartCommand() への複数のリクエストを同時に処理する場合は、開始リクエストの処理完了時にサービスを停止しないでください。新しい開始リクエストを処理する可能性があるためです(最初のリクエストの終了時に停止すると、2 番目のリクエストが終了します)。この問題を回避するには、stopSelf(int) を使用して、サービスの停止リクエストが常に最新の開始リクエストに基づいて行われるようにします。つまり、stopSelf(int) を呼び出すときに、停止リクエストに対応する開始リクエストの ID(onStartCommand() に配信された startId)を渡します。その後、stopSelf(int) を呼び出す前にサービスが新しい開始リクエストを受け取ると、ID が一致せず、サービスは停止しません。

注意: システム リソースを無駄にしたり、バッテリーの電力を消費したりしないように、アプリケーションの動作が完了したらサービスを停止してください。必要に応じて、他のコンポーネントで stopService() を呼び出すことでサービスを停止できます。サービスのバインディングを有効にする場合でも、onStartCommand() への呼び出しを受け取った場合は、常にサービスを自分で停止する必要があります。

サービスのライフサイクルの詳細については、以下のサービスのライフサイクルを管理するをご覧ください。

バインドされたサービスを作成する

バインドされたサービスとは、bindService() を呼び出して長時間の接続を作成することで、アプリケーション コンポーネントをサービスにバインドできるサービスです。通常は、コンポーネントが startService() を呼び出して起動することはできません。

バインドされたサービスは、アプリ内のアクティビティやその他のコンポーネントからサービスを操作する場合や、プロセス間通信(IPC)を介してアプリの機能の一部を他のアプリに公開する場合に作成します。

バインドされたサービスを作成するには、サービスとの通信用のインターフェースを定義する IBinder を返すように onBind() コールバック メソッドを実装します。その後、他のアプリ コンポーネントが bindService() を呼び出してインターフェースを取得し、サービスのメソッドの呼び出しを開始できます。サービスは、バインドされているアプリケーション コンポーネントにのみサービスを提供するため、バインドされているコンポーネントがない場合、システムによって破棄されます。バインドされたサービスを、onStartCommand() を介して開始するときと同じように停止する必要はありません。

バインドされたサービスを作成するには、クライアントがサービスと通信する方法を指定するインターフェースを定義する必要があります。サービスとクライアント間のこのインターフェースは IBinder の実装でなければならず、サービスは onBind() コールバック メソッドから返す必要があります。クライアントは IBinder を受信すると、そのインターフェースを介してサービスとのやり取りを開始できます。

複数のクライアントが同時にサービスにバインドできます。クライアントがサービスの操作を完了すると、unbindService() を呼び出してバインド解除します。サービスにバインドされているクライアントがない場合、システムはサービスを破棄します。

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

ユーザーに通知を送信する

サービスの実行中、スナックバー通知またはステータスバー通知を使用してユーザーにイベントを通知できます。

スナックバー通知は、現在のウィンドウのサーフェスに一瞬だけ表示されるメッセージです。ステータスバーの通知では、メッセージとともにアイコンがステータスバーに表示されます。ユーザーは、アイコンを選択してアクション(アクティビティの開始など)を行うことができます。

ファイルのダウンロードなどのバックグラウンド処理が完了し、ユーザーが処理できるようになった場合は、ステータスバーの通知を使用することをおすすめします。ユーザーが展開したビューから通知を選択すると、通知によってアクティビティ(ダウンロードしたファイルの表示など)を開始できます。

サービスのライフサイクルを管理する

サービスのライフサイクルは、アクティビティのライフサイクルよりもはるかにシンプルです。ただし、サービスはユーザーが気付かないうちにバックグラウンドで実行される可能性があるため、サービスがどのように作成、破棄されるかに細心の注意を払うことがさらに重要です。

サービスのライフサイクル(作成から破棄まで)は、次の 2 つのパスのいずれかをたどります。

  • 開始されたサービス

    別のコンポーネントが startService() を呼び出すと、サービスが作成されます。その後、サービスは無期限に実行され、stopSelf() を呼び出して自身を停止する必要があります。別のコンポーネントが stopService() を呼び出してサービスを停止することもできます。サービスが停止すると、システムによって破棄されます。

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

    別のコンポーネント(クライアント)が bindService() を呼び出すと、サービスが作成されます。その後、クライアントは IBinder インターフェースを介してサービスと通信します。クライアントは unbindService() を呼び出して接続を閉じることができます。複数のクライアントを同じサービスにバインドでき、すべてがバインド解除されると、システムはサービスを破棄します。サービス自体を停止する必要はありません。

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

ライフサイクル コールバックを実装する

アクティビティと同様に、サービスにはライフサイクル コールバック メソッドがあり、これを実装してサービスの状態の変化をモニタリングし、適切なタイミングで作業を実行できます。次のスケルトン サービスは、各ライフサイクル メソッドを示しています。

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 onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // 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 onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

注: アクティビティのライフサイクル コールバック メソッドとは異なり、これらのコールバック メソッドのスーパークラス実装を呼び出す必要はありません。

図 2. サービスのライフサイクル。左側の図は、startService() を使用してサービスを作成した場合のライフサイクルを示しています。右側の図は、bindService() を使用してサービスが作成されたときのライフサイクルを示しています。

図 2 は、サービスの典型的なコールバック メソッドを示しています。この図では、startService() によって作成されるサービスと bindService() によって作成されるサービスが分けられていますが、サービスの開始方法にかかわらず、クライアントがサービスにバインドできる可能性があることに留意してください。最初に onStartCommand() で開始されたサービス(クライアントが startService() を呼び出した場合)でも、onBind() の呼び出しを受け取ることができます(クライアントが bindService() を呼び出した場合)。

これらのメソッドを実装すると、サービスのライフサイクルの次の 2 つのネストされたループをモニタリングできます。

  • サービスの全期間は、onCreate() が呼び出されてから onDestroy() が返すまでの間に発生します。アクティビティと同様に、サービスは onCreate() で初期設定を行い、onDestroy() の残りのリソースをすべて解放します。たとえば、音楽再生サービスは、音楽を再生するスレッドを onCreate() で作成してから、onDestroy() でスレッドを停止できます。

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

  • サービスのアクティブ ライフタイムは、onStartCommand() または onBind() の呼び出しから始まります。各メソッドには、startService() または bindService() に渡された Intent が渡されます。

    サービスが開始されると、アクティブなライフタイムは全期間が終了するまでに終了します(onStartCommand() が返された後も、サービスはアクティブのままです)。サービスがバインドされている場合、onUnbind() が返されるとアクティブな存続期間が終了します。

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

バインディングを提供するサービスの作成の詳細については、バインドされたサービスのドキュメントをご覧ください。このドキュメントには、バインドされたサービスのライフサイクルを管理するセクションにある onRebind() コールバック メソッドの詳細が記載されています。