NullPointerException on this.getPreferences() - android

class PlayerDetails : AppCompatActivity(), View.OnClickListener, TextWatcher {
val sharedPref = this.getPreferences(Context.MODE_PRIVATE) // NPE
var applicationID = sharedPref.getString("applicationID", null)
private lateinit var binding: ActivityPlayerDetailsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_player_details)
...
}
...
I'm attempting to create a reference to the default Shared Preferences file of my activity by called this.getPreferences().
The results is giving me a NullPointerException - why is this? It's not associated with the UI so it doesn't need to be called after setContentView(). Why would the activity be null at this point?

It's not associated with the UI so it doesn't need to be called after
setContentView()
Right, but it must be called after:
super.onCreate(savedInstanceState)
because this refers to the activity's Context and this Context is only valid after this call.
So what you can do is change the declaration to:
val sharedPref: SharedPreferences? by lazy { this.getPreferences(Context.MODE_PRIVATE) }
this way sharedPref will be initialized the first time it will be used, after the call to super.onCreate(savedInstanceState).
Read more about lazy.

The safest place to call this is after super.onCreate(savedInstnaceState).

Move the line inside onCreate after super.onCreate
val PREFS_NAME = "com.teamtreehouse.colorsarefun.prefs"
var prefs: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
prefs = = this.getSharedPreferences(PREFS_FILENAME, 0)
binding = DataBindingUtil.setContentView(this, R.layout.activity_player_details)
...
}

By doing this.getPreferences(Context.MODE_PRIVATE) you called inherit method from AppCompatActivity , Since onCreate is the first callback when activity launched , so things are not set up yet.

Related

Without late initialization, why can't we declare a top-level property in kotlin by onCreate?

Why isn't this possible?
Declaring a top level property without using late initialization technique and assigning value to that variable in OnCreate(Bundle?)..
class MainActivity : AppCompatActivity() {
private var variable : Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
variable = 10
}
}
You can do it but then you need init block
class MainActivity : AppCompatActivity() {
private var variable : Int
init {
variable=10
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
variable = 10
}
}
First of all you should know that in Kotlin, when we declare a variable we have to initialize it with a value or we need to assign null.
But if we don’t want to initialize the variable with null or any value. Rather we want to initialize it in future with a valid value, then we have to use lateinit. Actually it is a promise to compiler that the value will be initialized in future..
But what you want to declare variable without lateinit ?
Then game changer lazy keyword will come.
but there is one drawback and that is Every lazy property is a val (immutable) property and cannot be changed once assigned.
so is there is certain condition that your value is fix and don't want to change it the n use lazy keyword.
otherwise you should initialize any default value without use of lateinit keyword.
like , private var number : Int = 0 or private var name : String = ""
and the you can change it in onCreate() too.

How to pass data from onCreate() to onSaveInstanceState()?

I have an onCreate() function and an onSaveInstanceState() function in my Main Activity. I have declared a Map in my onCreate() and I want to access the keys of that Map in my onSaveInstanceState() function, so as to save them to the outState bundle.
class MainActivity: AppCombatActivity(){
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//Want to access the iconMap here, but it is outside of the Map's scope. How do I access it?
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val iconMap = mapOf("contactlessIcon" to getDrawable(R.drawable.ic_contactless_24px), "fingerprintIcon" to getDrawable(R.drawable.ic_fingerprint_black_48dp), "codeIcon" to getDrawable(R.drawable.ic_code_24px))
}
}
I'm fairly new to Android programming, so this might be an easy fix. I want to access the iconMap in the onSaveInstanceState() but it is outside iconMap's scope. I cannot make iconMap a global variable, for this crashes my app.
As #ianhanniballake commented - you shouldn't be saving Drawables that are always the same. In this case it's especially unnecessary since you're onCreate will be called again whenever the activity is restored.
But, to answer your question, you can do it like this :
class MainActivity: AppCombatActivity(){
// define it here, so it's a member of the class
lateinit val iconMap: Map<String, Drawable>
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//now you can use it here
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//set its value
iconMap = mapOf("contactlessIcon" to getDrawable(R.drawable.ic_contactless_24px), "fingerprintIcon" to getDrawable(R.drawable.ic_fingerprint_black_48dp), "codeIcon" to getDrawable(R.drawable.ic_code_24px))
}
}

How to initialize ViewBinding?

How to initialize VievBinding? In AppCompactActivity version 1.0.0, it became possible to pass a layout to the parent constructor so that you would not write OnCreateView.
I do so, but then I want to use VievBinding, but it doesn’t work for me. Text is not displayed. What can be done?
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onStart() {
super.onStart()
var binding = ActivityMainBinding.inflate(layoutInflater)
binding.hello.text = "Hello"
}
}
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.bind(findViewById(Window.ID_ANDROID_CONTENT).getChildAt(0))
}
Although it's easier if you rely on __Binding.inflate().
class MainActivity : AppCompatActivity {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater, R.layout.activity_main)
setContentView(binding.root)
}
Layout inflation should not be in onStart. It goes in onCreate in Activity, and onCreateView in Fragment.
// Activity class
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
val binding:YourActivityLayoutBinding =
DataBindingUtil.setContentView(this, R.layout.your_activity_layout);
}
Layout views in xml should be surrounded by <layout> tag
<layout>
...// your activity view layout
</layout>
var binding = ActivityMainBinding.inflate(layoutInflater)
This will inflate the layout again and you will get a different instance of the views than what is already set in the activity
Try using the below code to bind the activity view to the binder. Where ROOT_VIEW_ID is view id of root view in your layout
var binding = ActivityMainBinding.bind(findViewById(ROOT_VIEW_ID))*
*binding.hello.text = "Hello"

Bypass Kotlin "Property getter or setter expected"

I need to create a private field and use it without access to it from other classes. But i can't instantiate it in a constructor.
I'm developing for Android, and a problematic field screenWakeLocker can`t be instantiated this way:
class MainActivity : AppCompatActivity() {
private val screenWakeLocker: PowerManager.WakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager)
.newWakeLock(PowerManager.FULL_WAKE_LOCK, "")
"System services not available to Activities before onCreate()"
So i instantiate it in OnCreate:
class MainActivity : AppCompatActivity() {
private var game: Game = Game()
private var screenWakeLocker?: PowerManager.WakeLock;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
screenWakeLocker = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.FULL_WAKE_LOCK, "")
}
override fun onResume(){
super.onResume()
screenWakeLocker.acquire()
}
and get a compilation error "Property getter or setter expected" at ?: PowerManager.WakeLock
How to make it unavailable to other classes and to use it?
You have two options:
option one, if you want to use "var" you can add lateinit
option two, you can use by lazy, where objects are initialised when it is needed
private lateinit var screenWakeLocker: PowerManager.WakeLock
private val screenWakeLockerTwo by lazy {(getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.FULL_WAKE_LOCK, "")}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
screenWakeLocker = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.FULL_WAKE_LOCK, "")
}`
I think option two is better.
Use lateinit keyword for lazy initialization.
private lateinit var screenWakeLocker: PowerManager.WakeLock

lateinit modifier is not allowed on primitive type properties in Kotlin

I am defining like a instance variable in kotlin and want to initialize it onCreate method of an activity.
var count: Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
count.inc()
}
Here I am getting a below error on count variable.
Property must be initialized or be abstract in Kotlin
Well, I read this thread Property must be initialized or be abstract and tried same but again I am getting a below error.
lateinit modifier is not allowed on primitive type properties
lateinit var count: Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
count.inc()
}
Is there any way to do this in Kotlin ?
There are several ways to resolve this issue.
You can Initialise it with default value (e.i 0 or -1 or whatever) and then initialise it whenever your logic says.
Or tell compiler that count will be initialised later in this code by using Delegates.notNull check notNull.
var count: Int by Delegates.notNull<Int>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// You can not call `Int.inc()` in onCreate()` function until `count` is initialised.
// count.inc()
// **initialise count**
}
And if you need count value on demand (if not necessary to initialise in onCreate), you can use lazy function. Use this only if you have an intensive (Some calculation/Inflating a layout etc) task that you want to do on demand, Not to just assign a value.
var count:Int by lazy {
// initialise
}
Now you can decide what to use.
I hope it helps.
There's no reason to leave it uninitialized. Just initialize it to 0 or -1.
lateinit is for non-null object references that can't easily be initialized in the class body definition.

Categories

Resources