This question already has answers here:
How to convert String to Int in Kotlin?
(11 answers)
Closed 1 year ago.
I have an EditText which will take a number from the user and save it as sharedPreferences. I have two Buttons to load and save and a TextView to display a saved data. Everything works fine but if the user enters nothing and saves, the app crashes. Here's the Kotlin code,
override fun onClick(p0: View?) {
val sharedPreferences=getSharedPreferences("key", Context.MODE_PRIVATE)
when(p0!!.id){
//saves user data
R.id.btn_save->{
val userAge = Integer.parseInt(ageInput.text.toString()) //the problem is here
val editor=sharedPreferences.edit()
editor.putInt("age",userAge)
editor.apply()
}
//loads or displays user data
R.id.btn_load->{
val age=sharedPreferences.getInt("age",0)
showTv.text="Age: $age"
}
}
}
Use toIntOrNull() instead. Instead of throwing an exception on invalid input, it returns null. Then you can use the Elvis operator to provide the default to use when it's null.
val userAge = ageInput.text.toString().toIntOrNull() ?: 0
You should avoid the Java primitive wrapper classes like Integer when using Kotlin.
Related
I am experimenting around with Kotlin's sharedPreferences, however I cannot seem to get the updated value to stick.
val sharedPreferences = getSharedPreferences("Files", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putInt("numbers",1).apply()
val textview = findViewById<TextView>(R.id.textview)
textview.text = sharedPreferences.getInt("numbers",0).toString()
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
editor.putInt("numbers",2).apply()
textview.text = sharedPreferences.getInt("numbers",0).toString()
}
In the code above I set the initial value of the sharedPreference to 1, upon clicking the button the value will be updated to 2 and displayed.That works fine however when closing the app and reopening it, the value reverts to 1. Is there a way to permanatly keep the updated value?
You are setting it to that value every time you open the activity, since onCreate() is called every time it opens. You should check if the value is already set, and if it is, skip that line of code.
if ("numbers" !in sharedPreferences) {
val editor = sharedPreferences.edit()
editor.putInt("numbers",1).apply()
}
By the way, there is an extension function for editing without having to call apply and editor. repeatedly:
if ("numbers" !in sharedPreferences) {
sharedPreferences.edit {
putInt("numbers",1)
}
}
context: data binding with a ViewModel, which gets data from a remote source in the form of JSON. I want to display a textual value from that JSON in a TextView, but if the data is absent in the JSON, I want to fall back to a string defined in strings.xml.
android:text="#{viewModel.theText}"
How I currently solved it is with a custom binding adapter that accepts an Any, and checks if the value is an Int or String:
app:anyText="#{viewModel.theText}". The viewModel has something like val theText = json.nullableString ?: R.string.placeholder.
I'm guessing that this is a problem more people deal with, and I was hoping if someone knows a more elegant solution.
You could provide Application context to your ViewModel or Resources and then do something like this:
val theText = json.nullableString ?: resources.getString(R.string.placeholder)
The other option would be keep using binding adapter like you do but I would wrap text input in another object like this:
data class TextWrapper(
val text: String?,
#StringRes val default: Int
)
#BindingAdapter("anyText")
fun TextView.setAnyText(textWrapper: TextWrapper) {
text = textWrapper.text ?: context.getString(textWrapper.default)
}
val theText = TextWrapper(text = json.nullableString, default = R.string.placeholder)
You do not need an adapter to handle this use Null coalescing operator operator ?? in xml.
Try below code:
android:text="#{viewModel.theText?? #string/your_default_text}"
Use case :
The null coalescing operator (??) chooses the left operand if it isn't null or the right if the former is null.
P.S: lean more about DB and expressions here-> https://developer.android.com/topic/libraries/data-binding/expressions
This question already has answers here:
java.lang.IllegalArgumentException : Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull
(7 answers)
Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter convertView
(4 answers)
java.lang.IllegalArgumentException: Parameter specified as non-null is null for Kotlin and WebView
(3 answers)
Kotlin - IllegalArgumentException in overridden method
(1 answer)
Closed 2 years ago.
I have an app that fetchs data from API using retrofit with coroutines. (Deferred)
I´m trying to implement a Room cache database for this, but I´ve just tried to use LiveData to fetch the data from Repository (not success). Tried to .alowMainThreadQueries (not success).
The message is:
Attempt to invoke...... on a null object reference.
The main problem is: When i launch the App again, it fetches the data from the database and displays it correctly.
What am i doing wrong?
This is my DAO
#Dao
interface VarietiesDao{
#Query("select * from pokevariationsentity where id = :id")
fun getVariety(id: Int): LiveData<PokeVariationsEntity>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(pokes: PokeVariationsEntity) // will pass the pokes
}
private lateinit var INSTANCE : PokeVarietiesDatabase
fun getVarietiesDatabase(context: Context): PokeVarietiesDatabase {
synchronized(PokeVarietiesDatabase::class.java){
if(!::INSTANCE.isInitialized){
INSTANCE = Room.databaseBuilder(context.applicationContext,
PokeVarietiesDatabase::class.java,
"odaps2l.db").build() // name
}
}
return INSTANCE
}
Repo:
class VarietiesRepositoryImpl(private val database: PokeVarietiesDatabase, private val id: String) {
val varieties: LiveData<PokeVariety>
get() = Transformations.map(database.varietiesDao.getVariety(Integer.parseInt(id))){
it.asDomainModelFromVariations()
}
suspend fun refreshVarieties(id: String) {
withContext(Dispatchers.IO) {
val pokeList = PokeApi.retrofitService.getVariations(Integer.parseInt(id)).await()
database.varietiesDao.insertAll(pokeList.asDatabaseModel())
}
}
}
Actually i thought that the problem was the TypeConverter, but once it runs okay on the second time, that´s i realized that is nothing about it.
UPDATE
I just alowed .aloMainThreadQueries
and tried to do it as Sync mode. returning an Object instead of LiveData.
Surrounded the message Parameter specified as non-null is null: method
`kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter $this$asDomainModelFromVariations`
At the first time I click on the item, and the second time it Loads perfectly. The Same happens with livedata.
This question already has answers here:
Misbehavior when trying to store a string set using SharedPreferences
(6 answers)
Closed 4 years ago.
I save some string values in SharedPreference and it is not updating, where I do mistake?
I try to update SharedPreference value from Timer().
I tried to use commit() and apply() after updating SharedPreference.Editor value,but it does not update values.At every step of for loop I add new values to val protocols which is getting own value from SharedPreference
val sharedPreferences = activity!!.getSharedPreferences("session",Context.MODE_PRIVATE)
val protocols = sharedPreferences.getStringSet("protocols",hashSetOf())
Log.d("old protocols",protocols.toString())
Timer().scheduleAtFixedRate(object : TimerTask() {
override fun run() {
Query(context!!).post(url,params,headers,object:ResponseCallBack{
override fun onSuccess(response: String?) {
val res = response?.string()
val document = Jsoup.parse(res)
val bals = document.select("#newspaper-b tbody tr")
if(!protocols.containsAll(bals.eachText())) {
for (bal in bals) {
val bprotokol = bal.allElements[5].text()
if (!protocols.contains(bprotokol)) {
protocols.add(bprotokol)
notification()
}
}
val editor = sharedPreferences.edit()
editor.putStringSet("protocols", protocols)
editor.apply()
val updatedProtocols = sharedPreferences.getStringSet("protocols",null)
Log.d("updated protocols",updatedProtocols.toString())
}
}
})
}
}, 0, 5000)
First Log.d("old protocols") output is {protocols=[MMX6859280]} it is okay first time opening app. In the for loop there are two values MMX6859280 and MMX6859281.
The second Log.d("updated protocols") output is {protocols=[MMX6859280,MMX6859281]},it is also okay. But when close app and open again I expected first Log.d output {protocols=[MMX6859280,MMX6859281]} but it returns {protocols=[MMX6859280]},so it is not updating values. The strange situation is when I add another value to SharedPreference with this updating,I get result what I want,but the second time all is same.
Try this
sharedPreferences.edit().putStringSet("protocols", protocols).apply();
You are applying to reference variable
I've been using Kotlin to develop some apps in Android and what i want to do currently is to set a field value inside the defining class without calling the setter method.
Here is the code inside my class:
var projectList: List<Project>? = null
set(value) {
saveProjects(value as ArrayList<Project>)
field = value
}
//GO to the database and retrieve list of projects
fun loadProjects(callback: Project.OnProjectsLoadedListener) {
database.projectDao().getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ success ->
callback.onProjectsLoaded(success)
//Here i don't want to save the projects, because i've loaded them from the database
this.projectList = success
},
{ error -> GenericErrorHandler.handleError(error,callback.retrieveContext())}
)
}
Does anybody know a way to do this without calling the set(value) method?
You can only gain access to the field directly from the setter. Inside a setter, the field can be accessed through the invisible field variable.
There are perhaps some other ways around your requirements though. Here are 2 examples. You wouldn't have to follow them exactly, but could instead also combine them to make whatever solution you want.
You could use another shell property to act as the setter for your actual property:
class Example1 {
var fieldProperty: Int = 0
var setterPropertyForField: Int
get() = fieldProperty
set(value) {
fieldProperty = value
}
}
You could use setters as you actually would in Java with a JVM field and a set method. The #JvmField is probably not necessary.
class Example2 {
#JvmField var fieldProperty: Int = 0
fun setField(value: Int) {
fieldProperty = value
}
}
You could probably access the field and change it through reflection, but I don't recommend that approach. That would likely only lead to problems.