【Kotlin/Android】Foreground Serviceの実装方法

この記事からわかること
- Android Studio/KotlinのForeground Serviceの使い方
- サービスの種類と実装方法
- アプリが停止しても処理を継続するには?
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
Foreground Serviceとは?
Foreground ServiceはAndroidのServiceの種類の1つでその中でもユーザーが認識できる操作を行うサービスです。バックグラウンドでの処理を長時間行えるようにするために実行している処理をユーザーに明示的に通知する必要があります。例えば音楽を再生するなど、ユーザーがアプリを操作していない間も動作するような処理の際に利用します。
またForeground Serviceは通常のバックグラウンドサービスよりも高い優先度で実行され、システムにより終了される可能性が低くなり、バッテリー最適化やDozeモードの影響も受けにくいのが特徴です。
実装方法
Foreground Serviceは通常のServiceとは少し実装方法が異なります。必要になるのが「通知の実装」と「startForeground
メソッドの呼び出し」、「パーミッションの追加」です。あとは通常のServiceの実装の流れと変わりません。
通常のService実装の流れ
- Serviceを継承したクラスの作成
- マニフェストファイルへServiceを追記
- Serviceを呼び出す
1.Serviceを継承したクラスの作成
Service
を継承したサブクラスを定義し、onStartCommand
メソッドの中で「通知の実装」と「startForeground
メソッドの呼び出し」を行います。
class MyForegroundService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 通知を作成
val notification = createNotification()
// フォアグラウンドサービスとして開始
startForeground(1, notification)
// onStartCommandメソッドの中はメインスレッド
Log.d("Service", Thread.currentThread().getName()) // main
// 何かしら実行したい処理
// 音楽の再生など
return START_NOT_STICKY
}
// サービス実行中に表示される通知を生成
private fun createNotification(): Notification {
val notificationChannelId = "MY_FOREGROUND_SERVICE_CHANNEL"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
notificationChannelId,
"My Foreground Service",
NotificationManager.IMPORTANCE_LOW
)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(notificationChannel)
}
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
return notificationBuilder.setOngoing(true)
.setContentTitle("Foreground Service")
.setContentText("サービスが実行中です")
.build()
}
}
2.マニフェストファイルへServiceとパーミッションの追記
Serviceを追加したら「AndroidManifest.kt」へ追加したServiceを明記する必要があります。さらにForeground Serviceの場合はAndroid 9(API レベル 28)以降から適切なパーミッションを定義する必要があります。
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
...
<application ...>
<service
android:name=".MyForegroundService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
3.Foreground Serviceを呼び出す
Foreground Serviceを呼び出すにはstartForegroundService
メソッドを使用します。これはAPI26以降でないと使用できません。
val intent = Intent(this, MyForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
}
実行すると以下のように通知欄にサービスが実行していることが通知されます。これはサービスが動作している間表示されます。

アプリをキルしても処理が継続する
Foreground Serviceはアプリ自体がバックグラウンド状態(ホーム画面や他のアプリ起動、アプリのキル、電源OFF(スリープ)状態)でも動作してくれます。放置し続けて30分ほど様子をみましたが、正常に動作していることを確認できました。
以下は動作検証で使用したカウントアップ処理です。
class MyForegroundService : Service() {
private lateinit var handlerThread: HandlerThread
private lateinit var handler: Handler
private var count = 0
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 通知を作成
val notification = createNotification()
// フォアグラウンドサービスとして開始
startForeground(1, notification)
Log.d("Service", Thread.currentThread().getName()) // main
// バックグラウンドスレッドでカウントアップを実行
handlerThread = HandlerThread("MyHandlerThread")
handlerThread.start()
handler = Handler(handlerThread.looper)
handler.post(countRunnable)
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacks(countRunnable)
handlerThread.quitSafely()
}
// カウントアップ処理
private val countRunnable = object : Runnable {
override fun run() {
count++
Log.d("Service", "Count: $count")
handler.postDelayed(this, 1000) // 1秒ごとに実行
}
}
// 〜〜〜〜〜〜〜〜
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。