Android - 仅在发布 apk 中,java.lang.IllegalArgumentException:无法为类创建转换器

Posted

技术标签:

【中文标题】Android - 仅在发布 apk 中,java.lang.IllegalArgumentException:无法为类创建转换器【英文标题】:Android - only in release apk, java.lang.IllegalArgumentException: Unable to create converter for class 【发布时间】:2017-12-02 11:25:26 【问题描述】:

我正在使用retrofitgsonrealm,我真的不知道是什么导致了这个错误,但它只发生在发布apk 时minifyEnabledtrue,所以我猜是发布了一个proguard,但我不知道到底是什么问题。

这是我的错误:

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)

这里是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;
    

还有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;
    

我的UserDto 班级:

@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;
    

这是我的改造配置:

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()));

最后是我的 Proguard 文件:

    # 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*

【问题讨论】:

Post@SerializedName注解不见了,但我觉得如果你用RealmModel,那么@RealmClass好像也不见了 @EpicPandaForce 是的,对不起,我已经在使用 @RealmClass。至于@SerializedName,如果该注解不存在,gson 不使用同名的属性吗? ProGuard 会吃掉你的对象。 @EpicPandaForce 我添加了@SerializedName 属性,但错误仍然存​​在。 (编辑问题) 看这里:***.com/questions/32367469/… 【参考方案1】:

Retrofit 似乎在方法 getPosts() 中失败了,很可能是因为找不到 Post 类。看来您没有包括 Realm 特定的 ProGuard 规则:

-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.**

强制在混淆和修剪期间保留所有带有@RealmModule 注释的类。将这些规则添加到您的 ProGuard 文件应该可以解决问题。

【讨论】:

我不太明白,我已经将这些模型添加到 proguard 规则中:` -keep class com.ool.oolapp.data.models.** *;` 这还不够吗?因为我试过你贴的sn-p还是不行。 您需要同时包含两者。 com.ool.oolapp.data.** 可用于保存您编写的代码,但您还需要包含 Realm 和 Retrofit 的内部组件以确保一切正常。此外,如果您有其他组件(例如自定义接口、Gson 转换器等),您可能必须使用 @Keep 注释它们或将它们显式添加到 ProGuard 规则中以防止它们被丢弃。

以上是关于Android - 仅在发布 apk 中,java.lang.IllegalArgumentException:无法为类创建转换器的主要内容,如果未能解决你的问题,请参考以下文章

ClassNotFoundException android 仅在签名的 apk 上

R8 / Android Gradle插件是否足够聪明,可以仅在测试apk中保留所需的库类/方法?

java.lang.NoClassDefFoundError:无法解决:Landroid/support/v4/content/LocalBroadcastManager 仅在构建 APK 上

android - 加速仅在调试中起作用

Android Studio无法生成APK

由于第三方库,无法签署 APK