【问题标题】:Transition from AsyncTask to Coroutines in Retrofit: IllegalArgumentExceptionRetrofit 中从 AsyncTask 到 Coroutines 的转换:IllegalArgumentException
【发布时间】:2019-12-02 19:58:28
【问题描述】:

在从AsyncTask(之前)到Kotlin Coroutines(之后)进行最小更改时,我不断收到错误IllegalArgumentException。请注意,AsyncTask 的代码按预期工作。

注意:Retrofit 正在调用我自己的 .php 脚本,该脚本返回一些在 json 字符串中编码的对象 SimpleResultObject

变更前:

改造:

@FormUrlEncoded
@POST("activity_signup.php")
fun activitySignUp(
        @Field("activity_id") activityId: Int,
        @Field("user_id") userId: Int) : Call<SimpleResultObject> 

Activity(AsyncTask 内部):

@Override
protected doInBackground(...) {

        val gson = GsonBuilder().setLenient().create()
            val retrofit = Retrofit.Builder()
                    .baseUrl(LOCALHOST_URL)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build()

            val service = retrofit.create(RetrofitAPI::class.java)
            val call = service.activitySignUp(activity_id, userId)
            call.enqueue(Callback<SimpleResultObject>() {}

@onResponse 方法中接收对象并正常继续。

改动后:

改造:

 @FormUrlEncoded
 @POST("activity_signup.php")
 suspend fun activitySignUp(
            @Field("activity_id") activityId: Int,
            @Field("user_id") userId: Int): SimpleResultObject

活动:

 fun signUp() {
        myActivityScope.launch(Dispatchers.Main) {

            val gson = GsonBuilder().setLenient().create()
            val retrofit = Retrofit.Builder()
                    .baseUrl(LOCALHOST_URL)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build()

            val service = retrofit.create(RetrofitAPI::class.java)
            try {
                val result = service.activitySignUp(specificResultObject.activityId, userId)
                } catch (t:Throwable)

service.activitySignUp 线路调用上抛出java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #3) for method RetrofitAPI.activitySignUp

注意:myActivityScope 是托管活动结束时结束的服装CoroutineScope

我已经尝试了所有我能记住的方法:添加 OkHttpClient,更改为 MoshiConverterFactory,尝试其他 CoroutineScopes 和 Dispatchers,...

编辑:问题可能出在我的.php 一方,因为Exeption 高于我的参数编号(可能是空结果?),但不知道为什么以前有效的东西现在不起作用。

【问题讨论】:

  • 那次崩溃是来自retrofit.create() 电话吗?如果是这样,请尝试将您的 gsonretrofitservice 声明/初始化移到协程之外。这些都不会在运行时发生变化,也不需要Context 或任何奇怪的东西,因此您可以将它们作为包含signUp() 的任何类的属性。另外,您使用的是哪个版本的 Retrofit?
  • @CommonsWare 感谢您的帮助!抱歉,如果不够清楚:错误来自“service.activitySignUp”调用。我正在使用 Retrofit 2.5.0 版。我希望该错误必须来自 .php 方面,但不知道为什么它现在无法正常工作,而之前它工作得很好。
  • 您仍然可以尝试将 gsonretrofitservice 声明/初始化移到协程之外,以防万一。这个错误没有多大意义,因为activitySignUp()只有两个参数,所以我不知道“参数#3”是什么意思。
  • @CommonsWare 我已将所有内容移到协程之外,但问题仍然存在。无论如何,感谢您的帮助 - 希望会有人知道如何解决这个问题!
  • @Androidz 我认为是库版本的原因,因为 Retrofit 2.5.0 不支持挂起功能,您正在使用这种方式,更新到 2.6.0 应该可以解决您的问题

标签: android retrofit2 kotlin-coroutines


【解决方案1】:

根据对问题的回答,我对代码进行了一些修改并设法解决了问题。正如@Mohammad Sianaki 指出的那样,最重要的是将改造版本从 25.0.0 提高到 26.0.0 以解决问题

因此,对于可能在参数编号上方的参数中获得 IllegalArgumentException 的其他所有人 - 考虑检查改造版本

特别感谢所有提供帮助的人,尤其是@CommonsWare!

【讨论】:

    【解决方案2】:

    所提供的代码存在一些结构性问题。

    首先,似乎正在为每个 API 调用创建一个改造对象。因此,它应该是应用程序的所有 API 调用之一。

    其次,网络操作应该在非主线程中执行。对于协程,它们应该在非主上下文中调用,例如Dispatchers.IO

    第三,我认为你应该在 API 函数中返回 Response&lt;SimpleResultObject&gt; 而不是 SimpleResultObject

    假设上面,我写了一些代码希望解决这个问题。因为我认为问题信息中存在一些隐藏因素。

    build.gradle

    dependencies {
        implementation 'com.squareup.retrofit2:retrofit:2.6.1'
        implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
    
        implementation 'com.squareup.okhttp3:logging-interceptor:3.14.1'
        implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.14.1'
        implementation 'com.squareup.okhttp3:okhttp:3.14.1'
    }
    

    RetrofitAPI.kt

    import retrofit2.Response
    import retrofit2.http.Field
    import retrofit2.http.FormUrlEncoded
    import retrofit2.http.POST
    
    interface RetrofitAPI {
    
        @FormUrlEncoded
        @POST("activity_signup.php")
        suspend fun activitySignUp(
            @Field("activity_id") activityId: Int,
            @Field("user_id") userId: Int
        ): Response<SimpleResultObject>
    
        // Other api declarations ...
    
    }
    

    BaseApiManager.kt

    import okhttp3.JavaNetCookieJar
    import okhttp3.OkHttpClient
    import okhttp3.logging.HttpLoggingInterceptor
    import retrofit2.Retrofit
    import retrofit2.converter.gson.GsonConverterFactory
    import java.net.CookieManager
    import java.util.concurrent.TimeUnit
    
    abstract class BaseApiManager(endPoint: String) {
    
        protected val retrofitAPI: RetrofitAPI =
            createAdapter(endPoint)
                .create(RetrofitAPI::class.java)
    
        private fun createAdapter(choice: String): Retrofit {
            return Retrofit.Builder()
                .baseUrl(choice)
                .client(createHttpClient())
                .addConverterFactory(GsonConverterFactory.create()).build()
        }
    
        companion object {
    
            private fun createHttpClient(): OkHttpClient {
                val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
                val cookieHandler = CookieManager()
                val interceptor = HttpLoggingInterceptor()
                interceptor.level = HttpLoggingInterceptor.Level.BODY
                httpClient.interceptors().add(interceptor)
    
                httpClient.cookieJar(JavaNetCookieJar(cookieHandler))
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
    
                return httpClient.build()
            }
        }
    
    }
    

    ApiManager.kt

    private const val API_END_POINT = "https://your.webservice.endpoint/"
    
    object ApiManager : BaseApiManager(API_END_POINT) {
    
        suspend fun activitySignUp(
            activityId: Int,
            userId: Int
        ) = retrofitAPI.activitySignUp(activityId, userId)
    
        // Other api implementations ...
    
    }
    


    用法:

    fun signUp() {
        myActivityScope.launch(Dispatchers.IO) {
            ApiManager.activitySignUp(activityId, userId).also { response ->
                when {
                    response.isSuccessful -> {
                        val result = response.body()
                        result?.apply {
    
                            // do something with the result
                        }
                    }
                    else -> {
                        val code = response.code()
                        val message = response.message()
    
                        // do something with the error parameters...
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 感谢您的回复!我将解决您提到的所有问题。首先,我正在创建单个服务对象,但为了简单起见,我只是像这样包含它。其次,Retrofit 上的“挂起”函数会在 T 后自动调用,所以这不是问题。第三,结果中不需要“响应”。
    猜你喜欢
    • 2016-03-17
    • 1970-01-01
    • 2016-06-28
    • 2015-05-10
    • 2015-09-29
    • 2016-02-13
    • 1970-01-01
    • 2016-06-06
    • 2019-10-05
    相关资源
    最近更新 更多