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

この記事からわかること

  • Android Studio/KotlinForeground Service使い方
  • サービス種類実装方法
  • アプリが停止しても処理継続するには?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Foreground Serviceとは?

Foreground ServiceはAndroidのServiceの種類の1つでその中でもユーザーが認識できる操作を行うサービスです。バックグラウンドでの処理を長時間行えるようにするために実行している処理をユーザーに明示的に通知する必要があります。例えば音楽を再生するなど、ユーザーがアプリを操作していない間も動作するような処理の際に利用します。

またForeground Serviceは通常のバックグラウンドサービスよりも高い優先度で実行され、システムにより終了される可能性が低くなり、バッテリー最適化やDozeモードの影響も受けにくいのが特徴です。

実装方法

Foreground Serviceは通常のServiceとは少し実装方法が異なります。必要になるのが「通知の実装」と「startForegroundメソッドの呼び出し」、「パーミッションの追加」です。あとは通常のServiceの実装の流れと変わりません。

通常のService実装の流れ

  1. Serviceを継承したクラスの作成
  2. マニフェストファイルへServiceを追記
  3. 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)
}

実行すると以下のように通知欄にサービスが実行していることが通知されます。これはサービスが動作している間表示されます。

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

アプリをキルしても処理が継続する

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秒ごとに実行
        }
    }

    // 〜〜〜〜〜〜〜〜
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index