Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - only in release apk, java.lang.IllegalArgumentException: Unable to create converter for class

I'm using retrofit, gson and realm, I don't really know what causes this error, but it only occurs in release apk when minifyEnabled is true, so I guess it's a proguard issued but I don't know exactly what's the issue.

Here's my error:

java.lang.IllegalArgumentException: Unable to create converter for class com.ool.oolapp.data.models.NewsFeedResponse
   for method a.getPosts
   at retrofit2.ServiceMethod$Builder.methodError(Unknown Source)
   at retrofit2.ServiceMethod$Builder.createResponseConverter(Unknown Source)
   at retrofit2.ServiceMethod$Builder.build(Unknown Source)
   at retrofit2.Retrofit.loadServiceMethod(Unknown Source)
   at retrofit2.Retrofit$1.invoke(Unknown Source)
   at java.lang.reflect.Proxy.invoke(Proxy.java:393)
   at $Proxy1.getPosts(Unknown Source)
   at com.ool.oolapp.data.a.b.a(Unknown Source)
   at com.ool.oolapp.data.b.k.a(Unknown Source)
   at com.ool.oolapp.data.b.k.a(Unknown Source)
   at com.ool.oolapp.home.a.c.a(Unknown Source)
   at com.ool.oolapp.home.b.b.b(Unknown Source)
   at com.ool.oolapp.home.b.b.a(Unknown Source)
   at com.ool.oolapp.home.view.d.v(Unknown Source)
   at android.support.v4.b.p.L(Unknown Source)
   at android.support.v4.b.v.a(Unknown Source)
   at android.support.v4.b.v.e(Unknown Source)
   at android.support.v4.b.v.a(Unknown Source)
   at android.support.v4.b.g.e(Unknown Source)
   at android.support.v4.b.v.b(Unknown Source)
   at android.support.v4.b.v.a(Unknown Source)
   at android.support.v4.b.v.b(Unknown Source)
   at android.support.v4.b.v.b(Unknown Source)
   at android.support.v4.b.g.d(Unknown Source)
   at android.support.v4.b.aa.b(Unknown Source)
   at android.support.v4.view.ViewPager.a(Unknown Source)
   at android.support.v4.view.ViewPager.c(Unknown Source)
   at android.support.v4.view.ViewPager.onMeasure(Unknown Source)
   at android.view.View.measure(View.java:18794)
   at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
   at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
   at android.view.View.measure(View.java:18794)
   at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
   at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
   at android.support.v7.widget.ContentFrameLayout.onMeasure(Unknown Source)
   at android.view.View.measure(View.java:18794)
   at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
   at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
   at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
   at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
   at android.view.View.measure(View.java:18794)
   at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
   at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
   at android.view.View.measure(View.java:18794)
   at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
   at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
   at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
   at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
   at android.view.View.measure(View.java:18794)
   at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
   at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
   at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
   at android.view.View.measure(View.java:18794)
   at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100)
   at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216)
   at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452)
   at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
   at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
   at android.view.Choreographer.doCallbacks(Choreographer.java:670)
   at android.view.Choreographer.doFrame(Choreographer.java:606)
   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
   at android.os.Handler.handleCallback(Handler.java:739)
   at android.os.Handler.dispatchMessage(Handler.java:95)
   at android.os.Looper.loop(Looper.java:148)

Here's com.ool.oolapp.data.models.NewsFeedResponse:

public class NewsFeedResponse {
    @SerializedName("posts")
    private List<Post> posts;

    public List<Post> getPosts() {
        return posts;
    }

    public void setPosts(List<Post> posts) {
        this.posts = posts;
    }
}

And Post.java:

    @RealmClass
public class Post implements RealmModel {
    @PrimaryKey
    @SerializedName("postId")
    private int postId;
    @SerializedName("userId")
    private int userId;
    @SerializedName("userDto")
    private UserDto userDto;
    @SerializedName("caption")
    private String caption;
    @SerializedName("voiceDuration")
    private int voiceDuration;
    @SerializedName("commentCount")
    private int commentCount;
    @SerializedName("likesCount")
    private int likesCount;
    @SerializedName("shareCount")
    private int shareCount;
    @SerializedName("picUrl")
    private String picUrl;
    @SerializedName("voiceUrl")
    private String voiceUrl;
    @SerializedName("creationDate")
    @JsonAdapter(TimestampDeserializer.class)
    private Date creationDate;
    @SerializedName("comments")
    private RealmList<Comment> comments;

    public Post() {
    }

    public Post(String picUrl, String caption, int voiceDuration) {
        this.picUrl = picUrl;
        this.caption = caption;
        this.voiceDuration = voiceDuration;
    }

    public String getCreatedFrom()
    {
        return Utility.getCreatedFromString(creationDate);
    }

    public int getPostId() {
        return postId;
    }

    public String getPicUrl() {
        return picUrl;
    }

    public void setPicUrl(String picUrl) {
        this.picUrl = picUrl;
    }

    public int getVoiceDuration() {
        return voiceDuration;
    }

    public void setVoiceDuration(int voiceDuration) {
        this.voiceDuration = voiceDuration;
    }

    public String getVoiceUrl() {
        return voiceUrl;
    }

    public void setVoiceUrl(String voiceUrl) {
        this.voiceUrl = voiceUrl;
    }

    public int getUserId() {
        return userId;
    }

    public void setPostId(int postId) {
        this.postId = postId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getCaption() {
        return caption;
    }

    public void setCaption(String caption) {
        this.caption = caption;
    }

    public int getCommentCount() {
        return commentCount;
    }

    public void setCommentCount(int commentCount) {
        this.commentCount = commentCount;
    }

    public int getLikesCount() {
        return likesCount;
    }

    public void setLikesCount(int likesCount) {
        this.likesCount = likesCount;
    }

    public int getShareCount() {
        return shareCount;
    }

    public void setShareCount(int shareCount) {
        this.shareCount = shareCount;
    }

    public RealmList<Comment> getComments() {
        return comments;
    }

    public void setComments(RealmList<Comment> comments) {
        this.comments = comments;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public UserDto getUserDto() {
        return userDto;
    }

    public void setUserDto(UserDto userDto) {
        this.userDto = userDto;
    }
}

My UserDto class:

@RealmClass
public class UserDto implements RealmModel {
    @PrimaryKey
    @SerializedName("userId")
    private int userId;
    @SerializedName("fName")
    private String fName;
    @SerializedName("lName")
    private String lName;
    @SerializedName("pictureLocation")
    private String pictureLocation;

    public UserDto() {}

    public UserDto(int userId, String fname, String lname, String pictureLocation) {
        setUserId(userId);
        setfName(fname);
        setlName(lname);
        setPictureLocation(pictureLocation);
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getFName() {
        return fName;
    }

    public String getLName() {
        return lName;
    }

    public String getPictureLocation() {
        return pictureLocation;
    }

    public void setfName(String fName) {
        this.fName = fName;
    }

    public void setlName(String lName) {
        this.lName = lName;
    }

    public void setPictureLocation(String pictureLocation) {
        this.pictureLocation = pictureLocation;
    }
}

Here's my retrofit configurations:

private Retrofit.Builder createRetrofitBuilder() {
    HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
    if(BuildConfig.BUILD_TYPE.equals("release")) {
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
    } else {
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    }
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(httpLoggingInterceptor)
            .build();
    GsonBuilder gsonBuilder = new GsonBuilder();
    return new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(client)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()));
}

And finally my Proguard file:

    # retrofit
    # Platform calls Class.forName on types which do not exist on Android to determine platform.
    -dontnote retrofit2.Platform
    # Platform used when running on Java 8 VMs. Will not be used at runtime.
    -dontwarn retrofit2.Platform$Java8
    # Retain generic type information for use by reflection by converters and adapters.
    -keepattributes Signature
    # Retain declared checked exceptions for use by a Proxy instance.
    -keepattributes Exceptions
    #okio
    -dontwarn okio.**
    #Picasso
    -dontwarn com.squareup.okhttp.**
    #retrolambda
    -dontwarn java.lang.invoke.*
    -dontwarn **$$Lambda$*

    # Amazon AWS
    -dontwarn com.fasterxml.jackson.core.**
    # Guava
    -dontwarn sun.misc.Unsafe
    -dontwarn java.lang.ClassValue

    -keep class retrofit2.** { *; }
    -keep class com.ool.oolapp.data.models.** {*;}
    -keepclassmembernames interface * {
        @retrofit2.http.* <methods>;
    }
    -keepattributes *Annotation*
like image 541
Rafael Adel Avatar asked Mar 09 '23 17:03

Rafael Adel


1 Answers

It seems that Retrofit is failing in the method getPosts(), most likely due the Post class not being found. It seems that you're not including the Realm specific ProGuard rules:

-keep class io.realm.annotations.RealmModule
-keep @io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class *
-dontwarn javax.**
-dontwarn io.realm.**

that force all classes annotated with @RealmModule to be preserved during obfuscation and trimming. Adding these rules to your ProGuard file should solve the problem.

like image 169
Mike Laren Avatar answered Apr 25 '23 11:04

Mike Laren