Android - Foreground Service実行

By JS | Last updated: December 24, 2020

Android O(API 26)からBackground Serviceの実行が制限されてForegroundで実行する必要があります。

Activityが実行している場合、問題ないだろうが、BroadcastReceiverのようForegroundではない状態で、Background Serviceを実行させることはなくなりました。

こういうときは、サービスをForegroundで実行する必要があります。 Foregroundで実行させると、サービスが実行中であるコンテンツのNotificaitonが登録され、ユーザーが認知することができます。

この記事では、Foregroundでサービスを実行させる方法について説明します。

この記事で紹介されたSample AppはGitHub - StartForegroundServiceで確認することができます。 この記事の例では、Kotlinで作成されました。

Service定義

次のように MyServiceというサービスクラスを実装し、AndroidManifestに定義したと仮定します。

class MyService : Service() {
    override fun onCreate() {
        super.onCreate()
        Log.d("Test", "MyService is started")
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}
<application>
    ...

    <service android:name=".MyService" />

</application>

Foreground Service実行

Android O(API 26)未満のバージョンでは、 startService()でサービスを実行することができたが、Android O以上のデバイスでは、サービスがBackgroundで実行されるときに実行されていません。このとき startForegroundService()でサービスをForegroundで実行する必要があります。

したがって、次のようなサービスを実行することができます。

val intent = Intent(context, MyService::class.java)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(intent)
} else {
    context.startService(intent)
}

FOREGROUND_SERVICEパーミッション

Foregroundでサービスを実行するには、 FOREGROUND_SERVICEパーミッションをAndroidManifestに登録する必要があります。

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

startForegroundService()でNotification登録

startForegroundService()でサービスが実行されると、実行されたサービスは、5秒以内に startForeground()を呼び出して、サービスが実行中であるNotificaitonを登録する必要があります。もし呼び出さない場合、システムはサービスを強制的に終了させます。

Service.startForeground()は次のように Notificaiton IDNotificaitonオブジェクトを引数として受け取ります。 注意すべき点は、IDで0を渡すと、動作がないということです。 0以外の数字をIDで渡す必要があります。

public final void startForeground(int id, Notification notification)

次のようにNotificaiton ChannelとNotificationオブジェクトを作成し、 startForeground()を呼び出すことができます。

class MyService : Service() {
    companion object {
        const val NOTIFICATION_ID = 10
        const val CHANNEL_ID = "primary_notification_channel"
    }

    override fun onCreate() {
        super.onCreate()
        Log.d("Test", "MyService is started")

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel()
            val notification = NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("MyService is running")
                .setContentText("MyService is running")
                .build()
            Log.d("Test", "start foreground")
            startForeground(NOTIFICATION_ID, notification)
        }
    }

    private fun createNotificationChannel() {
        val notificationChannel = NotificationChannel(
            CHANNEL_ID,
            "MyApp notification",
            NotificationManager.IMPORTANCE_HIGH
        )
        notificationChannel.enableLights(true)
        notificationChannel.lightColor = Color.RED
        notificationChannel.enableVibration(true)
        notificationChannel.description = "AppApp Tests"

        val notificationManager = applicationContext.getSystemService(
                Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(
                notificationChannel)
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

Android O(API 26)以上のバージョンでNotificationを登録する前に、Notificaiton Channelを登録する必要があります。

テスト

サービスを実行してみると、サービスが実行され、次のようにNotifciatonが見えることを確認することができます。 start foreground service

Troubleshooting

FOREGROUND_SERVICE権限をアプリに追加しない場合は、次のようなエラーでアプリが終了します。

12-24 12:58:32.783  3688  3688 E AndroidRuntime: FATAL EXCEPTION: main
12-24 12:58:32.783  3688  3688 E AndroidRuntime: Process: com.example.app, PID: 3688
12-24 12:58:32.783  3688  3688 E AndroidRuntime: java.lang.RuntimeException: Unable to create service com.example.example.MyService: java.lang.SecurityException: Permission Denial: startForeground from pid=3688, uid=10153 requires android.permission.FOREGROUND_SERVICE

startForegroundService()の呼び出し後、実行されたサービスで、5秒以内に startForeground()を呼び出していない場合、システムによってサービスが終了します。

12-24 11:53:19.793   504   530 W ActivityManager: Bringing down service while still waiting for start foreground: ServiceRecord{21733d5 u0 com.example.app/.MyService}

12-24 11:53:19.912   504  3971 I ActivityManager: Crashing app skipping ANR: ProcessRecord{1bdf9c0 3660:com.example.app/u0a153} Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{21733d5 u0 com.example.app/.MyService}

参考

Related Posts

codechachaCopyright ©2019 codechacha