2

For an Android app I have some content that I want to show with a delay. Therefore I'm using a handler.

private lateinit var mHandler: Handler

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

    linearLayout.isVisible = false

    mHandler = Handler()
    mHandler.postDelayed({
        linearLayout.isVisible = true 
    }, 1000)
}

How can I save this state (for example in case of rotating the app)? And is it better to use SharedPreferences or something like this:

override fun onSaveInstanceState(outState: Bundle?) {
    super.onSaveInstanceState(outState)

}

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    super.onRestoreInstanceState(savedInstanceState)

}
Bryan
  • 14,756
  • 10
  • 70
  • 125
Vadim
  • 51
  • 11
  • It depends. if you just want to retain state for rotation, `savedInstanceState` is fine. but if this state is just one time state and for consecutive app launches you want to retain state, go for shared prefs. – Vivart Jul 29 '19 at 16:20
  • If you want to 'indefinitely' persist very small data, use shared preferences, else find other ways, which include `onSaveInstance()` – Taseer Jul 29 '19 at 16:27
  • For _tasks_ (as opposed to _state_), the recommended way to "save" them across rotation is to use a retained fragment: https://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html (yes, this is still true six years later). The Android Architecture Components use `ViewModel` for this: https://developer.android.com/reference/android/arch/lifecycle/ViewModel ... These are just retained fragments under the hood! – Ben P. Jul 29 '19 at 19:41

1 Answers1

0

I would recommend using the newer ViewModel component to tackle something like this. The ViewModel doesn't get destroyed on configuration changes like activities and fragments do, so you can run your Handler (or Timer) without fear of losing its state.

class MainViewModel : ViewModel() {
    private val layoutVisibility: MutableLiveData<Boolean> by lazy {
        MutableLiveData().also {
            delayVisibility()
        }
    }

    private fun delayVisibility() {
        Timer().schedule(1000) {
            layoutVisibility.postValue(true)
        }
    }
}

class MainActivity : AppCompatActivity() {
    private lateinit var model: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        model = ViewModelProviders.of(this)[MainViewModel::class.java]

        linearLayout.isVisible = false

        model.layoutVisibility.observe(this, Observer<Boolean> { visibility ->
            linearLayout.isVisible = visibility == true
        })
    }
}

You could save the state of the handler—by saving the start time and calculating how much time has elapsed when the activity is recreated—but the ViewModel architecture seems more intuitive to me.

class MainActivity : AppCompatActivity() {
    private lateinit var startTime: Long

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

        linearLayout.isVisible = false

        var elapsedTime: Long = 0L    
        if (savedInstanceState != null) {
            startTime = savedInstanceState.getLong(KEY_START_TIME, System.currentTimeMillis())
            elapsedTime = System.currentTimeMillis() - startTime
        } else {
            startTime = System.currentTimeMillis()
        }

        if (elapsedTime >= 1000) {
            linearLayout.isVisible = true
        } else {
            Handler().postDelayed({
                linearLayout.isVisible = true
            }, 1000 - elapsedTime)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putLong(KEY_START_TIME, startTime)
    }

    companion object {
        private const val KEY_START_TIME = "start_time"
    }
}
Bryan
  • 14,756
  • 10
  • 70
  • 125
  • 1
    Thank you! I tried the `ViewModel` and it worked perfectly fine! Just had to implement these two dependencies to my build.gradle: `implementation "android.arch.lifecycle:extensions:1.1.1"` and `implementation "android.arch.lifecycle:viewmodel:1.1.1"` :) – Vadim Jul 31 '19 at 11:03