サービスの概要

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

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

フォアグラウンド サービス
フォアグラウンド サービスは、ユーザーが認識できる操作を行います。たとえば、オーディオ アプリは、フォアグラウンド サービスを使用してオーディオ トラックを再生します。フォアグラウンド サービスは通知を表示する必要があります。フォアグラウンド サービスは、ユーザーがアプリを操作していない間も動作し続けます。
バックグラウンド サービス
バックグラウンド サービスは、ユーザーには直接認識されない操作を行います。たとえば、ストレージを圧縮するサービスをアプリで使用する場合、そのサービスは通常、バックグラウンド サービスになります。

注:アプリが API レベル 26 以上を対象としている場合、アプリ自体がフォアグラウンドで動作していないときには、バックグラウンド サービスの実行に関する制限が適用されます。このような場合は通常、代わりにスケジュールされたジョブをアプリで使用してください。

バインドされたサービス
アプリ コンポーネントが bindService() を呼び出してサービスにバインドすると、サービスは「バインドされた」状態になります。バインドされたサービスは、コンポーネントがサービスを操作したり、リクエストを送信したり、結果を受信したり、さらにはプロセス間通信(IPC)でもそれを行えるクライアントサーバー型インターフェースを提供したりします。バインドされたサービスは、他のアプリ コンポーネントがそれにバインドしている限り動作し続けます。サービスには同時に複数のコンポーネントがバインドできますが、すべてがアンバインドされると、サービスは破棄されます。

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

サービスが開始されるか、バインドされるか、その両方であるかどうかにかかわらず、どんなコンポーネントでもアクティビティを使用できるのと同じように、Intent を使って開始することで、どんなアプリ コンポーネントでもサービスを使用できます(別のアプリからでも可能です)。ただし、マニフェスト ファイルでサービスを非公開として宣言して、他のアプリからのアクセスをブロックすることもできます。詳細については、マニフェストでサービスを宣言するで説明します。

注意:サービスは、そのホスティング プロセスのメインスレッドで実行されます。特に指定しない限り、サービスが自身のスレッドを作成することはなく、別のプロセスで実行されることもありません。サービスが CPU を集中的に使う作業やブロック操作(MP3 の再生やネットワーク作業など)を行う場合は、サービス内に新しいスレッドを作成してその作業を行う必要があります。別のスレッドを使うことで、「アプリが応答していません(ANR)」というエラーが発生するリスクを軽減でき、アプリのメインスレッドをアクティビティのユーザー操作専用にすることができます。

サービスとスレッドのどちらを選択するか

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

ユーザーがアプリを操作している間に限り、メインスレッドの外部で作業を行う必要がある場合は、代わりに新しいスレッドを作成してください。たとえば、アクティビティを実行している間だけ音楽を再生する場合は、onCreate() でスレッドを作成し、onStart() で実行を開始して、onStop() で停止します。また、従来の Thread クラスの代わりに AsyncTaskHandlerThread を使用する方法もあります。スレッドの詳細については、プロセスとスレッドのドキュメントをご覧ください。

サービスを使用する際は、デフォルトではアプリのメインスレッドで実行されるため、集中的な作業やブロック操作を行う場合はサービス内に新しいスレッドを作成する必要があることに注意してください。

基本

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

onStartCommand()
他のコンポーネント(アクティビティなど)がサービスの開始をリクエストするときに startService() を呼び出すと、このメソッドが呼び出されます。このメソッドが実行されると、サービスが開始され、バックグラウンドで無期限に動作できます。これを実装すると、作業完了時に stopSelf()stopService() を呼び出して自身でサービスを停止する必要があります。バインドのみを提供する場合は、このメソッドを実装する必要はありません。
onBind()
他のコンポーネントがサービスにバインドするとき(リモート プロシージャ コールを行う場合など)に bindService() を呼び出すと、このメソッドが呼び出されます。このメソッドの実装時には、IBinder を返してクライアントがサービスとの通信に使うインターフェースを提供する必要があります。このメソッドの実装は常に必要です。ただし、バインドを許可しない場合は、null を返す必要があります。
onCreate()
サービスが最初に作成されたときに 1 回限りのセットアップ処理を行うために(onStartCommand() または onBind() を呼び出す前に)、このメソッドが呼び出されます。サービスが既に実行中の場合、このメソッドは呼び出されません。
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 属性は唯一の必須属性で、サービスのクラス名を指定します。アプリを公開した後は、この名前を変更しないでください。変更すると、サービスを開始またはバインドする明示的インテントの依存関係が原因で、コードが破損するリスクがあります(ブログ投稿の Things That Cannot Change をご覧ください)。

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

android:exported 属性を含めて false に設定すると、サービスを自身のアプリでしか利用できないようにすることができます。これにより、他のアプリによるサービスの開始を効果的に回避でき、たとえ明示的インテントを使用したとしても開始できなくなります。

:ユーザーは、端末でどのサービスが実行されているかを確認できます。認識できないサービスや信頼できないサービスについては、停止することができます。ユーザーによってサービスが誤って停止されることがないようにするには、アプリのマニフェストで android:description 属性を <service> 要素に追加する必要があります。この説明に、サービスが何を行うかや、サービスにどのような利点があるかを紹介する簡潔な文を記載してください。

開始されたサービスを作成する

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

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

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

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

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

従来どおり、開始されたサービスを作成するために拡張できるクラスが 2 つあります。

Service
これは、すべてのサービスの基本クラスです。このクラスを拡張するときには、サービスがすべての作業を完了できる新しいスレッドを作成することが重要です。サービスはデフォルトでアプリのメインスレッドを使用するため、アプリが実行しているアクティビティのパフォーマンスが低下することがあります。
IntentService
ワーカー スレッドを使用してすべての開始リクエストを 1 件ずつ処理する Service のサブクラスです。サービスで同時に複数のリクエストを処理する必要がない場合は、これが最適です。バックグラウンド作業を完了できるように、開始リクエストごとにインテントを受け取る onHandleIntent() を実装します。

この後のセクションでは、これらいずれかのクラスを使用してサービスを実装する方法について説明します。

IntentService クラスを拡張する

開始されたサービスで同時に複数のリクエストを処理する必要があることはほとんどないため(実際には危険なマルチスレッド シナリオになります)、IntentService クラスを使用してサービスを実装するのが最適です。

IntentService クラスは次の操作を行います。

  • onStartCommand() に配信されるすべてのインテントを実行する、アプリのメインスレッドとは別のデフォルトのワーカー スレッドを作成します。
  • マルチスレッドの懸念を排除するため、一度に 1 つのインテントを onHandleIntent() の実装に渡すワークキューを作成します。
  • stopSelf() を呼び出す必要がないよう、すべての開始リクエストの処理後にサービスを停止します。
  • null を返す onBind() のデフォルト実装を提供します。
  • インテントをワークキュー、onHandleIntent() の実装の順に送信する onStartCommand() のデフォルト実装を提供します。

クライアントで指定された作業を完了するために、onHandleIntent() を実装します。ただし、サービスの小さいコンストラクタを提供することも必要です。

IntentService の実装例を次に示します。

Kotlin

/**
 * A constructor is required, and must call the super [android.app.IntentService.IntentService]
 * constructor with a name for the worker thread.
 */
class HelloIntentService : IntentService("HelloIntentService") {

    /**
     * The IntentService calls this method from the default worker thread with
     * the intent that started the service. When this method returns, IntentService
     * stops the service, as appropriate.
     */
    override fun onHandleIntent(intent: Intent?) {
        // 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()
        }

    }
}

Java

public class HelloIntentService extends IntentService {

  /**
   * A constructor is required, and must call the super <code><a href="/reference/android/app/IntentService.html#IntentService(java.lang.String)">IntentService(String)</a></code>
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // 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();
      }
  }
}

必要なのは、コンストラクタと onHandleIntent() の実装だけです。

onCreate()onStartCommand()onDestroy() などの他のコールバック メソッドもオーバーライドする場合は、IntentService がワーカー スレッドの生存状態を正しく処理できるように、必ずスーパー実装を呼び出します。

たとえば、onStartCommand() はデフォルト実装を返す必要があります。これによりインテントが onHandleIntent() に配信されます。

Kotlin

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()
    return super.onStartCommand(intent, flags, startId)
}

Java

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}

onHandleIntent() を除き、スーパークラスを呼び出す必要がないメソッドは onBind() のみです。これを実装する必要があるのは、サービスでバインドを許可する場合のみです。

次のセクションでは、同じ種類のサービスが、基本の Service クラスを拡張したときにどのように実装されるかを説明します。これにはさらに多くのコードが使用されますが、開始リクエストの同時処理が必要な場合には適しています。

Service クラスを拡張する

IntentService を使用すると、開始されたサービスの実装が非常に簡単になります。ただし、開始リクエストをワークキュー経由で処理するのではなく、サービスでマルチスレッドを実行する必要がある場合は、Service クラスを拡張して、それぞれのインテントを処理できます。

比較のために、次のサンプルコードでは、IntentService を使用した前の例と同じ作業を行う Service クラスの実装を示します。つまり、それぞれの開始リクエストに対し、ワーカー スレッドを使用してジョブを実行し、一度に 1 つのリクエストのみを処理します。

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();
  }
}

このように、IntentService を使う場合よりもかなり面倒です。

ただし、onStartCommand() への各呼び出しを自身で処理するため、同時に複数のリクエストを実行することも可能です。この例には当てはまりませんが、自身の状況に合う場合は、リクエストごとに新しいスレッドを作成し、前のリクエストが完了するのを待つ代わりに、すぐに実行することもできます。

onStartCommand() メソッドは整数を返す必要があることに注意してください。この整数は、システムがサービスを強制終了した場合に、サービスをどのように続行するかを示す値です。IntentService のデフォルト実装ではこれが処理されますが、変更することもできます。onStartCommand() から返される値は、次のいずれかの定数である必要があります。

START_NOT_STICKY
onStartCommand() から戻った後でシステムがサービスを強制終了した場合、配信が保留中のインテントがない限り、サービスを再作成しません。これは、サービスが不必要で、アプリが未完了のジョブを再開できる場合にサービスを実行してしまうのを回避できる最も安全な選択肢です。
START_STICKY
onStartCommand() から戻った後でシステムがサービスを強制終了した場合、サービスを再作成し、onStartCommand() を呼び出しますが、最後のインテントは再配信しません。代わりに、システムは null インテントで onStartCommand() を呼び出します。ただし、サービスを開始する保留中のインテントがある場合は除きます。その場合は、それらのインテントが配信されます。これは、コマンドは実行しないが、無期限に動作し、ジョブを待機するメディア プレーヤー(または同様のサービス)に適しています。
START_REDELIVER_INTENT
onStartCommand() から戻った後でシステムがサービスを強制終了した場合、サービスを再作成し、サービスに最後に配信されたインテントで onStartCommand() を呼び出します。保留中のすべてのインテントが順に配信されます。これは、ファイルのダウンロードなど、活発にジョブを実行し、直ちに再開する必要のあるサービスに適しています。

これらの戻り値の詳細については、リンクされた各定数のリファレンス ドキュメントをご覧ください。このタイプのサービス実装を拡張する例については、GitHub の MessagingService のサンプルMessagingService クラスをご覧ください。

サービスを開始する

アクティビティや他のアプリ コンポーネントからサービスを開始するには、IntentstartService() または startForegroundService() に渡します。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);

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

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

サービスを開始するリクエストが複数ある場合は、それに対応する複数の呼び出しがサービスの onStartCommand() に対して発生することになります。ただし、サービスを停止するために必要なリクエストは 1 つのみです(stopSelf() または stopService() を使用)。

サービスを停止する

開始されたサービスは、自身でライフサイクルを管理する必要があります。つまり、システムメモリを回復する必要がある場合を除き、システムがサービスを停止したり破棄したりすることはなく、サービスは onStartCommand() から戻った後も動作し続けます。サービスは、stopSelf() を呼び出して自身で停止する必要があります。また、他のコンポーネントが stopService() を呼び出してサービスを停止することもできます。

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

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

注意:システム リソースの浪費や電池の消耗を防ぐために、サービスが作業を完了したら、アプリがサービスを停止するようにしてください。必要であれば、他のコンポーネントから stopService() を呼び出してサービスを停止することもできます。サービスのバインドを有効にしている場合でも、onStartCommand() への呼び出しを受け取ったときには常に自身でサービスを停止する必要があります。

サービスのライフサイクルの詳細については、後半のセクションのサービスのライフサイクルを管理するをご覧ください。

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

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

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

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

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

複数のクライアントが同時にサービスにバインドできます。クライアントとサービスのやり取りが終わったら、unbindService() を呼び出してアンバインドします。サービスにバインドされているクライアントがなくなったら、システムがサービスを破棄します。

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

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

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

トースト通知は、現在のウィンドウに少しの間だけ表示され、すぐに消えるメッセージです。ステータスバー通知では、ステータスバーにメッセージの付いたアイコンが表示され、ユーザーはそれを選択してなんらかの操作(アクティビティの開始など)を行うことができます。

ステータスバー通知は通常、ファイルのダウンロードなどのバックグラウンド作業が完了し、ユーザーがそれに対して操作を行えるようになったときに使用する最適なテクニックです。展開したビューでユーザーが通知を選択すると、通知がアクティビティ(ダウンロードしたファイルを表示するなど)を開始できるようになります。

詳細については、デベロッパー ガイドのトースト通知ステータスバー通知をご覧ください。

サービスをフォアグラウンドで実行する

フォアグラウンド サービスは、ユーザーがその存在を認識しているサービスであり、メモリ残量が少なくなった場合でも、システムによる強制終了の候補にはなりません。フォアグラウンド サービスではステータスバーに通知を表示する必要があり、「継続中」という見出しの下に通知が表示されます。これは、サービスが停止するか、フォアグラウンドから削除されない限り、通知を消すことができないことを意味します。

注意:アプリのフォアグラウンド サービスの使用を制限してください。

ユーザーがアプリを直接操作していないときでも、ユーザーが認識できるタスクをアプリが実行する必要がある場合にのみ、フォアグラウンド サービスを使用するようにしてください。そのため、フォアグラウンド サービスは、アプリが何を行っているかをユーザーが認識できるように、優先度が PRIORITY_LOW 以上のステータスバー通知を表示する必要があります。操作の重要性が低く、優先度が最も低い通知でも十分な場合は、サービスを使用するのではなく、スケジュールされたジョブを使用することを検討してください。

サービスを実行するアプリはすべて、システムに余分な負荷をかけ、システム リソースを消費します。優先度が低い通知を使用して、アプリがサービスを隠そうとする場合、ユーザーがアクティブに操作しているアプリのパフォーマンスが低下することがあります。そのため、優先度が最も低い通知を使用するサービスをアプリが実行しようとすると、システムは、通知ドロワーの下部セクションにアプリの動作を表示します。

たとえば、サービスから音楽を再生する音楽プレーヤーは、ユーザーがその操作を認識しているのは明らかであるため、フォアグラウンドで実行する必要があります。ステータスバーの通知には現在再生中の曲名を表示し、ユーザーが音楽プレーヤーを操作するためのアクティビティを起動できるようにすることができます。同様に、ユーザーの走行を追跡できるアプリには、ユーザーの位置情報を追跡するフォアグラウンド サービスが必要です。

サービスをフォアグラウンドで実行するようリクエストするには、startForeground() を呼び出します。このメソッドは、通知を一意に識別する整数と、ステータスバーの Notification の 2 つのパラメータを受け取ります。通知の優先度は PRIORITY_LOW 以上である必要があります。次に例を示します。

Kotlin

val pendingIntent: PendingIntent =
        Intent(this, ExampleActivity::class.java).let { notificationIntent ->
            PendingIntent.getActivity(this, 0, notificationIntent, 0)
        }

val notification: Notification = Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
        .setContentTitle(getText(R.string.notification_title))
        .setContentText(getText(R.string.notification_message))
        .setSmallIcon(R.drawable.icon)
        .setContentIntent(pendingIntent)
        .setTicker(getText(R.string.ticker_text))
        .build()

startForeground(ONGOING_NOTIFICATION_ID, notification)

Java

Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent =
        PendingIntent.getActivity(this, 0, notificationIntent, 0);

Notification notification =
          new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
    .setContentTitle(getText(R.string.notification_title))
    .setContentText(getText(R.string.notification_message))
    .setSmallIcon(R.drawable.icon)
    .setContentIntent(pendingIntent)
    .setTicker(getText(R.string.ticker_text))
    .build();

startForeground(ONGOING_NOTIFICATION_ID, notification);

注意:startForeground() に渡す整数 ID には、0 は使用できません。

サービスをフォアグラウンドから削除するには、stopForeground() を呼び出します。このメソッドは、ステータスバー通知も削除するかどうかを示すブール値を受け取ります。このメソッドでは、サービスは停止されません。ただし、サービスがまだフォアグラウンドで実行中に停止した場合は、通知も削除されます。

通知の詳細については、ステータスバー通知の作成をご覧ください。

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

サービスのライフサイクルは、アクティビティのライフサイクルよりもはるかにシンプルです。ただし、サービスはバックグラウンドでサイレントに実行されるため、サービスがどのように作成され、破棄されるかについては、より一層の注意を払っておく必要があります。

作成されてから破棄されるまでのサービスのライフサイクルには、次の 2 つの経路があります。

  • 開始されたサービス

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

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

    他のコンポーネント(クライアント)が bindService() を呼び出したときにサービスが作成されます。その後、クライアントが IBinder インターフェースを介してサービスとやり取りします。クライアントは、unbindService() を呼び出して接続を終了できます。サービスには同時に複数のクライアントがバインドでき、すべてがアンバインドされると、サービスはシステムによって破棄されます。サービスが自身で停止する必要はありません。

この 2 つの経路は、まったく別であるわけではありません。startService() で既に開始されているサービスにバインドすることも可能です。たとえば、再生する音楽を識別する 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 mStartMode
    }

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

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

    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 mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @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() で作成されたサービスを分けていますが、開始方法にかかわらず、すべてのサービスがクライアントからバインドされる可能性があることに注意してください。最初は(クライアントが startService() を呼び出して)onStartCommand() で開始されたサービスも、(クライアントが bindService() を呼び出したときに)onBind() への呼び出しを受け取ることができます。

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

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

    :onCreate() メソッドと onDestroy() メソッドは、それが startService()bindService() のどちらで作成された場合でも、すべてのサービスに対して呼び出されます。

  • サービスのアクティブな生存期間は、onStartCommand() または onBind() への呼び出しから始まります。各メソッドには、startService() または bindService() に渡された Intent が渡されます。

    サービスが開始された場合、アクティブな生存期間の終了は、全体の生存期間の終了と同じタイミングとなります(onStartCommand() から戻った後もサービスはまだアクティブです)。サービスがバインドされた場合、アクティブな生存期間は、onUnbind() から戻った時点で終了します。

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

バインドを提供するサービスの作成に関する詳細は、バインドされたサービスのドキュメントをご覧ください。このドキュメントのバインドされたサービスのライフサイクルを管理するでは、onRebind() コールバック メソッドの詳細についても説明しています。