Why do I have to push the button twice to get the correct result?

Hello, I am so confused with this. I clicked the Start Button to run the service, and then I clicked Stop Button to stop the service and broadcast data back to MainActivity. The first click of the Stop Button resulted in the Toast only displayed null value, but when I clicked it again (without reclicking Start Button), it displayed the correct broadcasted text. Why? Please help. Thank you.

My codes:
in MainActivity.kt:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val start: Button = findViewById(R.id.startButton)
        val stop: Button = findViewById(R.id.stopButton)

        start.setOnClickListener {
            NewService.startService(this, "My application")
        }
        
        var testData: String? = null
        stop.setOnClickListener {

            val tokenPassingReceiver: BroadcastReceiver = object : BroadcastReceiver() {
                override fun onReceive(context: Context, intent: Intent) {
                    val bundle = intent.extras
                    if (bundle != null) {
                        if (bundle.containsKey("testData")) {
                            testData = bundle.getString("testData")!!
                        }
                    }

                }
            }
            NewService.stopService(this)
            LocalBroadcastManager.getInstance(this)
                .registerReceiver(tokenPassingReceiver, IntentFilter("dataBroadcast"))
            Toast.makeText(this, testData, Toast.LENGTH_SHORT).show()
        }
    }

}

in NewService.kt

.class NewService : Service() {
    private val CHANNEL_ID = "My Application"

    // declaring object of MediaPlayer
    private lateinit var alarmSound: Ringtone
    private lateinit var timer: CountDownTimer
    private lateinit var pendingIntent: PendingIntent
    private var alarmOn: Boolean = false
    private var timerOn: Boolean = false


    companion object {
        fun startService(context: Context, message: String) {
            val startIntent = Intent(context, NewService::class.java)
            startIntent.putExtra("inputExtra", message)
            ContextCompat.startForegroundService(context, startIntent)
        }

        fun stopService(context: Context) {
            val stopIntent = Intent(context, NewService::class.java)
            context.stopService(stopIntent)
        }
    }

    // execution of service will startz
    // on calling this method

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        //do heavy work on a background thread
        val input = intent?.getStringExtra("inputExtra")
        createNotificationChannel()
        val notificationIntent = Intent(this, MainActivity::class.java)
        pendingIntent = PendingIntent.getActivity(
            this,
            0, notificationIntent, 0
        )
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("My Application timer: ")
            .setContentText(input)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentIntent(pendingIntent)
            .setTicker(input)
            .setOnlyAlertOnce(true)
            .build()

        startForeground(1, notification)

        countDown(60000, this)
        return START_STICKY
    }

    // execution of the service will
    // stop on calling this method
    override fun onDestroy() {
        super.onDestroy()
        // stopping the process
        val intent = Intent("dataBroadcast")
        intent.putExtra("testData", "This string is passed from service to MainActivity")
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
        if (timerOn) {
            timer.cancel()
        }
        if (alarmOn) {
            if (alarmSound.isPlaying) {
                alarmSound.stop()
                alarmOn = false
            }
        }
    }

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

    private fun countDown(start: Long, context: Context) {
        timer = object : CountDownTimer(start, 1000) {
            override fun onTick(millisUntilFinished: Long) {
                var ms = millisUntilFinished + 1
                val notification = NotificationCompat.Builder(context, CHANNEL_ID)
                    .setContentTitle("My Application timer: ")
                    .setContentText("Countdown: " + (ms / 1000).toString())
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .setTicker("Countdown: " + (ms / 1000).toString())
                    .build()

                startForeground(1, notification)
            }

            override fun onFinish() {

                val alarm = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
                alarmSound = RingtoneManager.getRingtone(applicationContext, alarm)
                alarmSound.isLooping = true
                alarmSound.play()
                alarmOn = true
                val notification = NotificationCompat.Builder(context, CHANNEL_ID)
                    .setContentTitle("My Application timer: ")
                    .setContentText("Alarm on")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .setTicker("Alarm on")
                    .build()

                startForeground(1, notification)
            }
        }
        timerOn = true
        timer.start()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val serviceChannel = NotificationChannel(
                CHANNEL_ID, "Foreground Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            val manager = getSystemService(NotificationManager::class.java)
            manager!!.createNotificationChannel(serviceChannel)
        }
    }
}

Firstly, this question is more suited for other general-purpose forums because this forum is specifically for Kotlin questions.

Secondly, the issue is that you’re registering the tokenPassingReceiver only after you stop the service, and the thing is broadcast receivers don’t retroactively receive messages, and so instead if you just change these 2 lines:

NewService.stopService(this)
LocalBroadcastManager.getInstance(this)
    .registerReceiver(tokenPassingReceiver, IntentFilter("dataBroadcast"))

to this (i.e. flip them around):

LocalBroadcastManager.getInstance(this)
    .registerReceiver(tokenPassingReceiver, IntentFilter("dataBroadcast"))
NewService.stopService(this)

it should work flawlessly. Also instead I’d consider creating and registering the tokenPassingReceiver outside of the onClickListener i.e. like this:

var testData: String? = null
val tokenPassingReceiver: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val bundle = intent.extras
        if (bundle != null) {
            if (bundle.containsKey("testData")) {
                testData = bundle.getString("testData")!!
            }
        }
    }
}
LocalBroadcastManager.getInstance(this)
    .registerReceiver(tokenPassingReceiver, IntentFilter("dataBroadcast"))
stop.setOnClickListener {
    NewService.stopService(this)
    Toast.makeText(this, testData, Toast.LENGTH_SHORT).show()
}
1 Like

Thank you, it works perfectly. Snce I am quite new to Kotlin, things like this confuses me quite a lot. I am still learning. Thank you.

I am so sorry if this question is not well suited for this forum. To tell the truth I am rather confused as to which question goes to which forum. And because the forum description said:

Android
Developing Android applications with Kotlin.

So I thought, since I am using Kotlin and I am developing an Android application, this question should be ok to be placed here. But if I am violating the rules here, I am so sorry.

1 Like