I want set my gorterilecekkart variable in code, but it will not change. It always sees the first value instead. How can I solve this problem? Please help me. I don't know English very well sorry. But in my code I have added comments.
class Sahnem : AppCompatActivity() {
var gorterilecekkart=5
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sahnem)
val parsUser = ParseUser.getCurrentUser().email
val queryTercih = ParseQuery.getQuery<ParseObject>("Tercih")
queryTercih.whereEqualTo("user", parsUser.toString())
queryTercih.findInBackground { objects, e ->
if (objects.isEmpty()){
}else{
var bas:String=objects[0].get("basla").toString()
var bit:String=objects[0].get("bitir").toString()
txvBasla.setText(bas)
txvBitir.setText(bit)
bitir=bit.toInt()
baslat=bas.toInt()
gorterilecekkart=bitir-baslat // i see log 3
}
println("116 "+bitir)
gorterilecekkart=bitir-baslat
println("119 "+gorterilecekkart) // i see log 3
}
println("124 "+gorterilecekkart) // why i dont see log 3, i see 5
It looks like ParseQuery#findInBackground(...) runs the callback lambda in a background thread. Documentation here.
This means that:
println("124 "+gorterilecekkart) // why i dont see log 3, i see 5
Actually gets executed first, before the lambda is done executing.
In order to fix this, you need to use ParseQuery#find(...) instead, or somehow find a way to wait until the background thread is done executing your lambda before checking the value of gorterilecekkart.
Related
In my android project I have tried to implement a shared View Model which does all the reading and writing data. I do this by using Mutable Live Data and my activity calls an update function within the View Model to update the Live Data. However I can't figure out how to get the data after it has been accessed. It seems that I am trying to update my UI before the data gets accessed. I have looked up this problem and it seems the solution has something to do with coroutines. I have not been successful implementing coroutines and I always get a null value for my data.
ViewModel :
private val firebaseDatabase: DatabaseReference = FirebaseDatabase.getInstance().reference
private val fAuth = FirebaseAuth.getInstance()
private val user: FirebaseUser = fAuth.currentUser!!
private var _saveLocation: MutableLiveData<LocationEvent> = MutableLiveData<LocationEvent>()
val saveLocation: LiveData<LocationEvent> get() = _saveLocation
fun loadData() {
firebaseDatabase.child("User").child(user.uid).child("SaveLocation").get()
.addOnSuccessListener {
_saveLocation.value = LocationEvent(
it.child("title").getValue<String>()!!,
it.child("organizer").getValue<String>()!!,
LatLng(
it.child("locationLatLng").child("latitude").value as Double,
it.child("locationLatLng").child("longitude").value as Double
),
it.child("address").getValue<String>()!!,
it.child("description").value as String,
it.child("allDay").value as Boolean,
it.child("sdate").getValue<Calendar>()!!,
it.child("edate").getValue<Calendar>()!!,
it.child("notifications").getValue<MutableList<Int>>()!!,
user.uid
)
}.addOnFailureListener {}
}
Activity function :
private fun loadSaveData() {
dataViewModel.loadData()
//using log statement just to see if any value
//Always get null
Log.d("MainFragment", "${dataViewModel.saveLocation.value}")
}
I did not include any attempt at coroutines above.
Question
How can I use coroutines to fix this problem?
If not coroutines than what?
(Side Question) : Why does casting to type Calendar cause a crash?
Any help whether its a solution or pointing me to a solution would be much appreciated.
Whenever you use code with names like "add listener" or "set listener" or ones with words like "fetch" or "async" in the name and take lambda parameters, you are calling an asynchronous function. This means the function returns before it finishes (and usually before it even starts) doing what you requested it to.
The purpose of the listener/callback/lambda function you pass to it is to do something sometime in the future, whenever the work eventually is completed. It could only be a few milliseconds in the future, but it absolutely will not happen until after your other code under the function call is complete.
In this case, your get() call to Firebase is synchronous, and you are adding a listener to it to tell it what to do with the results, when they eventually arrive. Then your flow of code continues on synchronously. Back in your loadSaveData() function, you are checking for the results, but the request and your listener have not been completed yet.
You don't need coroutines to get around this. Coroutines are a convenient syntax for dealing with code that normally uses callbacks, but regardless of whether you use coroutines, you need to understand what is going on. IO operations like what you're using cannot be done on the main thread, which is why they are done synchronously.
There's a lot more info about this in this StackOverflow question.
I'm writting my first app in Kotlin, so I'm pretty new to this. One of the functions it performs is to read something from an API, and then update the screen based on the result.
I have tried lots of things with the coroutines, but nothing seems to work.
For example, I have something like this:
private fun readAPI() {
runBlocking {
fun rAPI() = async {
val api = "..."
result = URL(api).readText()
}
println(tag, "Result: " + rAPI().await())
}
}
And lots of different approaches. Nothing seems to work. In the above case I'm getting an exception "android.os.NetworkOnMainThreadException".
The only thig that has worked so far, is something using OkHttp3 as described here: https://rstopup.com/como-hacer-una-solicitud-a-la-api-de-kotlin.html (it's in Spanish, but you'll get the idea), and this works, it brings the API response, I parse it, fill in my sqlite3 database and so on. But, since I don't know when the API ends, I can't update the screen controls. And if I try to do it serially, I get an exception that only the thread which started the activity is the one that can update the activity or something like that.
I've seen, and follow LOTS of tutorials that talks about suspend functions, launch, etc., and they try to mimick an API call with delay(), those tutorials work perfectly, until I try to do a real API call.
So, can you point me to a full example on how to call an API with Kotlin, and then update some screen elements?
EDIT
I'm editing changing the fun by val:
runBlocking {
val rAPI = async {
val api = "..."
URL(api).readText()
}
Log.w(tag, rAPI.await())
}
I got the "android.os.NetworkOnMainThreadException" exception.
Since you want to use coroutine-async ways, you must tell main thread to waiting for you. you need to use suspend function or block to do this.
GlobalScope.launch {
suspend {
Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
delay(10000)
withContext(Dispatchers.Main) {
Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
}
}.invoke()
}
result log
09:36:09.500 D/: #runs on DefaultDispatcher-worker-1
// 10 seconds later
09:36:19.678 D/: #runs on main
I think this should do the trick.
However I suggest you to understand how to use OkHttp/Volley by passing callbacks(with onSuccess and onFail or something) into that.
Or Retrofit2 with RxJavato handle many of these issues.
EDIT
For the Module with the Main dispatcher had failed to initialize error, replace the withContext() line with this
withContext(Handler(Looper.getMainLooper()).asCoroutineDispatcher())
EDIT
Now don't use RxJava, use liveData/LiveEvent to implement the observer pattern
In Kotlin you can convert callbacks to coroutines as described in this codelab https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#6
So I have this method that I want to use for logging while also returning the logged object:
inline fun <T> T.btwLog(prefix:String="")=apply { Timber.d("$prefix->${toString()}") }
The Timber class is from a logging Library and also (when set up this way) shows the line at which it was called, which is also why the method is inline (to show the line and correct class of where it was called)
For example:
val b = someThingComplex().btwLog()
This basically works, the only problem is that the line number is wrong. This basically has to be because Kotlin inserts the method and while doing so adds some lines to the class (4)?.
So the example from before probably looks similar to this:
val b = someThingComplex()
.apply {
Timber.d("$prefix->${toString()}")
}
An alternative is this method where Kotlin doesn't add additional lines:
inline fun Any.log(s:String="")= Timber.d(s+this)
Is there any way that I could force Kotlin to just inline it exactly as I wrote it and not add linebreaks and stuff? Or is there a better way that I could define the method in general?
Thanks.
I suspect that the shifting of line numbers might be due to the call to apply. Try it this way.
inline fun <T> T.btwLog(prefix: String = ""): T {
Timber.d(prefix + "->" + this)
return this
}
I am using Kotlin (for Android development) and I'm trying to pass a function to another function which I'd like to use as a callback. The code is very basic as this is just a test for now.
Please note that, although you will probably wonder why I'm using a callback like this, it's just for test purposes. In my actual application I would want to assign the callback to a value and call it later on once an asynchronous method has completed.
I cannot use co-routines etc... since this code will be used for a multi-platform solution, hence my interest in making a function callback.
My Kotlin Class that will receive the function (callback)
class SampleApi {
private var counter: Int = 0
fun startCounting(initialValue: Int, counterCallBack: (resultVal: Int) -> Unit) {
counter = initialValue
counter++
counterCallBack(counter)
}
}
The above is a basic class that has a function startCounting which will receive an integer and a function. It will then call that function and pass in a value.
The calling code
class MainActivity : AppCompatActivity() {
private val sampleApi: SampleApi = SampleApi()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sampleApi.startCounting(5, {counterCallBack()})
}
private fun counterCallBack(counter: Int) {
Toast.makeText(this, counter.toString(), Toast.LENGTH_SHORT).show()
}
}
The Sample code shown above contains the callback method (which is expecting to receive an integer), and contains the call to the startCounting method from the SampleApi class that is expecting to receive a function.
The problem I have is this line:
sampleApi.startCounting(5, {counterCallBack()})
The error within Android Studio is due to the fact that a value is that the function is expecting an integer and hence I receive the error:
No value passed for parameter 'counter'
I tried to look at lambdas but didn't think that was the issue. I have searched to see if an answer to this already existed and, whilst helpful they didn't seem to consider the same use case as mine.
Any help with this would be very much appreciated.
Because counterCallback has exactly the type you need, you can also use a function reference instead of a lambda:
sampleApi.startCounting(5, ::counterCallBack)
I assume what you want to do is create a toast displaying the Int every time your callback lamda is called from the SampleApi.
You just need to make use of the Int that your lamba is called with, using it:
sampleApi.startCounting(5, {counterCallBack(it)})
I have lateinit property in my Kotlin activity, this is simplified version of that:
class CreateNewListOfQuestions : AppCompatActivity() {
lateinit var questionAnswerListOfObjects: ArrayList<QuestionAnswerObject>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_new_list_of_questions)
save.setOnClickListener {
questionAnswerListOfObjects.add(0, QuestionAnswerObject("question", "answer", 1))
}
}
}
The main problem is that when I generate mobile app and press “Save” button app stops working.
Logcat shows this error: “lateinit property questionAnswerListOfObjects has not been initialized”
I was trying many ways to initialize it but these ways did not help to me. How should I properly initialize it?
I am plining to add to ArrayList many objects of this class:
class QuestionAnswerObject(var question: String, var answer: String, var probability: Int=100) {}
It depends what you want.
For example if everything you need is using ArrayList<QuestionAnswerObject> you dont need lateinit at all:
var questionAnswerListOfObjects = ArrayList<QuestionAnswerObject>()
would be enough
If you want to get from Bundle or from something else - you must initialize it before using.
Basically lateinit keyword is used only to say "hey, compiler, I have a variable, it is not initialized yet, but I promise it will be initialized before I use it, so please mark it as not nullable field".
So if you really want to use lateinit, just init that property earlier, for example add after setContentView
questionAnswerListOfObjects = ArrayList<QuestionAnswerObject>()