[Android] Service の使い方

Servce はバックグラウンドで作業をさせたい場合に使います。システムは直ぐにスリープに入れてしまいます。それでも裏で色々やりたい場合に有効です。

ただし、Oreoからはバックグラウンドの制限ができて、裏でごにょごにょ派にはイバラの道となりました。startForegroundService()を使うとか…
 

Android Studio 3.0.1
Android 8.1.0

 



Service

 
Serviceがバックグラウンドで動作するので、非同期と勘違いしそうですがそうではなく、Activityから表示UIを無くしたようなコンポーネントです。ですから別スレッドでの処理が必要なものはActivityと同じように扱います。
例としては、バックグラウンドでのネットからのデータダウンロードなどがあります。
 
別スレッド実装は IntentService を使うと簡単になります。
また、サービス | Android Developers にはこのようにあるので IntentService を推奨しているようでもあります。

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

IntentServiceについては別途扱って見たいと思います。
 

 

Serviceの開始

 
Serviceを開始するには、

  1. startService()
    • 開始後は、開始したActivityが破棄されても基本的には実行し続けられる、Activityとは別の独自のContextを持っている
    • 呼び出し側からの制御は開始か停止させるのみで、タスクの終了をコールバックしないので自身で終了するなどの手当が必要
  2. bindService()
    • 要求を送信したり、結果を取得したりとServiceを制御できますが、呼び出し元のActivityが終了すると一緒に終了してしまう
 
これにAndroid 8.0からの追加として、
startForegroundService()
を使ってバックグラウンド実行制限をある程度回避できます。

startService()

 
Activity からServiceを呼び出すのはIntent をセットして、startService(intent) で行います。
 

 
Activity から Activity に遷移させる場合は
startActivity(intent)でしたので、startService(intent)に変わっただけとわかりやすです
 
一方、サービス側は

 
Service を継承して

  1. onCreate()
    • これはActivity同様、最初だけ呼ばれて終わりなので初期化だけです。
  2. onStartCommand()
    • サービスで実行させたいコードはここに記述
  3. onDestroy()
  4. onBind()
    • bindService() で呼び出した場合
      onStartCommand() ではなく
      onBind() がcallbackされます

 
Manifest にサービスクラスの定義を忘れないように
AndroidManifest.xml

 

 

Serviceの終了

 
startService()で始めたServiceは勝手には止まりません。
 
MainActivityからstopService(intent)で止める
 

 
audioが終了したのを受けてstopSelf()を実行します。
 

 
と言いながらも、昨今はAndroidのバッテリー消費にうるさくなっているのでしょうか、サービス | Android Developersにはこのような記述もあります。

Android システムは メモリが少なくなって、ユーザーが使用しているアクティビティ用のシステムリソースを回復させる必要が生じた場合のみ、サービスを強制的に停止させます…

 
またOreoからバックグラウンドに対する制限ができて

アプリがバックグラウンドに移行すると数分間のウィンドウが提供され、アプリはそのウィンドウ内でサービスを作成して使用できます。
 
そのウィンドウの終了時に、アプリは アイドル状態 であると見なされます。 システムはこの時点で、アプリがサービスの Service.stopSelf() メソッドを呼び出したかのように、アプリのバックグラウンド サービスを停止します。

バックグラウンド実行制限 | Android Developers
 
またマルチウィンドウからアプリをユーザーが終了させることもあります。

 

startForegroundService()

 
バックグラウンドでアプリが実行できないと、一番困るのはmusic系のアプリでしょうか、それについては
バックグラウンド実行制限 | Android Developersにはこのような一文があります。

Android 8.0 では、追加機能があります。システムは、バックグラウンド アプリによるバッグラウンド サービスの作成を許可しません。
そのため、Android 8.0 では、フォアグラウンドで新しいサービスを作成する Context.startForegroundService() メソッドが新たに導入されています。

システムによってサービスが作成されると、アプリは、サービスの startForeground() メソッドを 5 秒以内に呼び出して、
その新しいサービスの通知をユーザーに表示します。
アプリが startForeground() を制限時間内に呼び出さない場合、サービスが停止し、アプリが ANR になります。

ということでこれを使ってMediaPlayerでをServiceで音楽再生してみます。

ActivityからstartForegroundService()でServiceを開始し、Service内で5秒以内に
startForegroundを呼び出します。これでステータスバーにはアイコンが表示されてForgroundにいるかのようになるということでしょう。
 
MainActivity.java

 
サービス側では、MediaPlayerを使ってmp3を再生させてみます。
MediaPlayerについてはこちらで解説しています。

[Android] 簡単なMediaPlayerで音楽を再生する
MediaPlayer()を使って簡単な音楽再生アプリが作れます。Audioを扱うものとしては他にはSoundPoolやAudioTrack...

TestService.java

 
レイアウトです。
activity_main.xml

 
strings.xml

 
AndroidManifest.xml

 
これでアプリを終了、マルチウィンドウから削除してもmp3が再生されています。


 

ただし、あまり調子に乗っていると、System様から睨まれます。これを見たユーザーが電池の使用量が多かった暁にはこのアプリを削除する確率は何パーセントでしょうか?

また、このstartForegroundService()もAndroid 8.0からなので、APIによる切り分けが必要になりそうです。

 
References:
サービス | Android Developers
バックグラウンド実行制限 | Android Developers
Service | Android Developers