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 ID
とNotificaiton
オブジェクトを引数として受け取ります。
注意すべき点は、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が見えることを確認することができます。
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
- Android - 振動、Vibrator、VibrationEffectの例
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信
- Android - FusedLocationProviderClientに位置情報を取得する
- Android - GPS、Network位置情報を取得する(LocationManager)
- Android - Foreground Service実行
- Android - 時間、日付、変更イベント受信
- Android - currentTimeMillis()、elapsedRealtime()、uptimeMillis()
- Android-PowerManager WakeLock
- Android - ファイル入出力の例(Read、Write、内部、外部ストレージ)
- Android - Screen On / Offイベントの受信、状態確認
- Android - 他のアプリのServiceにバインド
- Android - Handler vs Executor
- Android - Darkmode有効にする方法
- Android - hasSystemFeature()、サポートされているFeature確認
- Android - アプリの権限を確認(Permission check)
- Android - インストールされてアプリリストをインポートする
- Android App Shortcuts実装
- Android - ContentProviderを実装、および例
- Android - AIDLを利用して、Remote Serviceの実装
- Android - Uri、Scheme、SSP(Scheme Specific Part)説明
- Android - アプリのインストール、削除、イベントダウンロード(BroadcastReceiverインテントを受け取る)
- Android - SharedPreferencesに簡単なデータを保存する方法
- Android - AlarmManagerにアラームを登録する方法、および例
- Android - Quick SettingsにCustom Tile追加する方法(kotlin)
- Android - Broadcast Receiver登録およびイベントの受信方法
- Android - Runtime permissionリクエスト方法と例(kotlin)
- Android - ネットワーク(WIFI)の接続状態を確認し、変更の検出
- Mockito - static、final methodをmockingする方法
- Andriod - カスタムパーミッションを定義する方法
- RobolectricにUnit Testを作成する(kotlin)
- Android Mockitoのテストコードを作成する(kotlin)
- Android - Handlerの使用方法、および例
- Android - IntentService使用方法
- Android - JobIntentService使用方法