I am developing an android app. I'm having a hard time writing code that adds a to-do list to a calendar. Let me show you the code first.
private fun addTodoList(entity: MyTodo) {
val start = Calendar.getInstance()
val end = start.clone() as Calendar
end.time = SimpleDateFormat("MM dd, yyyy", Locale.getDefault()).parse(Date.END_OF_DAY)!!
CoroutineScope(Dispatchers.Default).launch {
val id = MyDatabase.getInstance(mContext).getTodoDao().insert(entity)
val dao = MyDatabase.getInstance(mContext).getCalendarDao()
do {
val date = ymd.format(start.timeInMillis)
val item = CalendarEntity(date, id.toInt())
dao.insert(item)
start.add(Calendar.DAY_OF_MONTH, 1)
} while (start.timeInMillis <= end.timeInMillis)
}
}
This is the code that inserts the Todo created by the user through TodoDao, and inserts the value into CalendarDao from today to Date.END_OF_DAY (I randomly designated this value as "December 31, 2025"). But the insertion speed through CalendarDao was quite slow. There is no inconvenience in the UI, but if the user completely terminates the app from the task during insertion, it will be terminated without inserting data until the end. It has been said that using RxJava can solve this problem, how can you solve it? Even if it's not RxJava, if there is a way to solve this problem, please let me know.
Change Dispatchers.Default to Dispatchers.IO
This is a database operation and it should be handled on an IO thread, Default is a real heavy duty CPU thread.
Using RxJava is a good way to standardize your database interactions, it allows you to stay off the UI thread and setups up an Observer pattern to allow you to react to the data as you get it instead of waiting for it.
If they close the app while this operation is happening there is not much you can do, as this is handled under 1 transaction. As such the actual changes to the database are only handled at the end, meaning if the operation doesn't complete nothing gets inserted. But you also don't want to do this 1 transaction at a time because that is extremely slow. Its a reliability vs speed question.
Related
I've been playing with Firebase transactions with Android for some time and that thought crossed my mind.
When you run a transaction like this one the variable data gets assigned instantly using a synchronous call to transaction.get():
val db = Firebase.firestore
db.runTransaction { transaction ->
val ref = db.collection("example").document("exampledoc")
val ref2 = db.collection("example").document("exampledoc2")
val data = transaction.get(ref)
val data2 = transaction.get(ref2)
}
How can it get the value instantly? Does it request a copy of the entire database or of a portion of it before running the transaction and then instantly returns the values when they are requested while running it? If it requests a copy of a portion of the database, how does it know what portion to get before the requests are even made?
I looked for this everywhere and couldn't find anyone explaining how it works.
It's not really instant, but it is synchronous, and nothing is preloaded. Each call to get() inside a transaction requests that specific document to be fetched and returned on demand so you can work with its contents inside the transaction handler. If you have a slow network connection, you will more clearly observe the delay. Try adding some timing code - you will see that it is not single-digit milliseconds that you would observe from something that is truly instant from being in memory.
While you might expect the method to return a Task like other methods that read and write the database, that is just not the case in a transaction. The API assumes that you need the data immediately and hides the implementation detail of doing the request so that you don't have to worry about dealing with so many tasks.
You might be helped further by reading the source code.
I have a situation where I want to update an Activity's text fields as data comes in. The update only occurs when the simulation is completed, not while it is running (takes maybe 2 seconds to run).
Here is the code I have:
...
private var totalLoops = 0
private val updateDisplayTask = Runnable {
totalLoopsTV.text = totalLoops.toString()
totalEmailsSentTV.text = totalEmailsSent.toString()
totalPushesSentTV.text = totalPushesSent.toString()
private fun mainLoopFunction(currentTime: Long) {
...
totalLoops++
if(totalLoops % 20 == 0 || onDeckList.size == 0) {
Timber.w("UPDATING UI")
runOnUiThread(updateDisplayTask)
//handler.post(updateDisplayTask)
}
} //end of main loop
I've tried both runOnUiThread and handler/post as well as a few other things using Kotlin Coroutines, but nothing so far has worked. Can you see what I'm doing wrong here please? I see the logs of UPDATING UI so I know that the updates do get sent and I do see the last update (the only one I see) at the end.
Is this running on another thread, and then you run updateDisplayTask on the main thread? If you're updating totalLoops, totalEmailsSent and totalPushesSent on one thread (this worker thread) and reading them on another (main thread) then because of the way concurrency works, you might not actually see the new values on the main thread.
There are a few ways to manage synchronizing them, but if you're only writing the values on one thread (and you're not massively concerned about the possibility of some of the values changing partway through reading them, so they don't all match up) you can just use the #Volatile annotation on those variables to make them update across threads (works like the volatile keyword in Java).
If you care about atomic updates (everything changing together, can't read or write while something is in the middle of reading or writing them) you'll have to look into some kind of locking, like using synchronized methods and blocks. It's kind of a major (important!) subject, here's a post on it:
https://proandroiddev.com/synchronization-and-thread-safety-techniques-in-java-and-kotlin-f63506370e6d
so I've been looking at the coroutines for past few days and found quite interesting piece of code
val input = MutableStateFlow(5)
lifecycleScope.launch {
input.collectLatest {
val job = launch {
doSomething1()
delay(1000L)
doSomething2()
}
job.join()
}
}
I've been debugging it and im not sure why do we need that job.join in there, how I understand the whole flow right now:
Lets say we push fast 3 values to input, collectLatest will terminate previous unfinished box, but since we are launching new coroutine its job is not terminated, so after pushing fast 3 values we will always reach doSomething2 for each value, if we would skip launch inside collectLatest we would reach only one doSomething2 - but now coming to the job.join - it should suspend coroutine until the job is done, but since we are not doing anything else in this coroutine is it really needed? Does it give any value? From my tests it doesn't really matter if we have that job.join or not, what I am missing in here?
I'm trying to implement a simple chat application on web sockets in Clean Architecture. I had to choose a db for caching all information, so I decided to use Realm, because I heard it was pretty good database for any kind of mobile applications. But when I actually faced the Realm, it turned out to be really painful experience for me to implement caching logic with it.
All problems come from applying transaction to database which then must be synced on all threads working with Realm. There seems to some kind of synchronization problem with my code. For example, I want to save my object to Realm and then query it out of.
Here I have two simple functions to save and to get chat:
fun getBackgroundLooper(): Looper {
val handlerThread = HandlerThread("backgroundThread")
if (!handlerThread.isAlive)
handlerThread.start()
return handlerThread.looper
}
fun saveChat(chat: Chat): Completable {
val realmChat = ChatMapper.domainToCache(chat)
return Completable.create { e ->
val realm = Realm.getDefaultInstance()
realm.executeTransactionAsync({
it.insertOrUpdate(realmChat)
}, {
realm.close()
e.onComplete()
}, {
realm.close()
e.onError(it)
})
// Subscribe on background looper thread
// to be able to execute async transaction
}.subscribeOn(AndroidSchedulers.from(getBackgroundLooper()))
}
fun getSingleChat(chatId: String): Single<Chat> {
return Single.defer {
val realm = Realm.getDefaultInstance()
realm.isAutoRefresh = true
val realmChat = realm.where(RealmChat::class.java)
.equalTo("id", chatId).findFirstAsync()
if (realmChat.isValid) {
realmChat.load()
val chat = ChatMapper.cacheToDomain(realmChat)
realm.close()
Single.just(chat)
}
realm.close()
Single.error<Chat>(ChatNotExistException())
// Subscribe on background looper thread
// to be able to execute auto refreshing
}.subscribeOn(AndroidSchedulers.from(getBackgroundLooper()))
}
So, when I try to run simple code like this
remote.getChat().flatMap {
cache.saveChat(it) //save chat to realm
.andThen(cache.getSingleChat(it.id)) //then query it by id
}
I always get no matter of what ChatNotExistException, but if I try to run query again in another attempt or after restarting the application, then the chat object gets found
I also tried many different approaches to execute this code:
I tried to use realm.refresh() in getSingleChat or not use it at all.
I tried to query chat synchronously with findFirst() and findAll() instead of findFirstAsync().
I tried querying chat on current thread without .subscribeOn().
I tried to use realm.executeTransaction() instead of async transactions.
I tried to add thread sleep between saving and querying, so that transaction may take some time to get applied and I need to wait before attempting to query the chat
I'm begging anybody to explain me what am I doing wrong and how to make this code working. I can't change the architecture of my application and use Realm objects as my view models, I need to find solution in these conditions.
But when I actually faced the Realm, it turned out to be really painful experience for me to implement caching logic with it.
Reading the docs regarding best practices help. For example, the default idea is that you define a RealmResults using an async query on the UI thread, add a change listener to it, and observe the latest emission of the database.
There is no "caching" involved in that beyond saving to the database and observing the database. Any additional complexity is added by you and is completely optional.
All problems come from applying transaction to database which then must be synced on all threads working with Realm.
All looper threads automatically make the Realm auto-refresh, therefore if addChangeListener is used as intended in the docs, then there is no need for trickery, Realm will manage the synchronization between threads.
I want to save my object to Realm and then query it out of.
realm.executeTransactionAsync({
No reason to use executeTransactionAsync when you are already on a background thread.
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction((r) -> {
// do write here
});
}
realm.where(RealmChat::class.java)
If you do import io.realm.kotlin.where, then you can do realm.where<RealmChat>().
.findFirstAsync()
No reason to use findFirstAsync() instead of findFirst() when you are already on a background thread. Also no reason to use load() when you're on a background thread, because you should be using findFirst() in the first place anyway.
You are also most likely missing a return#defer Single.just(chat) to actually return the chat if it's found. That is most likely what your original problem is.
With the handler thread things you're doing though, you might want to consider taking a look at this project called "Monarchy", as it intends to set up the ability to run queries on a background looper thread while still observing the results. It is labelled stagnant but the ideas are sound.
I playing around with Kotlin and Coroutines in my demo android application.
Here's what I have:
fun testCoroutine3() = runBlocking {
var num = 0
val jobs = List(10_000) { // create a lot of coroutines and list their jobs.
launch(CommonPool) {
delay(1000L)
println(num++)
}
}
for(job in jobs) {
job.join() //wait for all jobs to finish
}
println("FINAL RESULT $num")
}
Basically I'm creating a list of 10,000 Coroutines that wait for 1 second and print a number then increment it.
Then when all jobs are done I print the final result.
(This demo is taken from the GitHub Documentation)
Now most of my test run fine, all the coroutines run almost simultaneously, and my final result is 10000
However in some rare occasions, I am getting the final result as 9,999
This become more obvious when I increase the number to 50,000 for example:
Is it possible that Kotlin is skipping some coroutines when there's a lot of them? on the 50,000, looks like it skipped 2
Or is something else happening here?
num++ consists of two operations: tmp = num + 1 and num = tmp. When dealing with multithreading like your example there are cases where some operations might overwrite the results of another thread, leading to cases like your example.
If you want to know more, research "race conditions" where the end result depends on a "race" between two seperate processes.