Текст пустого ответа в производственной сборке после добавления адаптера вызовов Android RetrofitAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Текст пустого ответа в производственной сборке после добавления адаптера вызовов Android Retrofit

Сообщение Anonymous »

Ожидание:
Я добавил адаптер вызовов в приложение, чтобы моя сетевая служба оборачивала все, что она возвращает, в Result и сопоставляла все исключения с моим CustomException.
Удивительно, что в режиме отладки все работает нормально.
Проблема:
Даже когда я подключаю средство ведения журнала OkHttp в производственной сборке, я вижу, что запрос возвращает правильный ответ (не null), но в репозитории получаю null:
authService.login(data).fold(onSuccess = { response -> // returns null }
Можно ли заставить это работать с помощью системной оболочки Result?
Спасибо за помощь :)
Мои попытки решить проблему:
Предполагаю, что это может быть связано с очисткой файлов с помощью R8/ProGuard. Я тщательно проверил, добавлена ​​ли аннотация @keep в нужных местах. Я также попробовал использовать собственную оболочку ApiResult вместо Result, которую, предположительно, R8/ProGuard может неправильно очистить, но у меня возникли серьезные трудности с ее реализацией.
Код
Это мой собственный адаптер вызовов:

@Keep
@Suppress("UNCHECKED_CAST")
class ResultCallAdapterFactory : CallAdapter.Factory() {

override fun get(
returnType: Type,
annotations: Array,
retrofit: Retrofit
): CallAdapter? {
if (getRawType(returnType) != Call::class.java || returnType !is ParameterizedType) {
return null
}

// get inner type
val upperBound = getParameterUpperBound(0, returnType)

return if (upperBound is ParameterizedType && upperBound.rawType == Result::class.java) {
// return new call adapter that change Call to ResultCall
object : CallAdapter {
override fun responseType(): Type = getParameterUpperBound(0, upperBound)

override fun adapt(call: Call): Call =
ResultCall(call) as Call
}
} else {
null
}
}
}

Реализация вызова:

@Keep
class ResultCall(val delegate: Call) :
Call {
override fun enqueue(callback: Callback) {
delegate.enqueue(
object : Callback {
override fun onResponse(call: Call, response: Response) {
// server response status code 200–299
if (response.isSuccessful) {
Timber.d("response: ${response.body()}")
callback.onResponse(
this@ResultCall,
Response.success(
response.code(),
Result.success(response.body()!!)
)
)
} else {
Timber.d("response has error")
// server response status code 404, 500
val exception = when (response.code()) {
401, 403 -> CustomException.Unauthorized()

404 -> CustomException.NotFound()

else -> {
if (response.message().isEmpty()) CustomException.Unknown()
else CustomException.Custom(response.message())
}
}
callback.onResponse(
this@ResultCall,
Response.success(
Result.failure(
exception
)
)
)
}
}

// call with connection errors
override fun onFailure(call: Call, t: Throwable) {
val exception = when (t) {
is IOException -> CustomException.NoConnection()
is HttpException -> CustomException.Unknown()
else -> {
if (t.localizedMessage.isNullOrEmpty()) CustomException.Unknown()
else CustomException.Custom(t.localizedMessage!!)
}
}
callback.onResponse(
this@ResultCall,
Response.success(Result.failure(exception))
)
}
}
)
}
override fun execute(): Response =
Response.success(Result.success(delegate.execute().body()!!))
override fun isExecuted(): Boolean = delegate.isExecuted
override fun cancel() = delegate.cancel()
override fun isCanceled(): Boolean = delegate.isCanceled
override fun clone(): Call = ResultCall(delegate.clone())
override fun request(): Request = delegate.request()
override fun timeout(): Timeout = delegate.timeout()
}

Сетевая служба: (я добавил аннотацию сохранения к LoginRequest и LoginResponse.)
@Keep
interface AuthService {
@POST("/Myapp/Login")
suspend fun login(@Body body: LoginRequest) : Result // Here is the problem, it returns null instead of LoginResponse.

@POST("/Myapp/Logout")
suspend fun resetToken() : Result

@GET
suspend fun resetSynodiPassword(
@Url absoluteUrl: String,
@Query("login") email: String
): Result
}

Файл ProGuard:
-keepattributes Signature
-keepattributes *Annotation*
-keepattributes InnerClasses
-keepattributes EnclosingMethod
-keep class kotlin.Metadata { *; }

# remove logging
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}

# Keep all fields for Gson reflection
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName *;
@com.google.gson.annotations.Expose *;
}

# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken



Подробнее здесь: https://stackoverflow.com/questions/798 ... all-adapte
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»