Home > OS >  How Can I Save A Value From Runnables In Kotlin?
How Can I Save A Value From Runnables In Kotlin?

Time:08-16

lateinit var sharedPreferences : SharedPreferences
var number = 0
var runnable : Runnable = Runnable{ }
var handler : Handler = Handler(Looper.getMainLooper())

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    sharedPreferences = this.getSharedPreferences("com.mycompany.runnables", MODE_PRIVATE)
    val recordFromSP = sharedPreferences.getInt("record",-1)
    textView2.text = "Last Time Record: $recordFromSP"

}


fun start(view:View){
        number = 0
        runnable = object : Runnable{
            override fun run() {
                number = number 1
                textView.text = "Time: $number"
                button3.setEnabled(false)
                button4.setEnabled(true)
                handler.postDelayed(this,1000)
                var record = number

            }
        }
        handler.post(runnable)
}

fun stop(view:View){

    handler.removeCallbacks(runnable)
    number = 0
    textView.text = "Time: 0"
    Toast.makeText(this,"Stopped",Toast.LENGTH_LONG).show()
    button4.setEnabled(false)
    button3.setEnabled(true)
    ***sharedPreferences.edit().putInt("record",record)***

}

How can I get the last value from the timer(from runnable in start() function) I can't reach the record value from anywhere outside runnable function of course. So it gives an error in "sharedPreferences.edit().putInt("record",record)" How can I take the value?

CodePudding user response:

You can't use it in such a way since the var record is a local variable of another method inside another anonymous class. So you need to declare the var record as a variable of your main Activity class (where you already have var number and others).

CodePudding user response:

you need something ina scope thats accessible from both start and stop function

i would suggest to declare record in the encloding class/file maybe like a private plateinit var

then yo can check if it is initialized (aka it ran at least once) before accessing it

or you can make it a plain var initialized to a default value if you don't care about that

private lateinit var record: Int

fun start() {
    number = 0
    runnable = object : Runnable{
        override fun run() {
            number = number 1
            textView.text = "Time: $number"
            button3.setEnabled(false)
            button4.setEnabled(true)
            handler.postDelayed(this,1000)
            record = number // here it is assigned
        }
    }
    handler.post(runnable)
}

fun stop(view:View){

    handler.removeCallbacks(runnable)
    number = 0
    textView.text = "Time: 0"
    Toast.makeText(this,"Stopped",Toast.LENGTH_LONG).show()
    button4.setEnabled(false)
    button3.setEnabled(true)
// TODO: use if(this::record.isInitialized) to prevent exceptions
    sharedPreferences.edit().putInt("record",record)

}

CodePudding user response:

The simple solution here is to just store number (which is what record is set to) before you clear it:

// can't access 'record' from outside the runnable (or even outside the 'run' function),
// but it's the same value as 'number', which you can read!
var record = number
fun stop(view:View){
    // immediately stop any more updates from happening
    handler.removeCallbacks(runnable)
    // the runnable has been incrementing 'number' (and referring to it as 'record'),
    // so we just need to store whatever value that's reached
    sharedPreferences.edit().putInt("record", number)

    // now we've stored it, we can clean up and reset everything
    number = 0
    textView.text = "Time: 0"
    Toast.makeText(this,"Stopped",Toast.LENGTH_LONG).show()
    button4.setEnabled(false)
    button3.setEnabled(true)
}

As a more general case, when you're declaring anonymous objects, then any properties you add to them (like a top-level record property, as opposed to the local variable you were creating in run()) will only be visible inside the function that created it:

// storing the Runnable where everything can access it
lateinit var myRunnable: Runnable

fun start() {
    // creating a Runnable stored at the top level, with an extra property
    myRunnable = object : Runnable {
        // adding a public property
        var record = 0
        override fun run() { ... }
    }

    // you can access 'record' here because the object was declared in this function's
    // scope, so the compiler -knows- it has that 'record' property
    println(myRunnable.record)
}

fun stop() {
    // you can't do this here, because this function only knows 'myRunnable's -declared type-.
    // It doesn't know it has a 'record' property, only that it has a 'run()' function
    // (since it's a Runnable)
    println(myRunnable.record)
}

So you'll have to create an actual class with that property defined on it, so that everything interacting with that object knows about record:

abstract class RecordRunnable : Runnable {
    abstract var record: Int
}

lateinit var myRunnable: RecordRunnable

fun start() {
    // creating a Runnable stored at the top level, with an extra property
    myRunnable = object : RecordRunnable() {
        // override since it's a property declared on the type
        override var record = 0
        override fun run() { ... }
    }
}

fun stop() {
    // now this works because 'record' is a property on the declared type, RecordRunnable
    println(myRunnable.record)
}

You could also create a single top-level object declaration instead of creating a class (if you want to reuse the same singleton Runnable instead of creating new instances of it). You can read more about this stuff here!

  • Related