Moshi JsonAdapter для обработки Observable ‹Bitmap› в модернизации

В настоящее время я декодирую Bitmap следующим образом:

@GET("api/users/get_avatar/{userId}/default.png")
fun getAvatar(@Header("ApiToken") apiToken: String, @Path("userId") userId: String): Observable<ResponseBody>

и расшифровываем в ViewModel

val avatar = it()?.let { body ->
   val stream = body.byteStream()
   BitmapFactory.decodeStream(stream)
}

Однако я хотел бы использовать для этого более элегантный Moshi JsonAdapter.

Мой звонок выглядит так:

@GET("api/users/get_avatar/{userId}/default.png")
fun getAvatar(@Header("ApiToken") apiToken: String, @Path("userId") userId: String): Observable<Bitmap>

Добавляю адаптер:

return Moshi.Builder()
        .add(BitmapAdapter())

Однако, скорее всего, мой адаптер неправильный:

private class BitmapAdapter {

    @ToJson
    fun toJson(value: Bitmap): String {
        return value.encodeBase64()
    }

    @FromJson
    fun fromJson(value: String): Bitmap {
        return value.decodeBase64()
    }
}

Как это должно выглядеть?


person qbait    schedule 30.12.2019    source источник


Ответы (1)


Moshi предназначен для анализа JSON, а не для прямого декодирования изображений. Если вы хотите получить Bitmap от клиента Retrofit, вам нужно, чтобы Converter.Factory поставлялся напрямую в Retrofit.

Пример:

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import okhttp3.ResponseBody
import retrofit2.Converter
import retrofit2.Retrofit
import java.lang.reflect.Type

class BitmapConverterFactory : Converter.Factory() {

    override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? {
        return if (type == Bitmap::class.java) {
            Converter<ResponseBody, Bitmap> {
                value -> BitmapFactory.decodeStream(value.byteStream())
            }
        } else {
            null
        }
    }
}

И предоставьте его везде, где вы создаете свой экземпляр Retrofit:

Retrofit.Builder()
    .baseUrl("https://myapi.com")
    .addConverterFactory(BitmapConverterFactory())
    .addConverterFactory(MoshiConverterFactory.create())
    .build()

Изменить: изначально я ошибся в BitmapCoverterFactory. Сравнение type изначально было с Bitmap::javaClass, должно быть Bitmap::class.java.

person Jonba    schedule 30.12.2019
comment
К сожалению, я все еще получаю Caused by: java.lang.IllegalArgumentException: Platform class android.graphics.Bitmap (with no annotations) requires explicit JsonAdapter to be registered at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:60) at com.squareup.moshi.Moshi.adapter(Moshi.java:137) at com.squareup.moshi.Moshi.adapter(Moshi.java:97) при добавлении BitmapConverterFactory - person qbait; 31.12.2019
comment
@qbait Порядок важен. Когда вы создаете экземпляр Retrofit с помощью конструктора, BitmapConverterFactory должен стоять перед MoshiConverterFactory. В противном случае фабрика преобразователей Moshi попытается взять на себя ответственность. - person Jonba; 31.12.2019
comment
@qbait Я тоже ошибся в BitmapConverterFactory, обновил в ответе. - person Jonba; 31.12.2019
comment
Отлично, он работает с двумя небольшими изменениями: Bitmap::class.java вместо BufferedImage::class.java, и мне нужно было добавить типы в Converter Converter<ResponseBody, Bitmap>. Благодаря тонну! - person qbait; 31.12.2019
comment
Ой, извини! Я делал пример на своей машине на чистом Kotlin (без Android), поэтому я использовал BufferedImage вместо Bitmap. Рад, что это сработало! - person Jonba; 31.12.2019