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!
从 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!