androidアプリの作成についてお尋ねします。
Serviceクラスについてですが、
foreground serviceだと、
起動させると、通知欄に UIを表示して、
追加したボタンなどで そのサービスを
停止したり、再開したりなどの操作ができるそうですが、
あるActivityに
砂時計のような機能のタイマーの機能と
UIを実装させたい場合、
foreground serviceを組み込めば、
アプリがバックグラウンドにいても
そのタイマーを動かせるのでしょうか?
その場合の
serviceクラスと
activityクラス、というのは
コードというのはどのようなものになるのでしょうか?
↓
はい、Foreground Service を使えば、
アプリがバックグラウンドにあってもタイマーを動かし続けることができます。
さらに、通知を使って、ユーザーにタイマーの状態を表示し、
操作ボタンで制御することも可能です。
### **実装の概要**
1. **Foreground Service**:
- `CountDownTimer` や `Handler` を使ってタイマーを実装
- `Notification` を表示し、タイマーの残り時間を更新
- 停止や再開のアクションを追加
2. **Activity**:
- タイマーの状態を UI に表示
- サービスの開始、停止、リセットのボタンを配置
- `BroadcastReceiver` でサービスからのデータを受信し、UI を更新
---
## **Foreground Service の実装**
`TimerService.kt`
```kotlin
import android.app.*
import android.content.*
import android.os.*
import androidx.core.app.NotificationCompat
import java.util.*
class TimerService : Service() {
private var timer: CountDownTimer? = null
private val timerDuration = 10 * 1000L // 10秒
private var timeRemaining = timerDuration
private var isRunning = false
override fun onCreate() {
super.onCreate()
startForeground(
NOTIFICATION_ID,
createNotification("Timer Stopped"))
}
override fun onStartCommand(
intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
ACTION_START -> startTimer()
ACTION_STOP -> stopTimer()
ACTION_RESET -> resetTimer()
}
return START_STICKY
}
private fun startTimer() {
if (isRunning) return
isRunning = true
timer = object : CountDownTimer(timeRemaining, 1000) {
override fun onTick(millisUntilFinished: Long) {
timeRemaining = millisUntilFinished
updateNotification(
"Remaining: ${timeRemaining / 1000} sec")
sendUpdateToActivity(timeRemaining / 1000)
}
override fun onFinish() {
isRunning = false
updateNotification("Timer Finished")
sendUpdateToActivity(0)
}
}.start()
}
private fun stopTimer() {
timer?.cancel()
isRunning = false
updateNotification("Timer Stopped")
}
private fun resetTimer() {
stopTimer()
timeRemaining = timerDuration
updateNotification("Timer Reset")
sendUpdateToActivity(timeRemaining / 1000)
}
private fun sendUpdateToActivity(time: Long) {
val intent = Intent(ACTION_UPDATE)
intent.putExtra(EXTRA_TIME, time)
sendBroadcast(intent)
}
private fun createNotification(contentText: String): Notification {
val stopIntent = Intent(
this,
TimerService::class.java).apply { action = ACTION_STOP }
val resetIntent = Intent(
this,
TimerService::class.java).apply { action = ACTION_RESET }
val stopPendingIntent = PendingIntent.getService(
this, 0, stopIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val resetPendingIntent = PendingIntent.getService(
this, 1, resetIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Timer")
.setContentText(contentText)
.setSmallIcon(android.R.drawable.ic_media_play)
.addAction(
android.R.drawable.ic_delete,
"Stop", stopPendingIntent)
.addAction(
android.R.drawable.ic_media_next,
"Reset", resetPendingIntent)
.setOngoing(true)
.build()
}
private fun updateNotification(contentText: String) {
val notification = createNotification(contentText)
val notificationManager = getSystemService(
NotificationManager::class.java)
notificationManager.notify(
NOTIFICATION_ID, notification)
}
override fun onBind(intent: Intent?): IBinder? = null
companion object {
const val CHANNEL_ID = "TimerServiceChannel"
const val NOTIFICATION_ID = 1
const val ACTION_START = "ACTION_START"
const val ACTION_STOP = "ACTION_STOP"
const val ACTION_RESET = "ACTION_RESET"
const val ACTION_UPDATE = "ACTION_UPDATE"
const val EXTRA_TIME = "EXTRA_TIME"
}
}
```
---
## **MainActivity の実装**
`MainActivity.kt`
```kotlin
import android.content.*
import android.os.*
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var textView: TextView
private lateinit var startButton: Button
private lateinit var stopButton: Button
private lateinit var resetButton: Button
private val timerReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == TimerService.ACTION_UPDATE) {
val timeRemaining = intent
.getLongExtra(TimerService.EXTRA_TIME, 0)
textView.text = "Remaining: $timeRemaining sec"
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.timerTextView)
startButton = findViewById(R.id.startButton)
stopButton = findViewById(R.id.stopButton)
resetButton = findViewById(R.id.resetButton)
startButton.setOnClickListener {
sendCommandToService(TimerService
.ACTION_START) }
stopButton.setOnClickListener {
sendCommandToService(TimerService
.ACTION_STOP) }
resetButton.setOnClickListener {
sendCommandToService(TimerService
.ACTION_RESET) }
registerReceiver(
timerReceiver,
IntentFilter(TimerService.ACTION_UPDATE))
startForegroundService()
}
private fun sendCommandToService(action: String) {
val intent = Intent(this, TimerService::class.java).apply { this.action = action }
startService(intent)
}
private fun startForegroundService() {
val intent = Intent(this, TimerService::class.java)
startForegroundService(intent)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(timerReceiver)
}
}
```
---
## **レイアウト (`activity_main.xml`)**
```xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="20dp">
<TextView
android:id="@+id/timerTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remaining: 10 sec"
android:textSize="24sp"
android:padding="10dp"/>
<Button
android:id="@+id/startButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"/>
<Button
android:id="@+id/stopButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop"/>
<Button
android:id="@+id/resetButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset"/>
</LinearLayout>
```
---
### **補足**
- **サービスは `ForegroundService` として動作** し、
バックグラウンドでもタイマーが動き続ける。
- **通知上のボタンで制御可能**(停止・リセット)。
- **`BroadcastReceiver` を使って Activity の UI に反映**。
この実装で、Activity を閉じてもタイマーは動き、
通知から制御できます! 🚀
コメント
コメントを投稿