kotlin: accessing class variable from multiple launch block - kotlin

Good day,
I learned kotlin some months ago, but I'm totally new to coroutines. I can't post my full code here, but basically I want to access a variable inside a class.
Here is some codes to help understanding my question:
class CertainClass {
var someVar: Boolean = false;
suspend fun doIt(){
//...
certainScope.launch(Dispatchers.Main) {
someFun2()
}
certainScope.launch(Dispatchers.Main) {
someFun1()
}
//...
}
//....
suspend fun someFun1(){
//...
someVar = true
//...
}
//...
suspend fun someFun2(){
while(earth.exists()) {
print(someVar); // Here is my problem, this always print false, even thought someVar was set to true...
if (someVar == true){
// Do something nasty...
}
delay(1000)
}
}
}
What did I miss...?

Related

Unable to Execute code after Kotlin Flow collect

I'm trying to execute some code after calling collect on a Flow<MyClass>. I'm still kind of new to using Flows so I don't understand why the code after the function doesn't get called.
How I use the Flow:
incidentListener = FirebaseUtils.databaseReference
.child(AppConstants.FIREBASE_PATH_AS)
.child(id)
.listen<MyClass>() //This returns a Flow<MyClass?>?
How I consume the Flow:
private suspend fun myFun() {
viewmodel.getListener()?.collect { myClass->
//do something here
}
withContext(Dispatchers.Main) { updateUI() } //the code never reaches this part
}
How myFun() is called:
CoroutineScope(Dispatchers.IO).launch {
myFun()
}
As far as what I've tried to make it work I've tried closing the coroutine context and it didn't work. I'm assuming Flows work differently than regular coroutines.
Update:
I'm listening through Firebase using this block of code. I don't know if it'll help but maybe the way I implemented it is causing the issue?
inline fun <reified T> Query.listen(): Flow<T?>? =
callbackFlow {
val valueListener = object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
close()
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
try {
val value = dataSnapshot.getValue(T::class.java)
offer(value)
} catch (exp: Exception) {
if (!isClosedForSend) offer(null)
}
}
}
addValueEventListener(valueListener)
awaitClose { removeEventListener(valueListener) }
}
collect is a suspending function, the code after collect will only run once the flow completes.
Launch it in a separate coroutine:
private suspend fun myFun() {
coroutineScope {
launch {
viewmodel.getListener()?.collect { myClass->
//do something here
}
}
withContext(Dispatchers.Main) { updateUI() } //the code never reaches this part
}
}
I forgot to post my own answer to this. I've found the problem before. It's because I wasn't returning the Coroutine Context.
My code has been updated since but with the code above as an example it should be written as follows:
private suspend fun myFun() {
viewmodel.getListener()?.collect { myClass->
//do something here
return#collect
}
withContext(Dispatchers.Main) { return#withContext updateUI() }
//the code should flow downwards as usual
}

Mockk: Verify method called within coroutine

I have a simple object which provides a suspend function to simulate a delaying network request and afterwards calls another method from the object.
class CoroutinesObject {
suspend fun doApiCall() {
delay(1000)
println("Hello from API")
val apiResult = "result #1"
callMe(apiResult)
}
fun callMe(result: String) {
println("[${Thread.currentThread().name}] call me with result: $result")
}
}
I would like to write a simple test which should verify that the method callMe has been called.
class CoroutinesTest {
#Test
fun doApiCall_callsCallMe() {
val obj = CoroutinesObject()
runBlocking {
obj.doApiCall()
}
coVerify { obj.callMe("result #1") }
}
}
Unfortunately the test fails with the following exception and I'm not sure why this happens.
io.mockk.MockKException: Missing calls inside verify { ... } block.
Anybody got an idea whats the problem and how to write a test which is able to verify the called method?
Okay, it seems as if a missing mock for my object was the problem. The following test works:
#Test
fun doApiCall_callsCallMe() {
val obj = spyk(CoroutinesObject())
runBlocking {
obj.doApiCall()
}
coVerify { obj.callMe(any()) }
}

How to mock objects in relaxed mode?

I have an object
object Foo {
fun doSomething(param: String) {
throw Exception()
}
}
I want it to become a stub (relaxed mock in mockk terminology) in my test.
Other words, I want this test to pass without exception:
#Test
fun shouldAskFooWithCorrectParams() {
mockkObject(Foo) // How to change it to make Foo a stub
Foo.doSomething("hey!")
verify(exactly = 1) { Foo.doSomething("hey!") }
}
Additional every { Foo.doSomething(any()) } answers {} does the trick for a single method.
This test is passes:
#Test
fun shouldAskFooWithCorrectParams() {
mockkObject(Foo)
every { Foo.doSomething(any()) } answers {}
Foo.doSomething("hey!")
verify(exactly = 1) { Foo.doSomething("hey!") }
}

How would I "wrap" this not-quite-"by lazy" result caching function call in idiomatic Kotlin?

I can't use "by lazy" because the callbacks require suspendCoroutine, which borks in android if it blocks the main thread, so I have to use the following "cache the result" pattern over and over. Is there a way to wrap it in a funButUseCachedResultsIfTheyAlreadyExist pattern to encapsulate the xCached object?
private var cameraDeviceCached: CameraDevice? = null
private suspend fun cameraDevice(): CameraDevice {
cameraDeviceCached?.also { return it }
return suspendCoroutine { cont: Continuation<CameraDevice> ->
... deep callbacks with cont.resume(camera) ...
}.also {
cameraDeviceCached = it
}
}
When what I'd really like to write is
private suspend fun cameraDevice(): CameraDevice = theMagicFunction { cont ->
... deep callbacks with cont.resume(camera) ...
}
You can build a generalized solution by wrapping an async call as follows:
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineStart.LAZY
class LazySuspendFun<out T>(
scope: CoroutineScope,
private val block: suspend () -> T
) {
private val deferred = scope.async(Dispatchers.Unconfined, LAZY) { block() }
suspend operator fun invoke() = deferred.await()
}
fun <T> CoroutineScope.lazySuspendFun(block: suspend () -> T) =
LazySuspendFun(this, block)
This is a simple example of how you can use it. Note that we are able to compose them so that we use a lazy-inited value as a dependency to getting another one:
val fetchToken = lazySuspendFun<String> {
suspendCoroutine { continuation ->
Thread {
info { "Fetching token" }
sleep(3000)
info { "Got token" }
continuation.resume("hodda_")
}.start()
}
}
val fetchPosts = lazySuspendFun<List<String>> {
val token = fetchToken()
suspendCoroutine { continuation ->
Thread {
info { "Fetching posts" }
sleep(3000)
info { "Got posts" }
continuation.resume(listOf("${token}post1", "${token}post2"))
}
}
}
On the calling side you must be inside some coroutine context so you can call the suspending functions:
myScope.launch {
val posts = fetchPosts()
...
}
This solution is robust enough that you can concurrently request the value several times and the initializer will run only once.
I'll write this as an answer, since it's not possible to post much code in comments.
What you're looking for is something like this:
private suspend fun cameraDevice() = theMagicFunction {
CameraDevice()
}()
suspend fun theMagicFunction(block: ()->CameraDevice): () -> CameraDevice {
var cameraDeviceCached: CameraDevice? = null
return fun(): CameraDevice {
cameraDeviceCached?.also { return it }
return suspendCoroutine { cont: Continuation<CameraDevice> ->
cont.resume(block())
}.also {
cameraDeviceCached = it
}
}
}
Unfortunately, this will not compile, since closures cannot be suspendable, and neither are local functions.
Best I can suggest, unless I miss a solution there, is to encapsulate this in a class, if this variable bothers you too much.

Kotlin: How to run service methods in the context of a transaction class?

I'd like to define database calls in service methods, but have them executed in the context of a Transaction class without opening the connection in the service itself so that I can include multiple service calls in the same transaction.
I'm looking for something like this, but can't quite figure it out.
class Transaction {
init { /** Grab connection **/ }
fun doSelect() { ... }
}
class UserService {
fun Transaction.getUser() {
return doSelect()
}
}
fun main (args: Array<String>) {
Transaction() {
UserService().getUser() // INVALID
...
}
}
Is there a way to do this?
I know that I can pass in a transaction instance to the service like so:
class UserService(tx: Transaction) {
fun getUser() {
with(tx) {
doSelect()
}
}
...
fun main (args: Array<String>) {
Transaction() {
UserService(this).getUser()
...
}
}
...but am hoping for a more elegant pattern.
The system works the other way around, so to fix the issue, swap the receivers:
fun main (args: Array<String>) {
UserService().apply {
Transaction().getUser()
}
}