top of page
Rechercher

Two ways of saving and retrieving data when your screen rotates

  • Photo du rédacteur: Ruben Mim
    Ruben Mim
  • 1 juin 2020
  • 3 min de lecture

Dernière mise à jour : 2 juin 2020




When we rotate our device, and the orientation changes something messy happens : your activity get destroyed. You'll answer me "the device still shows it, are you blind ?!" and you will be right. What happen is the activity calls on create() again after rotation and puf ! It's still displayed ! But the current data you were manipulating may be erased. Annoying isn't it ?


In this tutorial we are going to look at two different ways to save and retrieve data when the screen has rotated. The first and old one will be using onSaveInstanceState() and onRestoreInstanceState() methods. The second one, I personally like to use is saving the data with a ViewModel.


Good old fashion





class MainActivity : AppCompatActivity() {

  
    // 1 we create some test variables 
    var test = "test"
    var savedTest = ""
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

     //2 we print the variable every time on create is called
        Log.i("test create", test )
        
     //3 we print the saved bundle if its not null
        Log.i("bundle saved on create", savedInstanceState?.getString("test") ?: "")


     //4 To modify our test variable we use an editText with its         
         listener ( for the example )
     
        editText.addTextChangedListener(object  : TextWatcher{

            override fun afterTextChanged(s: Editable?) {}

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {  }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            
       //5 On textChanged() we set the new value to ours  
          
                test = s.toString()
                
            }

        })

    }


    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        
        //5 Here we insert our value in the device memory as a bundle,         
            meaning using a key, value storage
            
        outState.putString("test", test)
    }


    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    
        //6 To retrieve the value AFTER ROTATION and after onStart(),               we use our bundle         
            package and look for the required to get the value. 
            " ?: "" " sets your value to "", to avoid any             
            nullPointerException if it doesn't exist.
            
        savedTest =  savedInstanceState.getString("test") ?: ""
        Log.i("saved test after start", savedTest)
        super.onRestoreInstanceState(savedInstanceState)
    }
    
    override fun onStart() {
    super.onStart()
    Log.i("start " ,"start")
}

   
}

In the console after rotation of your device you are supposed to get this result :

I/test create: test
I/bundle saved on create: lol
I/start: start
I/saved test after start: lol

Here we can deduce several things : first things first, the activity called onCreate() again, proofed with the "bundle saved on create" log. Then onRestoreInstanceState() is called after onStart(). Consequently, if you want to get the data before onStart() you don't need to use this method and save some lines of code.

Also if you paid attention to your console before the rotation, our bundle, being empty at our first run (not printed at all in the console) is now filled with the value gotten from the bundle package saved in onSaveInstanceState() , isn't it magic ?


The savedInstanceState parameter in the onCreate() is a reference to a Bundle object that is passed from onSaveInstanceState() .



The ViewModel way





To be able to take this path, we need a SaveStateHandle object so we are going to implement this in our gaddle file :

implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-alpha03"

Then of course we will create a ViewModel class with our brand new SaveStateHandle as a parameter :


//1 As we said earlier we set our implemented object as a parameter

class SaveViewModel(state : SavedStateHandle) : ViewModel(){

    companion object{
        private const val ID = "userID"
    }

    private val savedStateHandle = state
    
 //2 manage data mutation
    private val user_id : MutableLiveData<String> = savedStateHandle.getLiveData(ID)                


    fun saveCurrentString( userId :  String){
 //3 Sets a new value for the object associated to the key.
        savedStateHandle.set(ID, userId)
    }


    fun getCurrentUser() : String {
  //4 Gets the current value of the user id from the saved state 
           handle
        return savedStateHandle.get(ID)?: ""
    }


}


Now let's set up our Activity :


class MainActivity : AppCompatActivity() {

    var viewModel : SaveViewModel? = null
    var test = "test"
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//1 Instanciating our view model with the SavedStateViewModelFactory
         viewModel  = ViewModelProvider(this,   SavedStateViewModelFactory(application, this))
            .get(SaveViewModel::class.java)
            
//2 As we have done previously here we test our values in the onCreate 
    method
    
        Log.i("test create", test )
        Log.i("saved test create", viewModel!!.getCurrentUser())



        userid.addTextChangedListener(object  : TextWatcher{

            override fun afterTextChanged(s: Editable?) {

            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            
//3 We save the value through the view model            
                viewModel!!.saveCurrentString(s.toString())
                Log.i("test",s.toString())

            }

        })

    }

}

And here is the result in the console after rotation :

I/test create: test
I/saved test create: lol
I/start test: lol
I/test: lol

Here the main difference is, the saved value exists already in onCreate method after recreating the activity.


We can deduce our view model is not dying when the activity is rotating , WOW !







 
 
 

Comments


© 2023 par Fête à venir. Créé avec Wix.com

logo rub-05.png
bottom of page