I have a problem with changing the properties of a button in Kotlin. That is only in one function, it works fine in all others. It is weird because I can change the properties of a TextView in that function. Here is the function:
override fun onResponse(call: Call, response: Response) {
val body = Klaxon().parse<API_result>(response.body!!.string())
//setting these textView texts is no problem
findViewById<TextView>(R.id.windSpeed).text = (body?.wind?.speed?.times(3.6)).toString() " km/h"
findViewById<TextView>(R.id.temperature).text = body?.main?.temp.toString() " C"
findViewById<TextView>(R.id.airPressure).text = body?.main?.pressure.toString() " hPa"
//changing button text is not working and crashes the app
findViewById<Button>(R.id.fillOutForm).text = "Test"
}
Here is the whole file:
class MainActivity : AppCompatActivity() {
lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private val client = OkHttpClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
fetchLocation()
}
private fun fetchLocation(){
val task = fusedLocationProviderClient.lastLocation
if(ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 101)
return
}
task.addOnSuccessListener {
if(it != null){
apiRequest(it.latitude, it.longitude)
}
}
}
private fun apiRequest(latitude: Double, longitude: Double){
var url = "https://api.openweathermap.org/data/2.5/weather?lat=" latitude "&lon=" longitude "&appid=...&units=metric&lang=en";
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
return
}
override fun onResponse(call: Call, response: Response) {
//code from above
}
})
}
}
data class API_result(
val main: main?,
val wind: wind?
)
data class main(
val temp: Double?,
val pressure: Int?
)
data class wind(
val speed: Double?
)
Before the app crashes, the button text gets updated correctly. If I remove the "button-line", the app works perfectly fine. I really don't know why that happens.
It seems like only just TextViews are working in this funciton, because I just also tested it with Password and Email (which are also Texts) and a Switch. It worked in non of these cases.
I also recognised that I can access the xml element (e.g. the button) in that function without any problem, but I just cant set the properties of it.
For example, I can do this
findViewById<TextView>(R.id.someText).text = findViewById<Button>(R.id.someButton).text
but not this
findViewById<Button>(R.id.someButton).text = "exampleText"
I hope you can help me! Thanks for your help in advance!
CodePudding user response:
I guess the issue here is that you are accessing the UI objects from a non UI thread and this is causing the issue. Accessing UI Element from a non UI Thread causes undefined behavior.
If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.
You can read more about it here
To fix this problem you need to access the UI objects from a UI thread as below. Also its not good to do findViewById<TextView>(R.id.someText)
inside the onResponse
as you are sure that the UI objects are always there. Instead initialize them inside the onCreate
and access the reference variable everywhere.
lifecycleScope.launch {
withContext(Dispatchers.Main){
textView.text = "sometext"
button.text = "sometext"
//and so on
}
}
if you don't have coroutines in your project
runOnUiThread(Runnable {
textView.text = "sometext"
button.text = "sometext"
//and so on
})
There is also a similar question and answer here