Android サービスの作成 (2022年1月 記事作成)

# Android サービスとは

Android端末でバックグラウンドで動作するアプリケーションです。

# Empty Projectの作成

Android Studioの[File] - [New] - [New Project] から [Empty Activity]を選択する。ただしEmpty Project以外でももちろんOKです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/c511f24a-7684-edc7-455a-5081712fa413.png">
Language: Kotlin以外は適当でOKです。

# サービスとして動作するクラスの作成

Android Studioの[File] - [New] - [Kotlin Class/File]を選択する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/5a0b1456-6e0e-2d02-d1a0-ab55f6754559.png">
クラス名を ServiceSample とした例で以下を進めていきます。以下のプログラムに入れ替えてください。

```kotlin
package jp.ac.nuis.nakada.myservice

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.ContentValues.TAG
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.annotation.RequiresApi
import java.util.*

// ServiceSampleクラスは、android.app.Serviceから派生させる。
class ServiceSample : Service() {
    // onBindメソッドをオーバライドし、nullを返す。
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
    
    // onStartCommandメソッドをオーバライドする。
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        
        // ここでサービスとして実行する非同期処理のメソッドを呼び出す。
        worker()

        // 通知を出力する。
        val channelID = TAG
        val notificationManager =
            getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        if (notificationManager.getNotificationChannel(channelID) == null) {
            val channel =
                NotificationChannel(channelID, "サービス起動中通知", NotificationManager.IMPORTANCE_HIGH)
            notificationManager.createNotificationChannel(channel)
        }
        val notification = Notification.Builder(applicationContext, channelID)
            .setSmallIcon((R.drawable.ic_launcher_background))
            .build()
        startForeground(1, notification)
        return START_NOT_STICKY
    }

    // サービスとして実行するメソッド
    private fun worker() {
        val timer = Timer()
        timer.schedule(object: TimerTask(){
            override fun run() {
                Log.d(TAG,"timeout")
            }
        },1000,1000)
    }
}
```

ServiceSampleクラスはandroid.app.Serviceクラスの派生となり、onBind、onStartCommandメソッドをオーバライドします。onBindではnullを返し、onStartCommandではサービスとして非同期処理を行うworkerメソッドを呼び出した後、サービスとして動作していることをユーザに通知します。この通知をしないとAndroidシステムにサービスが落とされるようです。


# AndroidManifest.xmlでサービスを作ることを宣言する

Android Studioの左側でAndroidManifest.xmlを選択し、uses-permissionタグ、Serviceタグを追加する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/ff02af5c-a369-643e-8486-c47f3e82094d.png">


uses-permissionタグの詳細です。

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

Serviceタグの詳細です。

```xml
<service android:name=".ServiceSample" android:foregroundServiceType="mediaProjection"
    android:exported="true">
</service>
```

uses-permissionではフォアグランドサービスとして実施することの許可を行い、serviceタグでサービスのクラス名を指定しておきます。


# MainActivityからサービスを起動する

```kotlin
package jp.ac.nuis.nakada.myservice

import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.annotation.RequiresApi

class MainActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // ServiceSampleクラスのサービスを起動する。
        startForegroundService(Intent(this,ServiceSample::class.java))
    }
}
```

このアプリでは、起動と当時にサービスをスタートさせます。




# 実行を確認する

サービスとして動作する処理は、ServiceSampleクラスの Worker メソッドに記述した以下のプログラムです。

```kotlin
// サービスとして実行するメソッド
private fun worker() {
    val timer = Timer()
    timer.schedule(object: TimerTask(){
        override fun run() {
            Log.d(TAG,"timeout")
        }
    },1000,1000)
}
```

1秒のディレイ後に、1秒ごとにLogcatに"timeout"と表示します。

このアプリケーションをAndroidで実行して、Android端末でアプリケーションを終了させたときに、"timeout"が表示され続ければサービスとして実行されています。



# サービスの終了方法

stopServiceメソッドによってサービスを終了させられるようですが、現在のプログラムではアプリの終了後にstopServiceを呼び出すところがないので、とりあえずAndroid Studioの終了ボタンで終了してください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/ff68986b-1cdd-cf72-a0db-2713de9e8a7b.png">



# 参考にさせて頂いたサイト

- <a href="https://developer.android.com/guide/components/services?hl=ja">Androidデベロッパー ドキュメント（公式） サービスの概要</a>
- <a href="https://qiita.com/naoi/items/03e76d10948fe0d45597">Foreground Serviceの基本</a>
- <a href="https://www.tuyano.com/index3?id=296001">Google androidプログラミング入門　サービスを作成する</a>
- <a href="https://akira-watson.com/android/service.html">[Android] Service の使い方</a>