Android中如何让Retrofit的Call对象实现Parcelable?
Hey there, great question! Let's break this down because Retrofit's Call<T> isn't built to be Parcelable out of the box, and trying to force it directly will cause headaches. Here's what you need to know and the best ways to solve this:
Call<T> Parcelable Retrofit's Call<T> interface (and its default OkHttpCall implementation) holds references to non-serializable objects like:
- The
OkHttpClientinstance - Raw
Requestobjects with headers/body data - Internal service method metadata
- Callback references (if already attached)
These can't be safely written to a Parcel, and even if you hacked around it, a Call is a one-time use object—once executed, it can't be reused, making the transferred instance useless in most cases.
The most reliable approach is to pass the parameters needed to create the Call instead of the Call itself, then rebuild it in the target Activity. Here's how to do it:
Step 1: Create a Parcelable Params Class
Wrap all the data needed to build your Call into a Parcelable data class. For example, if you're fetching user data with an ID and auth token:
data class UserFetchParams( val userId: String, val authToken: String ) : Parcelable { constructor(parcel: Parcel) : this( parcel.readString() ?: "", parcel.readString() ?: "" ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(userId) parcel.writeString(authToken) } override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator<UserFetchParams> { override fun createFromParcel(parcel: Parcel): UserFetchParams = UserFetchParams(parcel) override fun newArray(size: Int): Array<UserFetchParams?> = arrayOfNulls(size) } }
Step 2: Pass the Params via Intent
In your source Activity, package the params and send them to the target:
val fetchParams = UserFetchParams("user_123", "Bearer abc123xyz") val intent = Intent(this, UserDetailsActivity::class.java) intent.putExtra("USER_FETCH_PARAMS", fetchParams) startActivity(intent)
Step 3: Recreate the Call in the Target Activity
Pull the params from the intent, then use your Retrofit service instance to build the Call again:
// Get your singleton Retrofit service instance (adjust this to match your setup) val apiService = RetrofitClient.instance.getApiService() // Retrieve params from intent val fetchParams = intent.getParcelableExtra<UserFetchParams>("USER_FETCH_PARAMS") // Recreate the call fetchParams?.let { params -> val userCall = apiService.getUser(params.userId, params.authToken) // Execute the call as usual userCall.enqueue(object : Callback<User> { override fun onResponse(call: Call<User>, response: Response<User>) { // Handle successful response } override fun onFailure(call: Call<User>, t: Throwable) { // Handle error } }) }
If you have complex request logic and want to avoid duplicating Call creation code, you can create a sealed Parcelable factory class that encapsulates the creation logic:
sealed class ApiCallFactory : Parcelable { abstract fun createCall(apiService: ApiService): Call<*> data class UserFetchFactory(val userId: String, val authToken: String) : ApiCallFactory() { constructor(parcel: Parcel) : this( parcel.readString() ?: "", parcel.readString() ?: "" ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(userId) parcel.writeString(authToken) } override fun describeContents(): Int = 0 override fun createCall(apiService: ApiService): Call<User> { return apiService.getUser(userId, authToken) } companion object CREATOR : Parcelable.Creator<UserFetchFactory> { override fun createFromParcel(parcel: Parcel): UserFetchFactory = UserFetchFactory(parcel) override fun newArray(size: Int): Array<UserFetchFactory?> = arrayOfNulls(size) } } // Add other factory classes for different requests here }
Then pass the factory via intent:
val callFactory = ApiCallFactory.UserFetchFactory("user_123", "Bearer abc123xyz") val intent = Intent(this, UserDetailsActivity::class.java) intent.putExtra("API_CALL_FACTORY", callFactory) startActivity(intent)
And recreate the call in the target:
val callFactory = intent.getParcelableExtra<ApiCallFactory>("API_CALL_FACTORY") val apiService = RetrofitClient.instance.getApiService() callFactory?.let { factory -> val userCall = factory.createCall(apiService) as? Call<User> userCall?.enqueue(...) }
Avoid trying to serialize the Call itself—it's not designed for that, and you'll run into unpredictable issues like memory leaks, invalid state, or runtime crashes. The approaches above keep your code clean, maintainable, and aligned with Retrofit's design.
内容的提问来源于stack exchange,提问作者Klausar

