android_タイマー_砂時計_service,activity foreground service サービス

 


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 を閉じてもタイマーは動き、

通知から制御できます! 🚀



コメント