在模块(android 库)中使用改造 2.4.0 时出现 NoClassDefFoundError

Posted

技术标签:

【中文标题】在模块(android 库)中使用改造 2.4.0 时出现 NoClassDefFoundError【英文标题】:NoClassDefFoundError when using retrofit 2.4.0 in a module (android library) 【发布时间】:2018-11-13 18:06:43 【问题描述】:

一直致力于在一个项目的 android 库中使用改造。但是,如果在应用程序的 gradle 中进行了配置,或者如果库的源代码是应用程序项目的一部分,那么一切似乎都运行良好。

但是,如果库项目被保护并作为@aar 包含在应用程序中。似乎有一个混淆问题。

这是我在 android 模块项目中改造的 proguard:

-dontwarn retrofit2.**
-keep class retrofit2.**  *; 
-keepattributes Exceptions
-keepattributes RuntimeVisibleAnnotations
-keepattributes RuntimeInvisibleAnnotations
-keepattributes RuntimeVisibleParameterAnnotations
-keepattributes RuntimeInvisibleParameterAnnotations

-keepattributes EnclosingMethod
-keepclasseswithmembers class * 
    @retrofit2.http.* <methods>;

-keepclasseswithmembers interface * 
    @retrofit2.* <methods>;


# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on RoboVM on ios. Will not be used at runtime.
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
# 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

# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain service method parameters.
-keepclassmembernames,allowobfuscation interface * 
    @retrofit2.http.* <methods>;

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

-keep public interface com.mylibrary.interfaces.**
-keep public class com.mylibrary.rest.RetrofitClientInstance
-keep public class com.mylibrary.models.** *;

-dontwarn org.xmlpull.v1.**
-dontwarn javax.annotation.**


### OkHttp3
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

这就是我的 RetrofitClientInstance 类的样子:

public class RetrofitClientInstance 

    private static Retrofit retrofit;

    public static Retrofit getRetrofitInstance() 
        if (retrofit == null) 
            retrofit = new retrofit2.Retrofit.Builder()
                    .baseUrl(Constants.BASE_API_URL)
                    .addConverterFactory(ScalarsConverterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        
        return retrofit;
    

我得到的错误:

java.lang.NoClassDefFoundError: Failed resolution of: Lretrofit2/Retrofit$Builder;
        at com.mylibrary.rest.RetrofitClientInstance.getRetrofitInstance(RetrofitClientInstance.java:15)
        at com.mylibrary.rest.LoginAPIHelper.<init>(LoginAPIHelper.java:29)
        at com.mylibrary.rest.LoginAPIHelper.getInstance(LoginAPIHelper.java:36)
        at com.view.LoginActivity.onCreate(LoginActivity.java:124)
        at android.app.Activity.performCreate(Activity.java:6772)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2715)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2823)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6349)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "retrofit2.Retrofit$Builder" on path: DexPathList[[zip file "/data/app/com.inventaapp-2/base.apk"],nativeLibraryDirectories=[/data/app/com.inventaapp-2/lib/arm, /data/app/com.inventaapp-2/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at com.mylibrary.rest.RetrofitClientInstance.getRetrofitInstance(RetrofitClientInstance.java:15) 
        at com.mylibrary.rest.LoginAPIHelper.<init>(LoginAPIHelper.java:29) 
        at com.mylibrary.rest.LoginAPIHelper.getInstance(LoginAPIHelper.java:36) 
        at com.inventaapp.view.LoginActivity.onCreate(LoginActivity.java:124) 
        at android.app.Activity.performCreate(Activity.java:6772) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2715) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2823) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6349) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783) 

我已尝试对导入库的主应用程序进行多 dexing。不影响行为。事实上,如果源是作为应用程序项目中的模块导入的,它可以在没有多重索引的情况下工作。

另外,如果 aar 作为库导入。但是,如果我在主项目的 gradle 中添加改造 gradle 导入。在这种情况下也可以。

我在这里缺少什么?似乎与此类似的问题:https://github.com/square/retrofit/issues/1811

仅供参考,我的 gradle 看起来像:

 implementation 'com.google.code.gson:gson:2.8.2'
 implementation 'com.squareup.retrofit2:retrofit:2.4.0'
 implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
 implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'

非常感谢任何帮助。

提前致谢, 阿纳布

【问题讨论】:

查看这个答案***.com/questions/48186933/unable-to-merge-dex-error/… 嗨@Thunder 谢谢,但我已经提到我已经尝试过了。没有帮助,根据我的说法,这似乎是在 android 库中使用改造时的特殊情况。但是,不知道如何在不重新导入应用程序的 gradle 的情况下解决它。 你在使用这个实现 'com.squareup.retrofit2:retrofit:2.3.0' 是的,在 2.3.0 上。目前在 2.4.0 实现 'com.squareup.retrofit2:retrofit:2.4.0' 清理并重建您的项目,让我进一步了解。 【参考方案1】:

在 android 库模块项目中使用改造时遇到此问题。尝试了各种方法,以下对我有用

第 1 步:我在 app 和库模块 build.gradle 文件中添加和更新了改造依赖项(2.9.0)

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.7.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.8.0'

第 2 步:在应用程序的 gradle 中添加

compileOptions 
    sourceCompatibility = 1.8
    targetCompatibility = 1.8

第 3 步:使缓存无效并重新启动/清理构建并重新构建。

【讨论】:

谢谢你这对我有用。我很长一段时间以来一直面临这个问题。我只在 lib 模块中添加了依赖项,它正在工作。【参考方案2】:

问题已在改造 2.9.0 中修复,只需更新到最新版本。

【讨论】:

【参考方案3】:

我猜问题是当 aar 在本地生成时,它并没有将所有依赖项打包在里面。那是设计使然! 您需要将 aar 发布到一个 maven repo 和一个 pom 文件,其中列出了所有依赖项,然后 gradle 可以获得所需的所有依赖项。

快速解决方法是将此依赖项添加到主应用程序项目 build.gradle

【讨论】:

一个链接到一些支持它的文档会很好

以上是关于在模块(android 库)中使用改造 2.4.0 时出现 NoClassDefFoundError的主要内容,如果未能解决你的问题,请参考以下文章

NullPointerException 使用改造库 - Android

如何在不使用 GSON 或 android 中的任何其他库的情况下使用改造以字符串形式获得响应 [重复]

在 Android 中通过改造获取对象数组

GroovyAndroid Studio 中创建 Groovy 工程 ( 创建 Android Studio 工程 | 创建并改造 Java 依赖库 | 编写 Groovy 代码并运行 )

如何使用改造 Android 调用具有动态参数的 api?

改造解析JSON响应空值Android