Home > Enterprise >  Unable to create converter for kotlinx.coroutines.flow.Flow, No JsonAdapter for kotlinx.coroutines.f
Unable to create converter for kotlinx.coroutines.flow.Flow, No JsonAdapter for kotlinx.coroutines.f

Time:06-08

In this demo app, I used a retrofit, Moshi, MVVM, dagger hilt, and kotlin coroutine, I got stuck in this exception, I tried to change the structure of suspended fun in PokemonApiService delete it, change the return type from Flow<PokemonResponse> to Flow<Response<PokemonResponse>> but all this didn't work

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.mml.pokemonkotlin, PID: 3282
    java.lang.IllegalArgumentException: Unable to create converter for kotlinx.coroutines.flow.Flow<com.mml.pokemonkotlin.model.PokemonResponse>
        for method PokemonApiService.getPokemons
        at retrofit2.Utils.methodError(Utils.java:54)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:126)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:85)
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39)
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202)
        at retrofit2.Retrofit$1.invoke(Retrofit.java:160)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy1.getPokemons(Unknown Source)
        at com.mml.pokemonkotlin.data.RemoteDataSource.getPokemons(RemoteDataSource.kt:24)
        at com.mml.pokemonkotlin.viewmodels.PokemonViewModel$getPokemonList$1.invokeSuspend(PokemonViewModel.kt:26)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367)
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
        at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
        at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
        at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
        at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
        at com.mml.pokemonkotlin.viewmodels.PokemonViewModel.getPokemonList(PokemonViewModel.kt:25)
        at com.mml.pokemonkotlin.MainActivity.onCreate(MainActivity.kt:33)
        at android.app.Activity.performCreate(Activity.java:7994)
        at android.app.Activity.performCreate(Activity.java:7978)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.IllegalArgumentException: No JsonAdapter for kotlinx.coroutines.flow.Flow<com.mml.pokemonkotlin.model.PokemonResponse> (with no annotations)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:155)
        at com.squareup.moshi.Moshi.adapter(Moshi.java:105)
        at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:89)
        at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:362)
        at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:345)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:124)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:85) 
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39) 
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202) 
        at retrofit2.Retrofit$1.invoke(Retrofit.java:160) 
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006) 
        at $Proxy1.getPokemons(Unknown Source) 
        at com.mml.pokemonkotlin.data.RemoteDataSource.getPokemons(RemoteDataSource.kt:24) 
        at com.mml.pokemonkotlin.viewmodels.PokemonViewModel$getPokemonList$1.invokeSuspend(PokemonViewModel.kt:26) 
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367) 
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) 
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) 
        at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) 
        at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) 
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) 
        at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1) 
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47) 
        at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1) 
        at com.mml.pokemonkotlin.viewmodels.PokemonViewModel.getPokemonList(PokemonViewModel.kt:25) 
        at com.mml.pokemonkotlin.MainActivity.onCreate(MainActivity.kt:33) 
        at android.app.Activity.performCreate(Activity.java:7994) 
        at android.app.Activity.performCreate(Activity.java:7978) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 

BaseAppliction class

@HiltAndroidApp
class BaseApplication : Application() {


}

model classes

Pokemon.kt

@JsonClass(generateAdapter = true)
data class Pokemon(
    @Json(name = "name")
    var name: String,
    @Json(name = "url")
    var url: String
)

Pokemon response

@JsonClass(generateAdapter = true)
data class PokemonResponse(
    @Json(name = "count")
    var count: Int,
    @Json(name = "next")
    var next: String,
    @Json(name = "previous")
    var previous: String?,
    @Json(name = "results")
    var results: ArrayList<Pokemon>
)

PokemonApiService interface

interface PokemonApiService {

    @GET("pokemon")
    suspend fun getPokemons() : Flow<PokemonResponse>
}

Network Module

@InstallIn(SingletonComponent::class)
@Module
object NetworkModule {

    @Singleton
    @Provides
    fun provideHttpClient(): OkHttpClient {
        return OkHttpClient.Builder().readTimeout(
            15, TimeUnit.SECONDS
        ).connectTimeout(15, TimeUnit.SECONDS).build()
    }

    @Singleton
    @Provides
    fun provideMoshi(): Moshi {
        return Moshi.Builder().build()
    }

    @Singleton
    @Provides
    fun provideRetrofitInstance(
        okHttpClient: OkHttpClient,
    ): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://pokeapi.co/api/v2/")
            .client(okHttpClient)
            .addConverterFactory(MoshiConverterFactory.create(provideMoshi()))
            .build()
    }

    @Singleton
    @Provides
    fun provideApiService(retrofit: Retrofit): PokemonApiService {

        return retrofit.create(PokemonApiService::class.java)
    }
}

RemoteDataSource class

class RemoteDataSource @Inject constructor(private val pokemonApiService: PokemonApiService) {

    private val TAG = "RemoteDataSource"


    suspend fun getPokemons(): PokemonResponse? {
        var response: PokemonResponse? = null

        pokemonApiService.getPokemons()
            .catch { Log.e(TAG, "getPokemons: ${it.message}") }
            .onEmpty { Log.e(TAG, "empty response ") }
            .collect {
                response = it

            }

        return response

    }
}

PokemonViewModel

private const val TAG = "PokemonViewModel"

@HiltViewModel
class PokemonViewModel @Inject constructor(private val remoteDataSource: RemoteDataSource) :
    ViewModel() {

    var pokemonsResponse: MutableLiveData<ArrayList<Pokemon>> = MutableLiveData()

     fun getPokemonList(){
         viewModelScope.launch {
             pokemonsResponse.value = remoteDataSource.getPokemons()?.results

         }
    }
}

pokemon adapter

class PokemonAdapter : RecyclerView.Adapter<PokemonAdapter.PokemonViewHolder>() {

    private var pokemonList = emptyList<Pokemon>()


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonViewHolder {
        val binding = PokemonItemBinding.inflate(
            LayoutInflater.from(parent.context),
            parent, false
        )
        return PokemonViewHolder(binding)
    }

    override fun onBindViewHolder(holder: PokemonViewHolder, position: Int) {
        val currentPokemon = pokemonList[position]
        holder.bind(currentPokemon)
    }

    fun setList(pokemonList: ArrayList<Pokemon>){
        this.pokemonList = pokemonList
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int {
        return pokemonList.size
    }

    inner class PokemonViewHolder(private val binding: PokemonItemBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(pokemon: Pokemon) {
            binding.pokemonNameTV.text = pokemon.name
        }

    }
}

and finally MainActivity.kt

private const val TAG = "MainActivity"

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {


    private lateinit var pokemonViewModel: PokemonViewModel
    private lateinit var binding: ActivityMainBinding
    private lateinit var pokemonAdapter: PokemonAdapter


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        pokemonViewModel = ViewModelProvider(this).get(PokemonViewModel::class.java)
        pokemonAdapter = PokemonAdapter()

        binding.pokemonRecyclerView.adapter = pokemonAdapter

        pokemonViewModel.getPokemonList()

        pokemonViewModel.pokemonsResponse.observe(this, {
            if (it.isNullOrEmpty()) {
                Log.e(TAG, "it is null")
            } else {
                pokemonAdapter.setList(it)
            }
        })
    }
}

CodePudding user response:

As of now I'm not sure if Retrofit suports Flow, I guess it doesn't. You can remove it from the returning type:

interface PokemonApiService {

    @GET("pokemon")
    suspend fun getPokemons() : PokemonResponse
}

CodePudding user response:

after doing some search I found this answer the solution for the second exception, I add this line to the dependencies

kapt "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"

I also changed the return type of getPokemons fun from Flow to PokemonResponse,

finally, I changed results from ArrayList to List because the Moshi doesn't support it yet

@Json(name = "results")
var results: List<Pokemon>
  • Related