I try to manage my threads for IO Processes. One of is for Realm usage.
like…
init {
val ai = app.packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
bundle = ai.metaData
runBlocking(appExecutors.dbContext) {
Realm.init(app)
}
}
It works fine if I set val dbContext as newSingleThreadContext(“databaseIO”)…
But I develop an android-library, so If there is an implementation of Realm on the app module, I need to set the usage on the same thread. And generally, everyone uses main thread to access to Realm. In that case I tried to set UI but it caused ANR. I can understand why it causes ANR, but I can’t find a proper solution for this scenario.
Note: if I use it with launch… it works for here. But on my RealmManager class I need to use runBlocking. So there is no way to use only launch…:slight_smile:
like…
fun getProfile(id: String): Profile? {
try {
return runBlocking(dbCoroutine) {
val query = realm!!.where(Profile::class.java).equalTo("numbers.id", id)
query.findFirst()
}
} catch (ex: Exception) {
logger.e(TAG, ex)
return null
}
}
or
internal val allProfiles: List<Profile>
get() = runBlocking(dbCoroutine) { realm!!.where(Profile::class.java).findAll() }
Is there anything I do wrong way, or any advice for better implementation?
Related
I'm trying to use Kotlin co-routines to trigger a network request and handle Exceptions. I've looked at a lot of tutorials on co-routines but I'm really struggling to relate what i know to the problem i have.
The problem
Trying to get an exception to be caught in the View, but no Exception is thrown from the ViewModel so the application crashes.
The Code
My Android app has three layers that relate to this issue. I have the View, ViewModel and a Service layer.
In the service layer, request.execute() can throw a UserAuthException.
MyView (View)
private val mViewModel: MyViewModel by viewModels()
private fun getFileId() {
try {
mViewModel.requestFileId()
} catch (e: UserAuthException) {
Timber.i(e)
}
}
MyViewModel (ViewModel)
private val apiService = MyApiService()
fun requestFileId() {
viewModelScope.launch {
ApiService.requestFileId()
}
}
MyApiService (Service Layer)
suspend fun requestFileId(): FileId = withContext(Dispatchers.IO) {
request.execute()
}
Things that i have looked at
I have played around with CoroutineExceptionHandlers, supervisorJobs with no luck, but without the fundamental knowledge of how these things work I'm not really making any progress.
Any help would be appreciated, thanks.
fun requestFileId() {
viewModelScope.launch {
ApiService.requestFileId()
}
}
This is not a suspendable function. It launches a concurrent coroutine and returns right away. Clearly, calling requestFileId() will never throw an exception.
Launching a coroutine is just like starting another thread, it introduces concurrency to your code. If your current code hopes to stay non-concurrent while observing the results of suspendable functions, you may be looking at significant architectural changes to the application to make it behave correctly under concurrency.
In your model, change it to something like this:
fun requestFileId() {
viewModelScope.launch {
try {
ApiService.requestFileId()
} catch (e: Exception) {
// inform your view
}
}
}
I am rather new to android development and am trying to get the Advertising ID for a particular device. I've found some stack overflow suggestions on how to do so:
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext())
val adId = adInfo?.id
in a worker thread). However this keeps on giving me the "Timed out waiting for the service connection" error.
Others seems to have suggest adding the following gradle dependencies help, but not for me:
implementation 'com.google.android.gms:play-services-ads-identifiers:17.0.0'
implementation 'com.google.android.gms:play-services-base:17.1.0'
as well as having
apply plugin: 'com.google.gms.google-services'
What am I missing to properly get the Advertising ID?
I use Rxjava to get aid, you can use Asyntask, coroutine...
RxJava
fun getAdId() {
Observable.fromCallable { AdvertisingIdClient.getAdvertisingIdInfo(this).id }
.subscribeOn(AppScheduler().backgroundThread())
.observeOn(AppScheduler().mainThread())
.subscribe(Consumer<String> {
val adId = it
}, Consumer<Throwable> {
// nothing
})
}
Import Rxjava in app/build.gradle:
implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
With coroutine of Kotlin:
GlobalScope.launch {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext())
val adId = adInfo?.id
Log.e("text", adId)
}
If someone wonder (like me) why they get
"Calling this from your main thread can lead to deadlock"
when trying to read it from using viewModelScope and a suspend function. Here is what you need to do:
Since viewModelScope uses Dispatchers.Main by default you need it will throw this error.
Option 1:
Pass context when launched
viewModelScope.launch(Dispatchers.Default){
// call your suspend function from here
}
but in this way you need to do some work when you want to do some work on main thread.
Option 2 (I prefer this):
use CoroutineContext in your suspend function
suspend fun getAdId(): String? {
return withContext(Dispatchers.Default) {
try {
AdvertisingIdClient.getAdvertisingIdInfo(context).id
} catch (exception: Exception) {
null // there still can be an exception for other reasons but not for thread issue
}
}
}
Try to use this code:
fun getAdvertisingId(context: Context, callback: (advertisingId: String?) -> Unit) {
Thread(Runnable {
try {
val info = AdvertisingIdClient.getAdvertisingIdInfo(context)
val id = info.id
callback(id)
} catch (e: Exception) {
callback(null)
}
}).start()
}
Basically I have to make a network request using OkHttp in parallel to various addresses. I only care about the result of the first one that succeeds. Can I do this with Flow on Kotlin?
I've been looking around but I'm struggling with getting the requests to run in parallel, the always run in sequence.
The code basically takes a list of addresses and should return the only address that worked or null if none worked.
Thanks.
Edit: I should mention I plan on using this on Android. I can probably do it with RX but wanted to learn Flow. Also trying to limit the libraries I add to the app.
Edit: I have marked an answer as correct however that isn't how I did but it took me very close to how I did it but since I'm new to Flow I have no idea if how I did it is correct though I'm pretty sure it works after my testing.
I have a function that throws NoSuchElementException when not found. It calls searchForIPAsync which is a suspend function that does all the OkHttp work and returns true|false.
#Throws(NoSuchElementException::class)
private suspend fun findWorkingIP(ipsToTest: MutableList<String>): String? = ipsToTest
.asFlow()
.flatMapMerge(ipsToTest.size)
{ impl ->
flow<String?> {
val res = connectionHelper.searchForIPAsync(getURLToTest(impl))
if (res) {
emit(impl)
} else {
}
}
}.first()
Then I call this and catch the exception in case nothing is found:
try {
val ipFound = findWorkingIP(ipsToTest)
Log.w(TAG, "find: Got something " + ipFound);
return ipFound
} catch (ex: NoSuchElementException) {
Log.w(TAG, "find: not found");
}
Although the Flow-based solution in another answer is a close match to what you need, unfortunately as of Kotlin 1.3.2 the Flow implementation has a bug that breaks it. The bug already has a proposed fix so this should be resolved with the next patch release of Kotlin. In the meantime, here's a similar solution that uses async and Channel instead:
suspend fun getShortUrl(urls: List<String>): String = coroutineScope {
val chan = Channel<String?>()
urls.forEach { url ->
launch {
try {
fetchUrl(url)
} catch (e: Exception) {
null
}.also { chan.send(it) }
}
}
try {
(1..urls.size).forEach { _ ->
chan.receive()?.also { return#coroutineScope it }
}
throw Exception("All services failed")
} finally {
coroutineContext[Job]!!.cancelChildren()
}
}
I am really happy that I switched my long running tasks, which constantly produce results to UI thread to coroutines. It improved performance and decreased memory usage by 3 times and all memory leaks disappeared compared to AsyncTask or regular Threads in Android.
The only problem remains is that, I don't know how should I restart my long running operation after exception has occurred at some time...
I feel I did not understand exception handling in coroutines at all after reading tons of article. Let me know how can I achieve desired behaviour.
I have coroutine scope in fragment(will move to VM in near future).
lateinit var initEngineJob: Job
override val coroutineContext: CoroutineContext
get() = initEngineJob + Dispatchers.Main
Long running task with async/await.
fun initWorkEngineCoroutine()
{
launch {
while(true) {
val deferred = async(Dispatchers.Default) {
getResultsFromEngine()
}
val result = deferred.await()
if (result != null) {
//UI thread
draw!!.showResult(result)
}
}
}
}
fun getResultsFromEngine() :Result? {
result = // some results from native c++ engine, which throws exception at some times
return result
}
i don't know where should I put try catch. I tried to surround deferred.await() with try catch, but I could not call same method in catch block to retry long running task. I tried SupervisorJob(), but no success either. I still could not call initWorkEngineCoroutine() again and start new coroutine...
Help to solve this issue finally :)
You should treat your code as linear imperative and try/catch where it makes the most logical sense in your code. With this mindset, your question is probably less about coroutines and more about try/catch retry. You might do something like so:
fun main() {
GlobalScope.launch {
initWorkEngineCoroutine()
}
}
suspend fun initWorkEngineCoroutine() {
var failures = 0
val maxFailures = 3
while(failures <= maxFailures) {
try {
getResultsFromEngine()?.let {
draw!!.showResult(it)
}
} catch (e: Exception) {
failures++
}
}
}
// withContext is like async{}.await() except an exception occuring inside
// withContext can be caught from inside the coroutine.
// here, we are mapping getResultFromEngine() to a call to withContext and
// passing withContext the lambda which does the work
suspend fun getResultsFromEngine() :Result? = withContext(Dispatchers.Default) {
Result()
}
I've included some logic to prevent infinite loop. It's probably not going to fit your requirements, but you might consider some sort of thing to prevent an issue where exceptions are raised immediately by getResultsFromEngine() and end up causing an infinite loop that could result in unexpected behavior and potential stackoverflow.
I am using Kotlin coroutines to get data from the server, I am passing the deferred over to other functions. In case the server doesn't give an answer in 2000 ms I would like to retrive the object from a local Room DB (if it exists in a local database), but if I finally receive data from the server I would like to save in in a local DB for future calls. How can I acheive that? I thought about using withTimeout, but in this situation, there is no waiting for a response from the server after timeout.
override fun getDocument(): Deferred<Document> {
return GlobalScope.async {
withTimeoutOrNull(timeOut) {
serverC.getDocument().await()
} ?: dbC.getDocument().await()
}
}
An idea I came up with:
fun getDocuments(): Deferred<Array<Document>> {
return GlobalScope.async {
val s = serverC.getDocuments()
delay(2000)
if (!s.isCompleted) {
GlobalScope.launch {
dbC.addDocuments(s.await())
}
val fromDb = dbC.getDocuments().await()
if (fromDb != null) {
fromDb
} else {
s.await()
}
} else {
s.await()
}
}
}
I recommend using the select expression from the kotlinx.coroutines library.
https://kotlinlang.org/docs/reference/coroutines/select-expression.html
fun CoroutineScope.getDocumentsRemote(): Deferred<List<Document>>
fun CoroutineScope.getDocumentsLocal(): Deferred<List<Document>>
#UseExperimental(ExperimentalCoroutinesApi::class)
fun CoroutineScope.getDocuments(): Deferred<List<Document>> = async {
supervisorScope {
val documents = getDocumentsRemote()
select<List<Document>> {
onTimeout(100) {
documents.cancel()
getDocumentsLocal().await()
}
documents.onAwait {
it
}
}
}
}
The select expression resumes either with the onAwait signal from the network or with the timeout. We return the local data in that case.
You may want to load documents in chunks as well, for that Channels may help too
https://kotlinlang.org/docs/reference/coroutines/channels.html
And lastly, we use an Experimental API of kotlinx.coroutines in the example, the function onTimeout may change in the future versions of the library