AndroidStudio: サービスの使い方 （サービスが3つ)

Android でこんなことを行うには、どうするかという話です。

## 仕様

３つのサービスを動かす例です。
Linux の　クローンで記述するとこうなります。

```text
* * * * * date >> /tmp/t101.txt
*/2 * * * * date >> /tmp/t102.txt
*/3 * * * * date >> /tmp/t103.txt
```

次のファイルが作成されます。

```text:/tmp/t101.txt
2023年 11月 21日 火曜日 12:13:01 JST
2023年 11月 21日 火曜日 12:14:01 JST
2023年 11月 21日 火曜日 12:15:01 JST
```

```text:/tmp/t102.txt
2023年 11月 21日 火曜日 12:12:13 JST
2023年 11月 21日 火曜日 12:14:01 JST
2023年 11月 21日 火曜日 12:16:01 JST
```

```text:/tmp/t103.txt
2023年 11月 21日 火曜日 12:12:13 JST
2023年 11月 21日 火曜日 12:15:01 JST
2023年 11月 21日 火曜日 12:18:01 JST
```

## 実装

```xml:AndroidManifest.xml
（省略）
 <application
 （省略）
<service android:name=".ServiceA" />
<service android:name=".ServiceB" />
<service android:name=".ServiceC" />
    </application>
</manifest>
```

```MainActivity.kt
package org.ekzemplaro

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(TAG,"*** MainActivity *** start ***")
        // ServiceAを起動
        startService(Intent(this, ServiceA::class.java))
        Log.d(TAG,"*** MainActivity *** cccc ***")
        // ServiceBを起動
        startService(Intent(this, ServiceB::class.java))

        // ServiceCを起動
        startService(Intent(this, ServiceC::class.java))
    }
}
```

```ServiceA.kt
package org.ekzemplaro

import android.app.Service
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.IBinder
import android.util.Log

import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale


private const val TAG = "ServiceA"
class ServiceA : Service()  {
private val handler = Handler(Looper.getMainLooper())
    private lateinit var runnable: Runnable

        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            Log.d(TAG,"*** ServiceA *** start ***")
handler.postDelayed(object : Runnable {
            override fun run() {
            val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
            val currentDate = sdf.format(Date())

        writeToTempFile("t101.txt",currentDate)
            Log.d(TAG,"*** ServiceA *** bbbb ***")
            handler.postDelayed(this, 60 * 1000) // 1分ごとに実行
            }
        }, 0)

        return START_STICKY
    }

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

    private fun writeToTempFile(fileName: String,currentDate: String) {
        Log.d(TAG,"*** ServiceA *** writeToTempFile *** PM 17:27")

        try {
            val context = applicationContext
            val file = File(context.filesDir, fileName)
            val writer = FileWriter(file, true)
            writer.append("$currentDate\n")
            writer.close()
        } catch (e: Exception) {
            Log.e("ServiceA", "Error writing to file: $e")
        }
    }
}
```

```ServiceB.kt
package org.ekzemplaro

import android.app.Service
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.IBinder
import android.util.Log
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

private const val TAG = "ServiceB"
class ServiceB : Service()  {

    private val handler = Handler(Looper.getMainLooper())
    private lateinit var runnable: Runnable

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // クローンと同等の動作
        Log.d(TAG,"*** ServiceB *** start ***")
        handler.postDelayed(object : Runnable {
            override fun run() {

        val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
        val currentDate = sdf.format(Date())
        writeToTempFile("t102.txt",currentDate)
                handler.postDelayed(this, 2 * 60 * 1000) // 2分ごとに実行
            }
        }, 0)
        return START_STICKY
    }

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

    private fun writeToTempFile(fileName: String,currentDate: String) {
        Log.d(TAG,"*** ServiceB *** writeToTempFile *** PM 17:32")

        try {
            val context = applicationContext
            val file = File(context.filesDir, fileName)
            val writer = FileWriter(file, true)
            writer.append("$currentDate\n")
            writer.close()
        } catch (e: Exception) {
            Log.e("ServiceA", "Error writing to file: $e")
        }
    }

}
```

```ServiceC.kt
package org.ekzemplaro

import android.app.Service
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.IBinder
import android.util.Log
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

private const val TAG = "ServiceC"
class ServiceC : Service()  {
    private val handler = Handler(Looper.getMainLooper())
    private lateinit var runnable: Runnable


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // クローンと同等の動作
        Log.d(TAG,"*** ServiceC *** start ***")
        handler.postDelayed(object : Runnable {
            override fun run() {
        val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
        val currentDate = sdf.format(Date())
        writeToTempFile("t103.txt",currentDate)
                handler.postDelayed(this, 3 * 60 * 1000) // 3分ごとに実行
            }
        }, 0)

        return START_STICKY
    }

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

    private fun writeToTempFile(fileName: String,currentDate: String) {
        Log.d(TAG,"*** ServiceC *** writeToTempFile *** PM 17:34")

        try {
            val context = applicationContext
            val file = File(context.filesDir, fileName)
            val writer = FileWriter(file, true)
            writer.append("$currentDate\n")
            writer.close()
        } catch (e: Exception) {
            Log.e("ServiceA", "Error writing to file: $e")
        }
    }


}
```

## エミュレーターで実行して確認

```bash
adb -s emulator-5554 shell
```

```text
$ adb -s emulator-5554 shell
emu64xa:/ $ su
emu64xa:/ # cd /data/data/org.ekzemplaro/files
emu64xa:/data/data/org.ekzemplaro/files # ls
t101.txt  t102.txt  t103.txt
```

## USB 接続した実機で確認

```text
 ./adb devices
List of devices attached
123456789A	device
emulator-5554	device
```

```text
$ ./adb -s 123456789A shell
rk322x_box:/ $ su
rk322x_box:/ # cd /data/data/org.ekzemplaro/files
rk322x_box:/data/data/org.ekzemplaro/files # ls
t101.txt t102.txt t103.txt
```