Maulik Sinroja
Maulik Sinroja

Reputation: 191

java.lang.RuntimeException: Failed to invoke public io.reactivex.Observable() with no args

Hello, I am currently preparing one simple demo example of MVVM with Coroutines and I am facing the below issue. Please have a look into the code and what wrong with the code.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.mvvmcoroutine.development, PID: 18974
    java.lang.RuntimeException: Failed to invoke public io.reactivex.Observable() with no args
        at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:113)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121)
        at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
     Caused by: java.lang.InstantiationException: Can't instantiate abstract class io.reactivex.Observable
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:110)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212) 
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39) 
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27) 
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225) 
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121) 
        at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:919)

Dependency Configuration

'rxJavaVersion'           : 'io.reactivex.rxjava2:rxjava:2.1.15'
'rxAndroidVersion'        : 'io.reactivex.rxjava2:rxandroid:2.1.1'
'rxKotlinVersion'         : 'io.reactivex.rxjava2:rxkotlin:2.4.0'
'gsonVersion'             : 'com.google.code.gson:gson:2.8.2'
'retrofitVersion'         : 'com.squareup.retrofit2:retrofit:2.6.2'
                          : 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
                          : 'com.squareup.retrofit2:converter-gson:2.6.2'
                          : 'com.squareup.retrofit2:converter-scalars:2.6.2'
'daggerVersion'           : 'com.google.dagger:dagger:2.23.2'
                          : 'com.google.dagger:dagger-android-support:2.23.2'
                          : 'com.google.dagger:dagger-compiler:2.23.2'
                          : 'com.google.dagger:dagger-android-processor:2.23.2'
'assistedInjectVersion'   : 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.2'
                          : 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.2'
'okHttpVersion'           : 'com.squareup.okhttp3:okhttp:4.2.0'
                          : 'com.squareup.okhttp3:logging-interceptor:4.2.0'

Application code as below

@ApplicationClass

open class App : MultiDexApplication(), Application.ActivityLifecycleCallbacks, HasActivityInjector {

    private var runningActivityCount: Int = 0

    @Inject
    lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun activityInjector(): AndroidInjector<Activity> {
        return activityDispatchingAndroidInjector
    }

    override fun onActivityPaused(p0: Activity) {}

    override fun onActivityStarted(p0: Activity) {}

    override fun onActivityDestroyed(p0: Activity) {
        runningActivityCount -= 1
        if (runningActivityCount == 0)
            Timber.d("Application :: onActivityDestroyed()")
    }

    override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}

    override fun onActivityStopped(p0: Activity) {}

    override fun onActivityCreated(p0: Activity, p1: Bundle?) {
        runningActivityCount += 1
    }

    override fun onActivityResumed(p0: Activity) {}

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder()
            .application(this)
            .build()
            .inject(this)
    }
}

@AppComponentInterface

@Singleton
@Component(
    modules = [
        (AndroidInjectionModule::class),
        (ActivityBuilder::class),
        (ApplicationModule::class),
        (APIModule::class),
        (RepositoryModule::class),
        (PostScreenModule::class)
    ]
)
interface AppComponent {
    fun inject(app: App)
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(app: Application): Builder

        fun build(): AppComponent
    }
}

@ActivityBuilder

@Module
abstract class ActivityBuilder {
    @ContributesAndroidInjector(modules = [(PostScreenModule::class)])
    internal abstract fun bindPostScreen(): PostScreen
}

@ApplicaitonModule

@Module
class ApplicationModule {

    @Provides
    @Singleton
    internal fun provideContext(application: Application): Context {
        return application
    }

    @Provides
    @Singleton
    internal fun provideCommonPreference(context: Context): CommonPreferences {
        return CommonPreferences(context)
    }
}

@APIModule

@Module
class APIModule {

    @Provides
    @Singleton
    internal fun provideHeaderInterceptor(mContext: Context): HeaderInterceptor {
        return HeaderInterceptor(mContext)
    }

    @Provides
    @Singleton
    internal fun provideOkHttp(mContext: Context): OkHttpClient {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        return OkHttpClient.Builder()
            .readTimeout(TIMEOUT, TimeUnit.SECONDS)
            .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
            .addInterceptor(HeaderInterceptor(mContext))
            .addInterceptor(interceptor)
            .build()
    }

    @Provides
    @Singleton
    internal fun provideGsonConverterFactory(): GsonConverterFactory {
        return GsonConverterFactory.create()
    }

    @Provides
    @Singleton
    internal fun provideScalarsConverterFactory(): ScalarsConverterFactory {
        return ScalarsConverterFactory.create()
    }

    @Provides
    @Singleton
    internal fun provideRxJava2CallAdapterFactory(): RxJava2CallAdapterFactory {
        return RxJava2CallAdapterFactory.createAsync()
    }

    @Provides
    @Singleton
    internal fun provideRetrofit(
        okHttpClient: OkHttpClient,
        gsonConverterFactory: GsonConverterFactory,
        scalarsConverterFactory: ScalarsConverterFactory,
        rxJava2CallAdapterFactory: RxJava2CallAdapterFactory
    ): Retrofit {
        return Retrofit.Builder()
            .addConverterFactory(gsonConverterFactory)
            .addConverterFactory(scalarsConverterFactory)
            .addCallAdapterFactory(rxJava2CallAdapterFactory)
            .client(okHttpClient)
            .baseUrl("https://jsonplaceholder.typicode.com")
            .build()
    }

    @Provides
    @Singleton
    internal fun provideAPIUrls(retrofit: Retrofit): APIUrls {
        return retrofit.create(APIUrls::class.java)
    }
}

@HeaderInterceptorClass

class HeaderInterceptor @Inject constructor(private val mContext: Context) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val token = CommonPreferences(mContext).token

        val request: Request

        if (token.isNotEmpty()) {
            val androidDeviceId = CommonPreferences(mContext).deviceUniqueId
            val appVersion = BuildConfig.VERSION_NAME
            val buildVersion = BuildConfig.VERSION_CODE
            var lastSyncDate = "2019-11-29 12:00:00"

            if (CommonPreferences(mContext).lastSyncTime.isNotEmpty()) {
                lastSyncDate = CommonPreferences(mContext).lastSyncTime
            }

            request =
                chain.request().newBuilder()
                    .addHeader(HEADER_X_ACCESS_TOKEN_KEY, token)
                    .addHeader(HEADER_DEVICE_MASTER_ID_KEY, androidDeviceId)
                    .addHeader(HEADER_DEVICE_ID_KEY, androidDeviceId)
                    .addHeader(HEADER_DEVICE_NAME_KEY, getDeviceName())
                    .addHeader(HEADER_DEVICE_TYPE_KEY, HEADER_DEVICE_TYPE_VALUE)
                    .addHeader(HEADER_PUSH_REGISTRATION_ID_KEY, token)
                    .addHeader(HEADER_APP_VERSION_KEY, appVersion)
                    .addHeader(HEADER_API_VERSION_KEY, HEADER_API_VERSION_VALUE)
                    .addHeader(HEADER_OS_VERSION_KEY, android.os.Build.VERSION.RELEASE)
                    .addHeader(HEADER_USER_ID_KEY, CommonPreferences(mContext).userId)
                    .addHeader(HEADER_SYNC_DATE_KEY, lastSyncDate)
                    .addHeader(HEADER_BUILD_VERSION, buildVersion.toString())
                    .build()

            return chain.proceed(request)
        }

        return chain.proceed(chain.request())
    }
}

@RepositoryModule

@Module
class RepositoryModule {

    @Provides
    @Singleton
    internal fun provideAuthRepository(apiUrls: APIUrls): PostRepository {
        return PostRepository(apiUrls)
    }
}

@PostScreenModule

@Module
class PostScreenModule {
    @Provides
    internal fun providePostViewModel(
        application: Application,
        postRepository: PostRepository
    ): PostViewModel {
        return PostViewModel(application, postRepository)
    }
}

@PostScreenActivity

class PostScreen : BaseActivity<ActivityPostScreenBinding, PostViewModel>() {

    @Inject
    lateinit var mPostViewModel: PostViewModel

    override fun getLayoutId(): Int = R.layout.activity_post_screen

    override fun getBindingVariable(): Int = BR.viewModel

    override fun getViewModel(): PostViewModel = mPostViewModel

    override fun initialization() {
        mPostViewModel.viewModelScope.launch(Dispatchers.Main) {
            mPostViewModel.postProcess()
        }
    }
}

@PostScreenViewModel

class PostViewModel @Inject constructor(
    application: Application,
    val postRepository: PostRepository
) : BaseViewModel(application, postRepository) {

    var loading = MutableLiveData<Boolean>()

    init {
        loading.value = false
    }

    suspend fun postProcess() {
        withContext(Dispatchers.IO) {
            postRepository.getPostData()
        }
    }
}

@PostRepositoryClass

@Singleton
class PostRepository @Inject constructor(private val apiUrls: APIUrls) {

    @SuppressLint("CheckResult")
    suspend fun getPostData() {
        apiUrls.getData("https://jsonplaceholder.typicode.com/posts/1")
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ data ->
                println("DATA RECEIVED ::: $data")
            }, { e ->
                Timber.d("Exception :: ${e.message}")
            })
    }

@APIUrlsInterface

@Singleton
interface APIUrls {
    @GET
    suspend fun getData(@Url url: String): Observable<PostResponse>
}

@PostResponseModel

data class PostResponse(
    @SerializedName("userId")
    val userId: Int,
    @SerializedName("id")
    val id: Int,
    @SerializedName("title")
    val title: String,
    @SerializedName("body")
    val body: String
) : Serializable

Upvotes: 1

Views: 2482

Answers (1)

akarnokd
akarnokd

Reputation: 69997

The problem turns out to be having the interface mehtod as both suspend and Observable. Retrofit puts priority on the suspend and thinks the return type should be Observable, but Observable is not a serializable data container.

The solution is to either remove suspend or unwrap the datatype of the API call:

@Singleton
interface APIUrls {
    @GET
    fun getData(@Url url: String): Observable<PostResponse>

    // or

    suspend fun getData(@Url url: String): PostResponse
} 

Upvotes: 8

Related Questions