Retrofit 中从 AsyncTask 到协程的转换:IllegalArgumentException

Transition from AsyncTask to Coroutines in Retrofit: IllegalArgumentException

AsyncTask(之前)到 Kotlin Coroutines(之后)进行最小更改时,我不断收到错误 IllegalArgumentException。请注意,代码在 AsyncTask.

下按预期工作

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

变更前:

改造:

@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

Activity:

 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 是在托管 Activity 结束时完成的服装 CoroutineScope

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

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

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

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

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

第三,我认为你应该 return 一个 Response<SimpleResultObject> 而不是 API 函数中的 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...
                }
            }
        }
    }
}

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

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

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