Reputation: 258
The problem starts with getParcelableArrayListExtra
doesn't support type check when we try to set it to a variable. Let me give an example as basic as I can.
A User Class.
import kotlinx.parcelize.Parcelize
import android.os.Parcelable
@Parcelize
data class UserClass(
var name: String? = null,
var text: String? = null,
var age: Int? = null
) : Parcelable
The random class which we'll try to set to the User variable.
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class MessageClass(
val title: String?, = Constant.STRING_EMPTY
val text: String? = Constant.STRING_EMPTY
) : Parcelable
The class that fills intent
class FillIntentClass(){
//Let's say one of the developers added the MessageClass object inside our intent.
//Or BE sent the wrong type of object and I passed its value to the intent.
private fun DummyFunctionToSetIntent(){
val messageList = arraylistOf(MessageClass(title = "hello",text ="dummy text")
intent.putParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA, messageList)
}
}
Test class
class MyTestClass(){
// UserList variable
private var mUserList: ArrayList<UserClass>? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
with(intent) {
// In this situation, mUserList became the type of ArrayList<MessageClass>
// But it shouldn't be possible. Because it must accept only ArrayList<UserClass>
// And that causes mostly crashes when the other code parts use it.
mUserList = getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA)
// mUserList now pretend its like ArrayList<MessageClass>. But i set it as ArrayList<UserClass> at the top of the class.
// The best way to solve this is to type check with as?. If the type is not as expected it must return null.
// But I cannot use type check here. It gives me a "Not enough information to infer type variable T" error.
mUserList = getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA) as? ArrayList<UserClass> //(compile error here on IDE)
// So I had to come out with the below solution. But I cannot say it's the best practice.
if (getParcelableArrayListExtra<UserClass>(EXTRA_PAYMENT_OPTIONS_EXTRA)
?.filterIsInstance<UserClass>()?.isNotEmpty() == true
) {
mUserList = getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA)
}
}
}
}
Type check(as,as?) works with getParcelable
functions as expected. But when it comes to the getParcelableArrayListExtra
it just doesn't work and gives compile error as I explained above.
Do you have any knowledge of what's the best option for as, as?
check? And how it's possible for mUserList
to accept a different type of Array and pretend like it?
Upvotes: 1
Views: 2285
Reputation: 1303
You don't cast the ParcelableArrayList into an ArrayList, you create it using an ArrayList when you retrieve it just like how the extra was emitted:
mUserList = ArrayList<UserClass>(getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA))
Upvotes: 1
Reputation: 211
Check the API level and code accordingly:
if (Build.VERSION.SDK_INT >= 33) {
data = intent.getParcelableExtra (String name, Class<T> clazz)
}else{
data = intent.getParcelableExtra("data")
}
Also you can use these extensions for bundle and intent:
inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? = when {
SDK_INT >= 33 -> getParcelableExtra(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelableExtra(key) as? T
}
inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? = when {
SDK_INT >= 33 -> getParcelable(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelable(key) as? T
}
Upvotes: 0
Reputation: 95626
This is a mess for a few reasons:
Parcelable
, Bundle
, Intent
, ArrayList
) are actually JavaI would split the problem into 2 parts:
ArrayList
into ArrayList<Parcelable>
ArrayList<Parcelable>
into the expected typeUpvotes: 0