Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage of parceler (@Parcel) with Realm.io (Android) #57

Closed
sam-ghosh opened this issue Dec 23, 2014 · 39 comments
Closed

Usage of parceler (@Parcel) with Realm.io (Android) #57

sam-ghosh opened this issue Dec 23, 2014 · 39 comments

Comments

@sam-ghosh
Copy link

I have the following code which produces the error: Error:Parceler: Unable to find read/write generator for type io.realm.Realm for io.realm.RealmObject.realm

It was working all fine without extends RealmObject , however I want to use Realm to put to database easily. Is there a way to exlcude the RealmObject fields and just use the basic pojo fields for @parcel?

    @Parcel
    public class Feed extends RealmObject{
        int id;
        public String text;
        public String time_created;
        String time_modified;
        int comments_count;
        int likes_count;
        String feed_type;
        int obj_id;
        String image;
        String user_name;
        String user_earthmile_points;
        boolean liked;
        boolean commented;
        boolean is_private;
        String url;
        int feed_creator_id;
  }
@johncarl81
Copy link
Owner

I have not looked into Realm deeply, but I'm disappointed to see it introduces a base class that you have to extend.

There's a couple of options here:

First, you could define a ParcelConverter that gives you the ability to define serialization manually.
Second, we (@loganj) have discussed adding a scan-limit to the processor which would allow you to tell the processor to stop and not process RealmObject or deeper. It seems that this would be the use case for such a feature.

Third, could you define your RealmObject as a wrapper instead (again, I'm nearly completely ignorant on Realm)?:

public class FeedRealmObject extends RealmObject {
    Feed feed;
}

@Parcel
public class Feed {
    //...
}

@johncarl81
Copy link
Owner

I figured I'd go ahead and implement the scan limit (#59) since it was a natural next step. I've also pushed this to 0.2.16-SNAPSHOT if you'd like to try it out.

@sam-ghosh
Copy link
Author

Thanks, should it be in the Maven repo? What should i put in my build.girdle instead of
compile 'org.parceler:parceler:0.2.15'

Many thanks

@johncarl81
Copy link
Owner

You need to add the maven central snapshots repo:

repositories {
    mavenCentral()
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots/"
    }
}

And update the parceler dependency to:

compile 'org.parceler:parceler-api:0.2.16-SNAPSHOT'
provided 'org.parceler:parceler:0.2.16-SNAPSHOT'

Keep in mind that SNAPSHOTS are volatile... without notice I may redact the current functionality. That being said, give it a try and let me know if it works for you.

@sam-ghosh
Copy link
Author

Thanks, i got an error saying:
Error:Could not find org.parceler:parceler-api:0.2.16-SNAPSHOT.
Required by:
TestAndroid:app:unspecified

@sam-ghosh
Copy link
Author

@sam-ghosh
Copy link
Author

Due to constraints with Realm, I need to put variables as public, while Parceler wants private (for speed)

So I need to do :

    @Parcel(analysisLimit = RealObject.class, Parcel.Serialization.METHOD )

is this the right syntax inside the bracket?

Thanks again

@johncarl81
Copy link
Owner

Looks about right:

@Parcel(value = Parcel.Serialization.METHOD, analysisLimit = RealmObject.class)

I'm not sure what you mean by Parceler wants private. Parceler will warn you that it will use reflection if using the Serialization.FIELD technique and it finds private fields. Can you clarify?

@sam-ghosh
Copy link
Author

Apologies, I meant Realm.io wants the class properties to be private (and having getter/setter), while if I put private properties without Serialization.METHOD, Parceler won't accept. hence I need to put both the arguments inside the brackets for both packages (parceler, realm) to work

@johncarl81
Copy link
Owner

Oh OK, yes, Serialization.METHOD will follow the private fields & getter/setter Java Bean standard.

@johncarl81
Copy link
Owner

By the way, I think I'm going to change the name of this Serialization technique to Serialization.BEAN to better represent what standard it follows. Any thoughts on this? (#60)

@johncarl81
Copy link
Owner

@somghosh, how did this work out for you?

@sam-ghosh
Copy link
Author

Thanks a lot @johncarl81 . I used it and it worked out from the Parceler point of view (meaning that the parcelling method stopped at the specified limit). So it works for simple Realm objects.

However, the JSON string I get from sever is as a compound Pojo (class inside a class) and hence things did not work from the Realm point of view. I would still encourage you to put this up as a revision, as for simple objects, Parceler can be used with Realm now.

Many thanks

@johncarl81
Copy link
Owner

Thinking about this more, we could do something like specify exactly what classes are analyzed, like so:

@Parcel(value = Parcel.Serialization.METHOD, analyze = {One.class, Three.class)
class One extends Two{}
class Two extends Three{}
class Three extends BaseClass{}

Which would offer even more flexability. What do you think @somghosh?

@ppamorim
Copy link

+1, not working with ActiveAndroid too.

@sam-ghosh
Copy link
Author

@johncarl81 I think that would solve the problem. Many thanks

@ppamorim
Copy link

@somghosh not working with snapshot, analysisLimit doesn't exist.

@johncarl81
Copy link
Owner

@ppamorim, I've been working on another branch that I pushed out to maven central. You can still get to this feature by building the limit_analysis branch on your own (#59).

@dracek
Copy link

dracek commented Jan 15, 2015

@ppamorim I've tried 0.2.16-SNAPSHOT with ActiveAndroid 3.1.0-SNAPSHOT (and Retrofit) and it seems working flawless so far.

@Parcel(value = Parcel.Serialization.BEAN, analysisLimit = SMS.class)
@Table(name = "SMS")
public class SMS extends Model {
    @SerializedName("id")
    @Column(name = "smsid", index = true, unique = true, onUniqueConflict = Column.ConflictAction.FAIL)
    String id;
    // etc.
}

@johncarl81 many thanks for your work! I really appreciate it!

@johncarl81
Copy link
Owner

@ppamorim, @dracek, @ppamorim Any thoughts on a more specific approach mentioned above:

@Parcel(value = Parcel.Serialization.METHOD, analyze = {One.class, Three.class)
class One extends Two{}
class Two extends Three{}
class Three extends BaseClass{}

^ Where only The One and Three class would be analyzed. This allows much more control and variability to the property with the drawback of being more verbose.

@johncarl81
Copy link
Owner

This is implemented and deployed in version 0.2.16.

@ppamorim
Copy link

ppamorim commented Apr 6, 2015

@johncarl81 Doc it.

@vickychijwani
Copy link

Thanks a lot for fixing this, @johncarl81!

@vickychijwani
Copy link

EDIT: please ignore this comment, do what @johncarl81 says in his comment below, i.e.,:

  • Use Parcels.wrap(MyObj.class, obj) instead of Parcels.wrap(obj), OR
  • Annotate your class as @Parcel(implementations={ MyObjRealmProxy.class }) (this would require you to make MyObjRealmProxy visible to the IDE by adding the android-apt plugin as below or adding the generated proxy class manually to your IDE sources)

For anyone actually looking to use Parceler with Realm, there's a couple more steps needed:

  • Add the android-apt plugin to your build.gradle, so that the generated Parcelable class is visible to Android Studio.
buildscript {
    repositories {
      mavenCentral()
    }
    dependencies {
        // replace with the current version of the Android plugin
        classpath 'com.android.tools.build:gradle:0.14.4'
        // use the latest version, see http://gradleplease.appspot.com/#android-apt
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
  • Use new MyObj$$Parcelable(obj) instead of Parcels.wrap(obj), otherwise your app will crash with org.parceler.ParcelerRuntimeException: Unable to create ParcelableFactory for io.realm.MyObjRealmProxy. You can still use Parcels.unwrap(...) on the other side.

@johncarl81
Copy link
Owner

@vickychijwani, does it work to specify the class type instead of referencing the generated class:
Parcels.wrap(Feed.class, obj)

@vickychijwani
Copy link

Yes, it works! Can you explain what causes the error if I call the single parameter version of Parcels.wrap(...)? Thanks!

@johncarl81
Copy link
Owner

Parcels.wrap() looks up your instance against a map of ParcelableFactorys by calling .getClass(). The downside of this is it ignores inheritance, so if your class is proxied (like I assume MyObjRealProxy is), then Parceler doesn't know what to do.

worth noting, another way to handle this is add yoru proxy class to the implementations parameter:

@Parcel(implementations={MyObjRealProxy.class})

This will add your proxy class to the Parcels wrap mappings.

@rolandvar
Copy link

I'm trying to use Parceler (1.0.2) and Active Android (3.0.1-SNAPSHOT), but there is something wrong!

I have the following code which produces the error: org.parceler.ParcelerRuntimeException: Unable to find generated Parcelable class for com.mywebsitetest.android.model.Playlist, verify that your class is configured properly and that the Parcelable class com.mywebsitetest.android.model.Playlist$$Parcelable is generated by Parceler.

@Table(name = "Playlists")
@Parcel(value = Parcel.Serialization.BEAN, analyze = Playlist.class)
public class Playlist extends Model {

    @SerializedName("RemoteId")
    @Column(name = "RemoteId")
    private int remoreId;

    @SerializedName("Token")
    @Column(name = "Token", index = true)
    private String token = "defaultToken";

    @SerializedName("User")
    @Column(name = "User", onDelete = Column.ForeignKeyAction.CASCADE)
    private User user;

    @SerializedName("Name")
    @Column(name = "Name")
    private String name;

    @SerializedName("Create")
    @Column(name = "Created")
    private long created;

    @SerializedName("Updated")
    @Column(name = "Updated")
    private long updated;

    public Playlist() {
        super();
    }

....

And in the Main.java I have:

...

Parcelable playlistParcelable = Parcels.wrap(playlist);

...

I also tried it in this way:

...

Parcelable playlistParcelable = Parcels.wrap(Playlist.class, playlist);

...

but still it does not work, someone knows why?

@johncarl81
Copy link
Owner

johncarl81 commented Jul 21, 2015 via email

@rolandvar
Copy link

Sure!

In the gradle console I also have the follow error:
error

build.gradle (Module: app)

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.mywebsitetest.android"
        minSdkVersion 18
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

repositories {
    mavenCentral()
    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'it.neokree:MaterialTabs:0.11'
    compile 'de.greenrobot:eventbus:2.4.0'
    compile 'com.squareup.retrofit:retrofit:1.9.0'

    compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'

    compile "org.parceler:parceler-api:1.0.2"
    apt "org.parceler:parceler:1.0.2"
}

build.gradle (Project)

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        // replace with the current version of the Android plugin
        // use the latest version, see http://gradleplease.appspot.com/#android-apt
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

Thanks for your help!

@johncarl81
Copy link
Owner

Ah, it looks like there's an error during compilation. From your screenshot:

Unable to find read/write generator for type com.activeandroid.TableInfo for com.activeandroid.Model.mTableInfo

Trying your code out on my test bench, it seems to work properly. What does User look like? Does User also extend Model?

@ppamorim
Copy link

@johncarl81 Same here :(

@johncarl81
Copy link
Owner

@ppamorim, @Makingweb, can you offer any more details around this?

@evasquezcr
Copy link

@johncarl81 I'm working with Realm and I got this error,
'Error:Parceler: Unable to find read/write generator for type io.realm.Realm for io.realm.RealmObject.realm
Error:Parceler: Unable to find read/write generator for type io.realm.internal.Row for io.realm.RealmObject.row
Error:Parceler: Unable to find read/write generator for type io.realm.Realm for io.realm.RealmObject.realm
Error:Parceler: Unable to find read/write generator for type io.realm.internal.Row for io.realm.RealmObject.row'

Do you know if I can change something to make it works?

@johncarl81
Copy link
Owner

@evasquezcr, you'll need to specify what classes Parceler should analyze, to avoid analyzing the RealmObject base class. People also have had success by specifying the Realm Proxy object in the implementations configuration. Example:

@Parcel(implementations = { PersonRealmProxy.class }, 
        value = Parcel.Serialization.BEAN, 
        analyze = { Person.class })
public class Person extends RealmObject {
    // ...
}

See https://realm.io/docs/java/latest/#parceler for details.

@hunain-liaquat
Copy link

HI,
How we can have refrence to Proxy class ?

@johncarl81
Copy link
Owner

What do you mean @hunain-liaquat?

@hunain-liaquat
Copy link

Issue resolved, I didn't find reference to BirthdayRealmProxy.class but found in io.realm package after build successfully compiled once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants