Kotlin+Retrofit API调用:书籍数组数据模型构建与解析求助
Hey there! Let's break this down step by step since you're new to Kotlin/Android and OOP—no worries, we'll start from the basics and clear up those confusions first.
class vs object Confusion This is a super common point of confusion for folks new to Kotlin, so let's get it straight:
objectin Kotlin is a singleton—there's only ever one instance of it in your app. This is great for things like a global Retrofit instance, utility helpers, or constant values, but not for data models (like your book details). Each book in the API's array is a separate piece of data with its own unique values, so we need multiple instances—this is whereclasscomes in.- Specifically, we'll use Kotlin's
data class—it's built exactly for holding structured data, and automatically generates useful methods likeequals(),hashCode(), andtoString()for you, saving tons of boilerplate code you'd have to write manually in other languages.
First, you need to add Retrofit and a JSON converter to your app's build file (Module-level build.gradle or build.gradle.kts):
// Retrofit core library implementation "com.squareup.retrofit2:retrofit:2.9.0" // Gson converter to parse JSON responses into Kotlin objects implementation "com.squareup.retrofit2:converter-gson:2.9.0"
You can use newer versions if you want, but these are stable and widely used.
Let's assume your API returns a JSON array like this:
[ { "id": 1, "title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "page_count": 180 }, { "id": 2, "title": "1984", "author": "George Orwell", "page_count": 328 } ]
We'll create a data class that mirrors this structure. If the JSON uses snake_case keys (like page_count), use the @SerializedName annotation to map it to a camelCase property in Kotlin:
import com.google.gson.annotations.SerializedName data class Book( val id: Int, val title: String, val author: String, @SerializedName("page_count") val pageCount: Int )
Since the API returns an array, our Retrofit call will return a List<Book>.
This interface defines the exact API call you want to make:
import retrofit2.Call import retrofit2.http.GET interface BookApiService { // Replace the endpoint path with your actual API's path (e.g., "/api/v1/books") @GET("books/details") // Returns a Call wrapping a List<Book> (for callback-style calls) fun getBookDetails(): Call<List<Book>> // For Kotlin Coroutines (recommended for Android, cleaner than callbacks) @GET("books/details") suspend fun getBookDetailsCoroutine(): List<Book> }
Coroutines are the modern, recommended way to handle async work in Android, so we'll focus on that next.
object the Right Way) We only need one instance of Retrofit in our app, so we'll use an object (singleton) to create and hold it:
import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory object RetrofitInstance { // Replace with your API's base URL (e.g., "https://your-api-domain.com/") private const val BASE_URL = "https://api.example.com/" val api: BookApiService by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(BookApiService::class.java) } }
This is the correct use case for object—a single, reusable instance that doesn't need multiple copies.
In Android, it's best to make API calls from a ViewModel to avoid memory leaks. Here's how to do it:
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch class BookViewModel : ViewModel() { fun fetchBooks() { // Use viewModelScope to automatically cancel the coroutine if the ViewModel is destroyed viewModelScope.launch { try { // Call the API and get the list of books val booksList = RetrofitInstance.api.getBookDetailsCoroutine() // Success! Use the booksList (it's a List<Book>) for (book in booksList) { println("Book: ${book.title} by ${book.author} (${book.pageCount} pages)") } } catch (e: Exception) { // Handle errors (network issues, invalid responses, etc.) println("Error fetching books: ${e.message}") } } } }
- Use
data classfor your API data models—each item in the array is a separate instance of this class. - Use
objectonly for things that need to be singletons (like the Retrofit instance, utility helpers, or constants). - Retrofit handles JSON parsing automatically as long as your
data classproperties match the API's JSON structure (use@SerializedNameif the key names don't align).
内容的提问来源于stack exchange,提问作者Zim

