11

I have a class that I use as my data model for a RecyclerViewin order to pass object of this class from one activity to another via Intent I had to make it Parcelable

The problem is now that I am being able to send the object from one activity to another and retrieve it, so that the app does not crash, but keeps throwing ClassNotFoundException in the logcat screen.

What am I doing wrong?

----> Person.kt

@Parcelize
class Person(var name: String, var username: String, var address: String, val avatar: Int) : Parcelable

----> in MainActivity.kt

val intent = Intent(this, ProfilePage::class.java)
        intent.putExtra("clicked_person",person)
        startActivity(intent)

---->. onCreate() in ProfilePAge.kt

var person  = intent.getParcelableExtra<Person>("clicked_person") as Person

And the Exception

E/Parcel: Class not found when unmarshalling: com.example.testkot.kotlinapp.Person
                                         java.lang.ClassNotFoundException: com.example.testkot.kotlinapp.Person

Please remember that the app does not crash, it keeps working but shows the exception in logcat

Malwinder Singh
  • 6,644
  • 14
  • 65
  • 103
codeKiller
  • 5,493
  • 17
  • 60
  • 115
  • Where is the exception being thrown? Please show the entire stack trace? Do you put this `Intent` with "extras" in a `Notification` or send it to `AlarmManager`? – David Wasser Oct 13 '17 at 12:53
  • please refer this ans https://stackoverflow.com/questions/33551972/is-there-a-convenient-way-to-create-parcelable-data-classes-in-android-with-kotl – KuLdip PaTel Oct 14 '17 at 09:50
  • have you tried putting person object in bundle and then putting this bundle in intent extra? – Malwinder Singh Oct 16 '17 at 02:55
  • @KuLdipPaTel if you check my code for `Person.kt` it matches the accepted answer you are referring to in your comment – codeKiller Oct 16 '17 at 08:28
  • @MalwinderSingh is it any major difference using `Bundle` or passing the parameter as I am doing on `MainActivity.kt`?? – codeKiller Oct 16 '17 at 08:29
  • Well, I do not know why, but using a `Bundle` for sending the `Parcelable` still works and does NOT throw any exception – codeKiller Oct 16 '17 at 08:40

6 Answers6

25

After testing a solution in a comment, the following works without throwing any exception

Sending theParcelable via Bundle

val intent = Intent(this, ProfilePage::class.java)
var bundle = Bundle()
bundle.putParcelable("selected_person",person)
intent.putExtra("myBundle",bundle)
startActivity(intent)

Recovering Parcelable

val bundle = intent.getBundleExtra("myBundle")
var person  = bundle.getParcelable<Person>("selected_person") as Person

However, I do not know the difference with my old code in the question and why that old code throws Exception and why this new one does not throw

codeKiller
  • 5,493
  • 17
  • 60
  • 115
  • 1
    No idea neither why it's working but your solution make it work. Thanks. – stankocken Oct 18 '17 at 14:03
  • 2
    I second that... this worked. For some reason when you use Intent.putExtra(CustomParcellableClass) it doesn't store the classloader information. Anyways thank you for the solution :D – Abhishek Dewan Feb 21 '18 at 21:03
  • I couldn't figure out how to recover the Parcelable from the savedInstanceState, but adding the line `as MyClass` did the trick. Oddly the type arguments within the angle brackets generate a warning saying that it's unnecessary and can be inferred. If I remove that, I get a compile error saying that the inference fails. Make up your mind, kotlin! – SMBiggs Jan 18 '20 at 06:40
5

I've prepared the extension functions below for your convenience to use the Kotlin Parcelables without any warnings.

fun Intent.putParcel(key: String = "parcel_key", parcel: Parcelable) {
    val bundle = Bundle()
    bundle.putParcelable(key, parcel)
    this.putExtra("parcel_bundle", bundle)
}

fun <T : Parcelable> Intent.getParcel(key: String = "parcel_key"): T? {
    return this.getBundleExtra("parcel_bundle")?.getParcelable(key)
}

Usage:

//Put parcel
intent.putParcel(parcel = Person()) //any Parcalable

//Get parcel
val person: Person?  = intent.getParcel() //auto converts using Generics
Gunhan
  • 6,807
  • 3
  • 43
  • 37
2

I guess this is a compatibility issue between Kotlin and Java for the moment. I found an easy solution that allows using only Kotlin and without boilerplate.

val intent = Intent(this, ProfilePage::class.java).apply {
    extras?.putParcellable("clicked_person",person)
}

startActivity(intent)

And then to retrieve the value you should use:

var person = intent?.extras?.getParcellable<Person>("clicked_person")

NB: you don't need to cast as Person

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Blake
  • 57
  • 5
1

For anyone reading this in 2022, starting from Android Tiramisu(API 33) getParcellable<Any> has been deprecated and we've to use getParcelable(@Nullable String key, @NonNull Class<T> clazz).

0
var bundle = intent.extras
var person = bundle.getParcelable<Person>("selected_person")

The above code is working for me. You can cut short the code as follows.

var person = intent.extras.getParcelable<Person>("selected_person")
mujeeb.omr
  • 499
  • 2
  • 12
0

In the first Activity:

 val intent = Intent(this@MainActivity, DetailsActivity::class.java)
        intent.putExtra(Const.SELECTED_NOTE, noteList[position])
        startActivity(intent)

In the Second Activity:

val note = intent.getParcelableExtra<Note>(Const.SELECTED_NOTE)

EXPLAIN: First you should Put Parcelable Data and call getParcelableExtra in next activity! in getParcelableExtra you should call Data class (here is Note ) and Key Of Bundle (here is Const.SELECTED_NOTE)!

Sana Ebadi
  • 6,656
  • 2
  • 44
  • 44