RxJava - Subscribe doesn't catch exception which cause crash - kotlin

I am trying to subscribe to a SingleSource, I have implemented both onSuccess And onError of subscribe method, here is my code:
disposable.add(repository
.getUser1()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
toast(it)
}, {
toast(it.message ?: "Error")
}))
The Problem is When an exception occur in repository it does't being caught in onError
Here is my repository implementation:
class Repository(private val sp: SharedPreferences) {
// It crashes
fun getUser1(): Single<String> = generateUser(name, "Hello")
// it doesn't crash
fun getUser2(): Single<String> = Single.fromCallable { name }.flatMap { generateUser(it, "Hello") }
private var name: String
get() = sp.getString("user", null) ?: throw NoNameException()
set(value) = sp.edit().putString("user", value).apply()
private fun generateUser(name: String, message: String): Single<String> = Single.just("$message $name")
}
And here is the crash Log:
09-24 10:13:40.930 6934-6934/com.mosius.samplerxtest E/AndroidRuntime:
FATAL EXCEPTION: main
Process: com.mosius.samplerxtest, PID: 6934
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:503)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826) 
Caused by: com.mosius.samplerxtest.NoNameException: No Name Supplied
at com.mosius.samplerxtest.Repository.getName(Repository.kt:17)
at com.mosius.samplerxtest.Repository.getUser1(Repository.kt:10)
at com.mosius.samplerxtest.MainActivity$onCreate$1.onClick(MainActivity.kt:24)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25881)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6649)
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
What's the different between these two implementation?

In the getUser1() method, SingleSource has not been created yet, so it is out of Rx control to handle the exception.
In the Second one the name is being fetched in Rx scope therefore it could handle the exception

Related

Getting java.net.SocketException: No buffer space available and BindException using Jsoup while connecting to url using Kotlin Coroutines

I was trying to do parser for a website, and because there's a lot of content, I used Kotlin coroutines to make parsing asynchronous, but I've ran into a problem where I constantly get java.net.SocketException: No buffer space available (maximum connections reached?): connect at java.base/sun.nio.ch.Net.connect0(Native Method) ~[na:na] at java.base/sun.nio.ch.Net.connect(Net.java:579) ~[na:na] at java.base/sun.nio.ch.Net.connect(Net.java:568) ~[na:na] and Suppressed: java.net.BindException: Address already in use: no further information at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na] at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na] at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
I use the function below to get a document
fun connect(url: String): Document {
return try {
Jsoup.connect(url).newRequest().get()
} catch (_: SocketTimeoutException) {
println("RETRYING $url")
connect(url)
}
}
and
runBlocking {
withContext(Dispatchers.IO) {
val updated: ArrayList<Deferred<List<Book>>> = arrayListOf()
for (booklist in booklists) {
updated.add(async {
booklist.forEach { book -> book.sections = ru.parseBook(book) }
return#async booklist
})
}
updated.awaitAll().forEach { u -> bookRepository.saveAll(u) }
}
}
to run parser
Worked it out via HttpClient which is being created for every Coroutine:
for (bookList in chunked) {
updated.add(async {
val client = HttpClient.newHttpClient()
bookList.forEach { book -> book.sections = engine.parseBook(book, client) }
return#async bookList
})
}
and passed as a variable into a function
val request = HttpRequest.newBuilder(URI(url)).GET().build()
withContext(Dispatchers.IO) {
val send = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
val doc = Jsoup.parse(send.body())
}

Encrypting existing Room database -> Exception Occur: file is not a database: , while compiling: select count(*) from sqlite_master;

I'm encrypting the existing Room database. When I call insert() method to insert data to Room and findAll() method to fetch data from Room After Encrypting the Room DB and run the app an exception occurs at both the methods and the app crashes. I researched a lot but the problem is not resolved. Please help me to solve this exception. And tell me what am I missing. Thanks a lot!
Code UserDB.kt:
#Database(entities = [User::class], version = 1)
abstract class UserDB : RoomDatabase() {
abstract fun userDao(): UserDAO
companion object {
#Volatile
private var instance: UserDB? = null
fun getInstance(context: Context, charArray: CharArray): UserDB? {
if (instance == null) {
synchronized(UserDB::class) {
val passphrase: ByteArray =
SQLiteDatabase.getBytes(charArray)
val factory = SupportFactory(passphrase)
instance = Room.databaseBuilder(
context.applicationContext,
UserDB::class.java,
"RoomDB"
).openHelperFactory(factory).build()
}
}
return instance
}
}
}
Code UserDAO.kt
#Dao
interface UserDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(user: User)
#Query("SELECT * FROM User")
fun findAll(): List<User>
}
Code MainActivity.kt:
class MainActivity : AppCompatActivity() {
private var userDB: UserDB? = null
private var pass: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pass = "!##ABC"
SQLiteDatabase.loadLibs(this)
userDB = UserDB.getInstance(this, pass!!.toCharArray())
userDB?.userDao()?.insert(User(0, "Ahmer"))
val textView1 = findViewById<TextView>(R.id.textData)
// TODO Display from Room
val u: User = userDB?.userDao()!!.findAll()[0]
Log.v("RoomProduct", u.id.toString() + " : " + u.name)
textView1.text = StringBuilder("${u.id} : ${u.name}")
}
}
Exception:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.roomwithcipher, PID: 32748
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.roomwithcipher/com.example.roomwithcipher.MainActivity}: net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3114)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7050)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
Caused by: net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:91)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2016)
at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1902)
at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2673)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2603)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1247)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1322)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:166)
at net.sqlcipher.database.SupportHelper.getWritableDatabase(SupportHelper.java:83)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:622)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:399)
at com.example.roomwithcipher.UserDAO_Impl.insert(UserDAO_Impl.java:46)
at com.example.roomwithcipher.MainActivity.onCreate(MainActivity.kt:23)
at android.app.Activity.performCreate(Activity.java:7327)
at android.app.Activity.performCreate(Activity.java:7318)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3094)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:214) 
at android.app.ActivityThread.main(ActivityThread.java:7050) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 
I added the following dependencies in build.gradle(:app)
implementation 'net.zetetic:android-database-sqlcipher:4.3.0#aar'
implementation "androidx.sqlite:sqlite:2.0.1"
implementation 'androidx.room:room-runtime:2.3.0'
annotationProcessor("androidx.room:room-compiler:2.3.0")
kapt("androidx.room:room-compiler:2.3.0")

Available doOnError{} did not solve The exception not handled due to missing onError handler in the subscribe()

I have a PublishSubject:
subjectA = PublishSubject.create()
whoch is then operated similar to:
subjectA
.flatMap {
//..
}
.flatMapUntil({ it }) {
//..
}
.observeOn(AndroidSchedulers.mainThread())
.filter { it.isFilter }
.doOnNext {
//..
}
.doOnError { e->
Log.d("TAG", "doOnError ${e.localizedMessage}")
}
.takeUntil(disposeComposable)
.subscribe()
Thinking that above code created following log output:
RX global error
io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the
subscribe() method call. Further reading:
https://github.com/ReactiveX/RxJava/wiki/Error-Handling |
java.util.NoSuchElementException: Collection contains no element
matching the predicate.
at io.reactivex.rxjava3.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:718)
at io.reactivex.rxjava3.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:715)
at io.reactivex.rxjava3.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
at io.reactivex.rxjava3.internal.util.AtomicThrowable.tryTerminateConsumer(AtomicThrowable.java:110)
at io.reactivex.rxjava3.internal.util.HalfSerializer.onError(HalfSerializer.java:118)
at io.reactivex.rxjava3.internal.operators.observable.ObservableTakeUntil$TakeUntilMainObserver.onError(ObservableTakeUntil.java:85)
at io.reactivex.rxjava3.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onError(ObservableDoOnEach.java:117)
at io.reactivex.rxjava3.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:97)
at io.reactivex.rxjava3.internal.operators.observable.ObservableFilter$FilterObserver.onNext(ObservableFilter.java:52)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:202)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:256)
at io.reactivex.rxjava3.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:123)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.util.NoSuchElementException: Collection contains no element matching the predicate.
at com.example.app.DataModel.initialize$lambda-31(data.model.kt:571)
at com.example.app.DataModel.$r8$lambda$9iWq6yMOxbhDAuxg-6-Wk1ZnNzk(Unknown
Source:0)
at com.example.app.DataModel$$ExternalSyntheticLambda11.accept(Unknown
Source:4)
at io.reactivex.rxjava3.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:93)
Error message says, I do not have implemented an onError() method call: The exception was not handled due to missing onError handler in the subscribe() method call.
But I obviously added a doOnError{}. Further the localizedMessage on the above code tells:
Collection contains no element matching the predicate.
Wha is wrong here?
As the error message indicates, you don't have an error handler in the subscribe method:
.doOnError { e->
Log.d("TAG", "doOnError ${e.localizedMessage}")
}
.takeUntil(disposeComposable)
.subscribe() // <------------------------------------------------
doOnError is a different method and is not an error handler, only a peek into an error in the chain.
Consequently, you'll have to put a handler the right place:
.doOnError { e->
Log.d("TAG", "doOnError ${e.localizedMessage}")
}
.takeUntil(disposeComposable)
.subscribe(
{ value -> Log.d("TAG", "onNext ${value}") },
{ e -> Log.d("TAG", "onError ${e.localizedMessage}") }
)
Collection contains no element matching the predicate.
Check what happens here:
at com.example.app.DataModel.initialize$lambda-31(data.model.kt:571)

InvocationHandler in Kotlin

I'm reading Head First: Design Patterns (2nd ed) and I followed the code sample but instead of using Java, I used Kotlin. Currently, I'm in a chapter tackling about proxy protection pattern and having difficulty to run it with Kotlin. Please see the code and exceptions below.
Sample code
interface Person {
fun getName(): String
fun setName(name: String)
}
class PersonImpl : Person {
private var _name: String = ""
override fun getName(): String = _name
override fun setName(name: String) {
_name = name
}
}
class OwnerInvocationHandler(private val person: Person) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<Any>?): Any? {
try {
val methodName = method.name
if (methodName.isNullOrEmpty())
return null
if (methodName.startsWith("get")) {
// return method.invoke(proxy, *(args ?: arrayOfNulls<Any>(0))) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *(args ?: emptyArray())) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *args.orEmpty()) // << Encountered "EXCEPTION B" below
return method.invoke(proxy, args) // << From the code sample, encountered "EXCEPTION A" below
} else if (methodName.startsWith("set")) {
return method.invoke(person, args)
}
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
return null
}
}
// main.kt
val listOfPeople = arrayListOf<Person>()
fun main(array: Array<String>) {
initializeDatabase()
val joe = getPersonFromDatabase("Joe Javabean") ?: return
val ownerProxy = getOwnerProxy(joe)
println("Name is ${ownerProxy.getName()}")
}
fun initializeDatabase() {
val p1 = PersonImpl()
p1.setName("Joe Javabean")
listOfPeople.add(p1)
}
fun getOwnerProxy(person: Person): Person {
return Proxy.newProxyInstance(
person.javaClass.classLoader,
person.javaClass.interfaces,
OwnerInvocationHandler(person)
) as Person
}
fun getPersonFromDatabase(name: String): Person? {
return listOfPeople.firstOrNull { p -> name.contentEquals(p.getName()) }
}
Exception
Exception A
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10a5ef4e0). One of the two will be used. Which one is undefined.
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at com.helloworld.app.MainKt.main(main.kt:12)
Exception B
objc[64094]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x109e744c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x109eee4e0). One of the two will be used. Which one is undefined.
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
java.lang.reflect.InvocationTargetExceptionjava.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
...
I found the link below but didn't solve my issue.
Kotlin: Argument Type Mismatch when passing Array as vararg parameter
This is just a user error (embarrassing). As #broot pointed out:
Code Sample
class OwnerInvocationHandler(private val person: Person) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<Any>?): Any? {
try {
val methodName = method.name
if (methodName.isNullOrEmpty())
return null
if (methodName.startsWith("get")) {
// return method.invoke(proxy, *(args ?: arrayOfNulls<Any>(0))) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *(args ?: emptyArray())) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *args.orEmpty()) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, args) // << From the code sample, encountered "EXCEPTION A" below
// `person` should be used not `proxy`
return method.invoke(person, *args.orEmpty())
} else if (methodName.startsWith("set")) {
return method.invoke(person, args)
}
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
return null
}
}

How to solver error for kotlin coroutines flow?

I tried to use NetworkBoundResource for my MVVM Model and after i follow some tutorial, i'm having an error look this
10-21 14:15:04.073 31376-31376/com.example.mvvmsecondmodel E/Process: android_os_Process_getProcessNameByPid pid is 31376
10-21 14:15:04.074 31376-31376/com.example.mvvmsecondmodel E/Process: android_os_Process_getProcessNameByPid value is mvvmsecondmodel
10-21 14:15:04.416 31376-31421/com.example.mvvmsecondmodel E/SQLiteLog: (283) recovered 8 frames from WAL file /data/data/com.example.mvvmsecondmodel/databases/movie_db-wal
10-21 14:15:04.640 31376-31376/com.example.mvvmsecondmodel E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.mvvmsecondmodel, PID: 31376
java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.flow.Flow<com.example.mvvmsecondmodel.data.respository.ApiResponse<com.example.mvvmsecondmodel.data.model.MovieResponse$Movie>>
for method ApiService.getMyMovie
at retrofit2.Utils.methodError(Utils.java:52)
at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:105)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:66)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy0.getMyMovie(Unknown Source)
at com.example.mvvmsecondmodel.data.remote.ApiService$DefaultImpls.getMyMovie$default(ApiService.kt:11)
at com.example.mvvmsecondmodel.data.respository.MovieRespository$getListMovie$$inlined$networkBoundResource$1.invokeSuspend(NetworkBoundResource.kt:49)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:55)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for kotlinx.coroutines.flow.Flow<com.example.mvvmsecondmodel.data.respository.ApiResponse<com.example.mvvmsecondmodel.data.model.MovieResponse$Movie>>.
Tried:
* retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
* retrofit2.DefaultCallAdapterFactory
at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:241)
at retrofit2.Retrofit.callAdapter(Retrofit.java:205)
at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:103)
... 14 more
At first i thought there's red sign at my code so i checked ApiService and Respository class and nothing wrong cause i already follow the tutorial
ApiService:
interface ApiService {
#GET("3/movie/popular")
fun getMyMovie(#Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
) : Flow<ApiResponse<MovieResponse.Movie>>
}
MovieRespository:
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() : Flow<Resource<Movie>> {
return networkBoundResource(
fetchFromLocal = { movieDao.getMyMovie() },
shouldFetchFromRemote = {true},
fetchFromRemote = {apiService.getMyMovie()},
processRemoteResponse = {},
saveRemoteData = {movieDao.insert(
it.results.let {
it.map { data -> Movie.from(data) }
}
)},
onFetchFailed = {_, _ ->}
).flowOn(Dispatchers.IO)
}
You should define your api service as suspend function.
Api Service:
interface ApiService {
#GET("3/movie/popular")
suspend fun getMyMovie(#Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
) : ApiResponse<MovieResponse.Movie>
}
Movie Repository:
class MovieRepository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() : Flow<Resource<Movie>> {
return flow {
// do your networkBoundResource functions
}.flowOn(Dispatchers.IO)
}
I can't test the code because you didn't share your NetworkBoundResource class. I hope the code help to fix your problem.