【Kotlin研修10日目】バックグラウンド処理の実装、サービスのライフサイクル

# バックグラウンド処理
`アクティビティ`から独立した`サービス`によって`メインスレッド`上で実行される、ユーザから**直接認識されず**、ユーザによって**操作を受けない**処理。

## サービス
`アクティビティ`から独立して`バックグラウンド処理`を実行する仕組み(=`コンポーネント`)。

### サービスのライフサイクル
`サービス`のライフサイクルは以下の通り。

`アクティビティ`の`startService()`メソッドによって`サービス`の初期化が行われ、自動的に`Active`状態へと移行する。

また、`アクティビティ`または`サービス自身`による`サービス`の終了時に`Inactive`状態へと遷移する。

なお、`Service`クラスを継承する`サービスクラス`では、以下のメソッドと抽象メソッドである`onBind()`メソッドを`オーバーライド`する際に、親(=`super`)である`Service`クラスのメソッドを**呼び出す必要がない**。

|ライフサイクル|メソッド|呼び出しタイミング|
|:--|:--|:--|
|`Initialized`|`onCreate()`|`サービス`の起動<br>=`アクティビティ`の`startService()`|
|`Active`|`onStartCommand()`|`サービス`の開始|
|`Inactive`|`onDestroy()`|`サービスの終了`<br>=`アクティビティ`の`stopService()`<br>または<br>`サービスクラス`の`stopSelf()`|

![サービスのライフサイクル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/0dffc147-43f6-7fba-9013-3494f0fac4c0.png)
[出典](https://www.javatpoint.com/android-service-tutorial)

# バックグラウンド処理の実装
`バックグラウンド処理`を実装する手順は、以下の通り。

> 1. `Service`クラスを継承する`サービスクラス`の作成
> 2. `マニフェストファイル`(=`AndroidManifest.xml`)に、1.で作成したサービスを登録
> 3. `サービスクラス`に`バックグラウンド処理`を記述
> 4. `アクティビティ`から`サービス`を起動
> 5. `アクティビティ`または`サービスクラス`内部から`サービス`を終了

## サービスクラスの作成
`Service`クラスを継承する`サービスクラス`は、`プロジェクト`に`Service`ファイルを新規作成して記述する。

### サンプルコード

```kotlin:SoundManageService.kt
class SoundManageService : Service() {
    // Serviceクラスの抽象メソッドの実装(必須)
    // -> サービスをバインドして実行する場合はブロック内{...}に処理を記述
    override fun onBind(intent: Intent): IBinder {}

    // サービスの初期化時に実行する処理
    override fun onCreate() {
        ...
    }

    // サービスの実行開始時に行う処理
    // -> サービスの強制終了時の処理を表すServiceクラス定数を返却
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        ...
        return <Serviceクラス定数>
    }

    // サービスの終了時に実行する処理
    override fun onDestroy() {
        ...
    }
}
```

## マニフェストファイルへのサービスの登録
`マニフェストファイル`(=`AndroidManifest.xml`)に、`service`タグを記述して`サービス`を登録する。
※`Android Studio`では`Service`ファイルの生成と同時に自動的に追記される

### serviceタグの属性

|属性名|ウィザード入力欄|内容|
|:--|:--|:--|
|`android:name`|Class Name|サービスクラス名|
|`android:enabled`|Exported|利用可否<br>`true`: 利用可能<br>`false`: 利用不可|
|`android:exported`|Enabled|外部アプリからの利用可否<br>`true`: **外部**から利用可能<br>`false`: **内部のみ**利用可能|

### サンプルコード

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application ...>
        <service
            android:name=".SoundManageService"
            android:enabled="true"
            android:exported="false">
        </service>
        ...
    </application>
</manifest>
```

## バックグラウンド処理の記述
実行する`バックグラウンド処理`は、`サービス`の開始時に呼び出される`onStartCommand()`のブロック`{...}`内に記述する。

また、`onStartCommand()`メソッドは、返り値として「`サービス`が**強制終了**した場合の`サービス`のリカバリ方法」を表す`Service`クラス定数が必要となる。

### Serviceクラス定数

|定数|内容|
|:--|:--|
|`START_NOT_STICKY`|自動で再起動しない<br>※常駐必須のサービスでない場合に推奨|
|`START_STICKY`|自動で再起動する<br>`インテント`は`null`|
|`START_REDELIVER_INTENT`|自動で再起動する<br>`インテント`は直前に保持していた値|

### サンプルコード

```kotlin:SoundManageService.kt
class SoundManageService : Service() {
    ...
    // サービスの実行開始時に行う処理
    // -> サービスの強制終了時の処理を表すServiceクラス定数を返却
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        ...  // バックグラウンド処理を記述

        // Serviceクラス定数の返却
        return START_NOT_STICKY
    }
    ...
}
```

## アクティビティによるサービスの起動
[参考1: インテントとインテントフィルタ](https://developer.android.com/guide/components/intents-filters?hl=ja)
[参考2: 研修3日目](https://qiita.com/b150005/items/f2c1d8236ac5e9770f4f#%E3%82%A4%E3%83%B3%E3%83%86%E3%83%B3%E3%83%88intent)
`アクティビティ`から`サービス`を起動する場合、`アクティビティ`と`サービスクラス`の橋渡し役となる`インテント`(=`Intent`オブジェクト)を生成し、`インテント`を通じて`サービス`を起動する。

### 定義

```kotlin
// Intentオブジェクトの生成
Intent(packageContext: Context!, cls: Class<*>!)
// パラメータ
// packageContext: 遷移元のアクティビティオブジェクト(=コンテキスト)
// cls: Javaクラス化した遷移先アクティビティ
// -> KotlinのクラスをJavaに変換する場合は、"<Kotlinクラス名>::class.java"と記述

// インテントを通じたサービスの起動
Context.startService(service: Intent!): ComponentName?
// パラメータ
// service: サービスを保持するIntentオブジェクト
```

### サンプルコード

```kotlin:MainActivity.kt
// Intentオブジェクト
val intent = Intent(this@MainActivity, SoundManageService::class.java)

// サービスの起動
startService(intent)
```

## サービスの終了
`サービス`を終了する方法は、以下の2通り。

> 1. `アクティビティ`(=`サービスクラス`**外部**)の`stopService()`
> 2. `サービス`自身(=`サービスクラス`**内部**)の`stopSelf()`

### 定義

```kotlin
// アクティビティ(サービスクラス外部)によるサービスの終了
Context.stopService(service: Intent!): Boolean
// パラメータ
// service: サービスを保持するIntentオブジェクト

// サービス自身(サービスクラス内部)によるサービスの終了
Service.stopSelf(): Unit
```

### サンプルコード

```kotlin:MainActivity.kt
// Intentオブジェクト
val intent = Intent(this@MainActivity, SoundManageService::class.java)

// アクティビティによるサービスの終了
stopService(intent)
```

```kotlin:SoundManageService.kt
// サービスクラス自身によるサービスの終了
stopSelf()
```