My app crashes when opening with this message:
java.lang.IllegalArgumentException: Unable to create converter for java.util.List<com.dov4k1n.vkinternshiptask.network.Products>
for method ProductsApiService.getProductsList
at retrofit2.Utils.methodError(Utils.java:54)
at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:126)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:85)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202)
at retrofit2.Retrofit$1.invoke(Retrofit.java:160)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy2.getProductsList(Unknown Source)
at ...
I want to retrieve products' info from web server https://dummyjson.com/products with Retrofit2 library using Kotlin and Android Studio.
This is my json:
{
"products": [
{
"id": 1,
"title": "iPhone 9",
"description": "An apple mobile which is nothing like apple",
"price": 549,
"discountPercentage": 12.96,
"rating": 4.69,
"stock": 94,
"brand": "Apple",
"category": "smartphones",
"thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg",
"images": [
"https://cdn.dummyjson.com/product-images/1/1.jpg",
"https://cdn.dummyjson.com/product-images/1/2.jpg",
"https://cdn.dummyjson.com/product-images/1/3.jpg",
"https://cdn.dummyjson.com/product-images/1/4.jpg",
"https://cdn.dummyjson.com/product-images/1/thumbnail.jpg"
]
},
{
"id": 2,
"title": "iPhone X",
"description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...",
"price": 899,
"discountPercentage": 17.94,
"rating": 4.44,
"stock": 34,
"brand": "Apple",
"category": "smartphones",
"thumbnail": "https://cdn.dummyjson.com/product-images/2/thumbnail.jpg",
"images": [
"https://cdn.dummyjson.com/product-images/2/1.jpg",
"https://cdn.dummyjson.com/product-images/2/2.jpg",
"https://cdn.dummyjson.com/product-images/2/3.jpg",
"https://cdn.dummyjson.com/product-images/2/thumbnail.jpg"
]
},
{
"id": 3,
"title": "Samsung Universe 9",
"description": "Samsung's new variant which goes beyond Galaxy to the Universe",
"price": 1249,
"discountPercentage": 15.46,
"rating": 4.09,
"stock": 36,
"brand": "Samsung",
"category": "smartphones",
"thumbnail": "https://cdn.dummyjson.com/product-images/3/thumbnail.jpg",
"images": [
"https://cdn.dummyjson.com/product-images/3/1.jpg"
]
}
],
"total": 100,
"skip": 0,
"limit": 30
}
This is my Products data class:
data class Products(
val id: Int,
val title: String,
val description: String,
val price: Double,
val discountPercentage: Double,
val rating: Double,
val stock: Int,
val brand: String,
val category: String,
val thumbnail: String,
val images: List<String>
)
This is my ProductsApiService:
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.http.GET
private const val BASE_URL = "https://dummyjson.com"
private val retrofit = Retrofit.Builder()
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.baseUrl(BASE_URL)
.build()
interface ProductsApiService {
@GET("/products")
suspend fun getProductsList(): List<Products>
}
object ProductsApi {
val retrofitService : ProductsApiService by lazy {
retrofit.create(ProductsApiService::class.java)
}
}
It seems like getProductsList() function doesn't work as expected
This is my ProductsViewModel where I use this api service, if it helps:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dov4k1n.vkinternshiptask.network.ProductsApi
import kotlinx.coroutines.launch
import java.io.IOException
sealed interface ProductsUiState {
data class Success(val products: String) : ProductsUiState
object Error: ProductsUiState
object Loading: ProductsUiState
}
class ProductsViewModel : ViewModel() {
var productsUiState: ProductsUiState by mutableStateOf(ProductsUiState.Loading)
private set
init {
getProducts()
}
fun getProducts() {
viewModelScope.launch {
productsUiState = try {
val listResult = ProductsApi.retrofitService.getProductsList()
ProductsUiState.Success(
"Success: ${listResult.size} products retrieved"
)
}
catch (e: IOException) {
ProductsUiState.Error
}
}
}
}
I use this in dependencies:
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// Retrofit with Kotlin serialization Converter
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
// Kotlin serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
and this plugin:
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22"
I did almost everything as in the Google's codelab https://developer.android.com/codelabs/basic-android-kotlin-compose-getting-data-internet
But in the codelab json https://android-kotlin-fun-mars-server.appspot.com/photos is like this:
[
{
"id": "424905",
"img_src": "https://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000MR0044631300503690E01_DXXX.jpg"
},
{
"id": "424906",
"img_src": "https://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"
},
{
"id": "424907",
"img_src": "https://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000MR0044631290503689E01_DXXX.jpg"
},
{
"id": "424908",
"img_src": "https://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
}
]
MarshPhoto data class:
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class MarsPhoto(
val id: String,
@SerialName(value = "img_src")
val imgSrc: String
)
MarsApiService:
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.http.GET
private const val BASE_URL =
"https://android-kotlin-fun-mars-server.appspot.com"
private val retrofit = Retrofit.Builder()
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.baseUrl(BASE_URL)
.build()
interface MarsApiService {
@GET("photos")
suspend fun getPhotos(): List<MarsPhoto>
}
object MarsApi {
val retrofitService : MarsApiService by lazy {
retrofit.create(MarsApiService::class.java)
}
}
MarsViewModel:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.marsphotos.network.MarsApi
import kotlinx.coroutines.launch
import java.io.IOException
sealed interface MarsUiState {
data class Success(val photos: String) : MarsUiState
object Error: MarsUiState
object Loading: MarsUiState
}
class MarsViewModel : ViewModel() {
/** The mutable State that stores the status of the most recent request */
var marsUiState: MarsUiState by mutableStateOf(MarsUiState.Loading)
private set
/**
* Call getMarsPhotos() on init so we can display status immediately.
*/
init {
getMarsPhotos()
}
/**
* Gets Mars photos information from the Mars API Retrofit service and updates the
* [MarsPhoto] [List] [MutableList].
*/
fun getMarsPhotos() {
viewModelScope.launch {
marsUiState = try {
val listResult = MarsApi.retrofitService.getPhotos()
MarsUiState.Success(
"Success: ${listResult.size} Mars photos retrieved"
)
}
catch (e: IOException) {
MarsUiState.Error
}
}
}
}
Their code doesn't crash and work as expected. Can't understand why my code does crash
I didn't understand well json format, figured it out only after I posted this question.
This is an element, which contains List called "products", Int called "total", Int called "skip" and Int called "limit":
So in my ProductsApiService I should get proper response:
P.S. also I removed "/" in
@GET("/products"). Maybe that was significant too.ProductsResponse:
Products:
Now it works as I expected.