I'm writing React Native and implemented a custom UI component for Android. One of the props I send to the component is a large array of objects. The deserialization in Android (Kotlin) tooks some time (>200ms) and I'm trying to use async to prevent blocking the UI.
#ReactProp(name = "items")
fun setItems(view: CustomListView, items: ReadableArray) {
async {
val itemsList = deserializItems(items)
view.setItems(itemsList)
}
}
but Android Studio says: Unresolved reference: async
I added these to my app build.gradle:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
and tried to import manually kotlinx.coroutines but Android Studio doesn't find it as well.
How can I get async functionality in Android?
You need a coroutine scope to be able to call async.
I am not familiar with react development, but how i would use it something like this from inside a viewModel.
val asyncFunction = viewModelScope.async {
//do your background work here
}
and then you need to await() it later.
viewModelScope.launch {
asyncFunction.await()
}
For import i have this in the gradle files
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
and these are the imports
import kotlinx.coroutines.*
Also, it might sound silly, but make sure to sync gradle after adding the dependencies.
Or by using the "Sync Now" button displayed on the screen when editing the gradle file.
I solved my problem by adding coroutines dependency.
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4'
Related
I'm trying to migrate my MVP application from Rx to Kotlin's coroutines (which I'm new to).
As I was running some trials, I found that any code with the following structure fails to compile with org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated
val scope = CoroutineScope(Dispatchers.IO)
fun a(i: Int) {
scope.launch {
withContext(Dispatchers.Main) {
val b = i + 1
}
}
}
It appears that trying to access the parameter i inside the withContext is the problem. If I assign the value of i to something else inside the function block and use that instead, it works alright. But I have the feeling I might be doing something extra wrong here hehe
This lives in a Presenter. My idea is to use "launch" with the IO dispatcher to call the repository and get some data from the database, then use it to update the UI in the Main dispatcher. It looks solid to me, but I'm a bit worried because apparently no one else is running into that same issue, which might mean this pattern I'm trying to implement should be avoided for some reason.
Any clues?
After some fiddling I found that the problem was with my kotlin plugin version.
I changed it from 1.3.50 to 1.3.72 and... magic :D
I am trying to test ViewModel to make sure livedata gets updated correctly. However when using ArgumentMatchers.any() it fails with IllegalStateException saying:
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java) must not
be null
#Test
fun emitValueIfCountIs7() {
doAnswer { invocation: InvocationOnMock ->
val subscriber: mViewModel.CountSubscriber = invocation.getArgument(0)
subscriber.onNext(7)
null
}.`when`(countUseCase).execute(
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java),
ArgumentMatchers.any(Parameters::class.java)
)
// When
mViewModel.getCount()
// Verify
assert(mViewModel.countResponse.value != null)
}
I am using Kotlin and have the following dependencies:
testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-inline:2.23.4"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
Here are my imports:
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.doAnswer
import io.reactivex.Observable
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.invocation.InvocationOnMock
Strange thing is that it used to work before, and I don't know what has happened that could affect this.
Getting matchers to work with Kotlin can be a problem.
If you have a method written in kotlin that does not take a nullable parameter, then we cannot match with it using Mockito.any().
This is because it can return void and this is not assignable to a non-nullable parameter.
If the method being matched is written in Java, then I think that it will work as all Java objects are implicitly nullable.
One possible solution would be to use a library like mockito-kotlin
But you can solve this issue easily with a few lines of code yourself.
If you need typed any(type: Class)
private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
OR
You can use this matcher instead of Matchers.any() :
object MockitoHelper {
fun <T> anyObject(): T {
Mockito.any<T>()
return uninitialized()
}
#Suppress("UNCHECKED_CAST")
fun <T> uninitialized(): T = null as T
}
and use MockitoHelper.anyObject() instead of any() in your kotlin tests.
You can find more information in this post: Using Mockito with Kotlin
There is a discussion about possible solutions in this post :
Is it possible to use Mockito in Kotlin?
The correct solution is mentioned in the comment section of the question by #Ana Koridze. Yes, if you are using Koltin + mockitoKotlin. Make sure you are using the following import:
1 - Using the Mockito-kotlin:
import org.mockito.kotlin.any from Mockito-kotlin
instead of
import org.mockito.Mockito.any
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
2 - Or if you are using older mockito kotlin version original created by nhaarman before the intergation
import com.nhaarman.mockitokotlin2.any from nhaaram's Mockito-kotlin instead of import org.mockito.Mockito.any
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockito_kotlin2_version"
BTW, if you are using Android Studio or IntelliJ IDEA. the any() from mockitokotlin library should be italic by default style/color scheme.
Notice the any() at the end of line. This is from mockitokotlin
And here is the any() from mockito
Thanks #Sattar for the recommended edit.
mockito-kotlin has added support for nullable args with anyOrNull()
`when`(myMock.doesThis(any(), anyOrNull())).thenReturn(someObject)
use Mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
this will use any that works for kotlin as this is a wrapper lib for Mockito but for kotlin
This is what worked for me,
either replaced all generic any()s , with a specific anyTYPE(), i.e anyInt(), anyList() etc from core lib org.mockito:mockito-core and it fixes the (nullability)issue, it seems the specific definitions with type can handle null. this option does not require you to import any extra lib
or
if you really need to use the generic type any() , add this official Mckito extension lib https://github.com/mockito/mockito-kotlin and make sure you use the any() from this lib (by making sure your imports has this in it import org.mockito.kotlin.any)
I suggest to start using MockK lib https://github.com/mockk/mockk instead of Mockito as it is a Mock library for Kotlin = MockK)
however, if you feel lazy to switch right now or maybe dealing with legacy tests (as in my case :), this should fix your issue too.
For me all solutions above were not enough - in addition to that I had to mark the called method as an 'open' method.
According to this:
https://github.com/mockito/mockito-kotlin/wiki/Parameter-specified-as-non-null-is-null
The method is final and Mockito couldn't mock it so I had to add 'open'.
I wrote a simple wrapper function around Mockito's any() and got rid of the warning.
private fun <T> any() : T {
return org.mockito.ArgumentMatchers.any()
}
I am still pretty green at Kotlin though, so I am not sure whether there may be some unwanted side effects. I put this answer out there in case it helps anyone or someone gives me feedback.
I am trying to find the main thread for subscribeOn in Rx3
Single.just(getHeavyData())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Data>() {
#Override
public void accept(Data d) throws Throwable {
setAdapters(d);
}
});
AndroidSchedulers.mainThread() - is not compatible with the brand new RX3
Gradle import: implementation "io.reactivex.rxjava3:rxjava:3.0.0-RC3"
How can we find the main thread in order to do changes to the UI?
AndroidSchedulers.mainThread() is not part of Rx Java 1,2 or 3. Its the part of RxAndroid Library. Add RxAndroid dependency to your project and you will get this method.
RxAndroid still uses RxJava2. Until there is an update from the creators of the library this problem remains.
The new package structure has been released with 3.0.0-RC2 and there is a support library so that v2 and v3 can talk to each other without hidden or overt compilation/runtime problems from before.
This also means that module override tricks no longer work so you have to bridge AndroidSchedulers manually or convert from v2 sources used in Retrofit until these (and many other) libraries start supporting v3.
Please refer this and this
This is an issue with imports.
I had a similar problem before realizing that I need to reference RxJava3.
Use these imports for Schedulers and Disposable Single Observers
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.observers.DisposableSingleObserver;
import io.reactivex.rxjava3.schedulers.Schedulers;
Also, if you have a service with a method that returns a Single use
import io.reactivex.rxjava3.core.Single;
Had a similar problem and the compatibility issue was from an import of the Single. Even though I was using RxJava3 library and RxAndroid3, I was still using:
import io.reactivex.Single
But was resolved when pointing the Single to:
import io.reactivex.rxjava3.core.Single
Check your import, maybe you are importing something like:
implementation 'io.reactivex.**rxjava2**:rxandroid:x.y.z'
but you should be importing something like:
implementation 'io.reactivex.**rxjava3**:rxandroid:3.0.0' (check the last version)
that solved my problem
I am trying to implement custom back navigation in my fragment based on the documentation here
But when I try to replace the "// Handle the back button event" bit with my back button evet code, my IDE throws up an error saying "Required: OnBackPressedCallback found () -> OnBackPressedCallback"
I am using appcompat:1.0.2. and I have core-ktx:1.0.2'.
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
Here is how I am trying to implement the onBackPressedDispatcher
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
patientsActivity.moveToLoginScreen(true) // <-- thing I want to happen when the back button is pressed
}
What am I missing here? How do I go about implementing custom back navigation?
The problem with the code is not on the gradle files but on the syntax. The documentation does not provide the proper kotlin code that corresponds to the java code that was given.
Here is the propper way of creating an object of an abstract class - OnBackPressedCallback
val callback = requireActivity().onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
patientsActivity.moveToLoginScreen(true)
}
})
support a change in the documentation here: https://issuetracker.google.com/issues/135469965
OnBackPressedDispatcher is part of the new androidx.activity dependency. The best way to gain access to the new library is to upgrade your version of Fragments:
implementation "androidx.fragment:fragment-ktx:1.1.0-beta01"
By using the fragment-ktx artifact, you also will gain a transitive dependency on activity-ktx, which is what has the Kotlin extension that the documentation uses.
Specifically, you need import androidx.activity.addCallback and in your app build.gradle, implementation "androidx.fragment:fragment-ktx:1.1.0-beta01"
You haven't passed the boolean argument and that is why it doesn't work. The right code is as shown in Jian Astrero's answer. Alternatively, you can use the lambda syntax like this:
val callback = requireActivity().onBackPressedDispatcher.addCallback(this, true) {
patientsActivity.moveToLoginScreen(true)
}
Note that the true passed as second parameter is essential for it to work.
What I'm trying to achieve is a simple pattern that I'm using in Java and should be do-able in Kotlin according to the documentation. I just want to declare an enum class with a couple of constant definitions that implement the same abstract functions.
My problem is that I cannot manage to make my code compile. I always get the same error:
modifier abstract not allowed here
Here is the code:
enum class Program {
HOME {
override fun readableName(context: Context): String {
return context.getString(R.string.program_home)
}
},
WEEKEND {
override fun readableName(context: Context): String {
return context.getString(R.string.program_weekend)
}
},
SHOPPING {
override fun readableName(context: Context): String {
return context.getString(R.string.program_shopping)
}
};
abstract fun readableName(context: Context): String
}
I have even tried with the sample code from the documentation and yet even this does not compile.
Could anyone have an idea about this odd issue? Btw I'm currently using Kotlin 1.0.6.
I just tried your code and it's compiled normally with Kotlin plugin 1.0.6-release-Studio2.2-1
Maybe you can just try to update AndroidStudio/Kotlin plugin ?
Alright... so I found out where the issue was and it was not what I expected to be. #Kirill Rakhman was right, there was nothing wrong with the enum code. I had an issue with kapt that was unable for some reason to generate my project annotations. More specifically I was using - what I missed as experimental - the newly available gradle kapt plugin documented here.
I rolled back to the previous kapt settings by replacing
apply plugin: 'kotlin-kapt'
by
kapt {
generateStubs = true
}
And it worked! Not sure why the compilation failed while declaring my enum though.
Thanks a lot for those who took time to look into it and if you have any idea why the experimental kapt plugin was not behaving as expected feel free to comment this answer, I would gladly want to know what happened :)