java.lang.RuntimeException: 没有参数调用 public io.reactivex.Observable() 失败

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

您好,我目前正在准备一个带有协程的 MVVM 的简单演示示例,我面临以下问题。请查看代码,看看代码有什么问题。

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.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.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.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.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)

依赖配置

'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'

申请代码如下

@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

问题出在接口方法同时为 suspendObservable。 Retrofit优先考虑suspend,认为return类型应该是Observable,但是Observable不是可序列化的数据容器。

解决方案是删除 suspend 或解包 API 调用的数据类型:

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

    // or

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