Search code examples
androidkotlinsharedpreferences

RuntimeException: preferences has not been initialised


I searched among many similar SharedPreferences questions, but couldn't apply to my project. It's a RuntimeException which requires a SharedPreferences lateinit variable to be initialised.

Error Message:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.me.tabuild3/com.me.tabuild3.Tutorial}: kotlin.UninitializedPropertyAccessException: lateinit property mPref has not been initialized

(...)

Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mPreferences has not been initialized
    at com.me.tabuild3.Z3Preferences.<init>(Z3Preferences.kt:15)
    at com.me.tabuild3.Tutorial.onCreate(Tutorial.kt:15)

This is Tutorial.kt, which triggers the error:

class Tutorial : AppCompatActivity() {
   val binding by lazy {
       B00TutorialBinding.inflate(layoutInflater)
   }
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.b00_tutorial)
       
       val fragmentList = listOf(Z0A(), Z1B(), Z3Preferences()) // the 15th line is here
       val adapter = Z99FragmentAdapter(this)
       adapter.fragmentList = fragmentList

       binding.tutorialPager.adapter = adapter
       binding.tutorialPager.currentItem = 1
   }
}

This activity loads three fragments, and the third one, Z3Preferences(), edits SharedPreferences to modify things(At least I plan so).

This is Z3Preferences, which holds actual(?) error:

class Z3Preferences : Fragment() {
   lateinit var binding: F04PrefBinding
   private lateinit var mPref: SharedPreferences // seems this thing has to be initialised
   val preferencesEditor: SharedPreferences.Editor = mPref.edit() // same error message for this line when the upper solved(?)

   override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       binding = F04PrefBinding.inflate(inflater, container, false)
       return binding.root
   }

   override fun onResume() {
       super.onResume()
       binding.radioGroupTextSize.setOnCheckedChangeListener { group, checkedId ->
        when (checkedId) {
            R.id.textSize_RB1 -> preferencesEditor.putInt("TXTSZ", 12)
            R.id.textSize_RB2 -> preferencesEditor.putInt("TXTSZ", 14)
            R.id.textSize_RB3 -> preferencesEditor.putInt("TXTSZ", 16)
            R.id.textSize_RB4 -> preferencesEditor.putInt("TXTSZ", 18)
            R.id.textSize_RB5 -> preferencesEditor.putInt("TXTSZ", 20)
        }
    }
    binding.radioGroupTextSpeed.setOnCheckedChangeListener { group, checkedId ->
        when (checkedId) {
               R.id.textSpd_RB1 -> {
                   preferencesEditor.putInt("TXTSP", 160)
                   preferencesEditor.putBoolean("TXTAN", true)
               }
               R.id.textSpd_RB2 -> {
                   preferencesEditor.putInt("TXTSP", 120)
                   preferencesEditor.putBoolean("TXTAN", true)
               }
               R.id.textSpd_RB3 -> {
                   preferencesEditor.putInt("TXTSP", 80)
                   preferencesEditor.putBoolean("TXTAN", true)
               }
               R.id.textSpd_RB4 -> {
                   preferencesEditor.putInt("TXTSP", 40)
                   preferencesEditor.putBoolean("TXTAN", true)
               }
               R.id.textSpd_RB5 -> {
                   preferencesEditor.putInt("TXTSP", 40)
                   preferencesEditor.putBoolean("TXTAN", false)
               }
           }
       }
   }

   override fun onPause() {
       super.onPause()
       preferencesEditor.apply()
   }
}

So.. How do I initialise the lateinit var mPref? I'm totally lost at this point and similar questions have different app construction and I couldn't get the idea.


Solution

  • So.. How do I initialise the lateinit var mPref?

    You set it to some valid value, usually as early as possible. On Android, this usually means doing so in onCreate or onCreateView. But at least before anything tries to access it. You can do this in your onCreateView:

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       mPref = // get preferences in your prefered way
       binding = F04PrefBinding.inflate(inflater, container, false)
       return binding.root
    }
    

    it shoots the same error for the next line(I marked on the code in the post): "lateinit property mPref has not been initialized", but at the next line! How do I do this?

    You are trying to use an unintialized lateinit variable immediately after declaring it:

    private lateinit var mPref: SharedPreferences // Declared but not initialized
    val preferencesEditor: SharedPreferences.Editor = mPref.edit() // Immediately attemps to use mPref which is not initialized (during constructor call)
    

    You need to not try to initialize the editor immediately like that.

    You can either make it a property, so it only tries to access mPref when accessed and not during constructor call, which assumes it'll be valid by the time you try to access the editor:

    val preferencesEditor: SharedPreferences.Editor get() = mPref.edit()
    

    Or, you could make the editor itself a lateinit property and initialize it later:

    lateinit var preferencesEditor: SharedPreferences.Editor
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       mPref = // get preferences in your prefered way
       preferencesEditor = mPref.edit()
       binding = F04PrefBinding.inflate(inflater, container, false)
       return binding.root
    }
    

    Please check the documentation for how lateinit and kotlin constructors work.