Android应用崩溃三星棒棒糖设备上的NoClassDefFoundError

问题描述:

我们最近将我们的应用的minSdkVersion从16(Jellybean)撞到了21(Lollipop)。尽管我们的应用程序主要使用调试版本进行了大量测试,但我们现在面临着应用程序启动时的大量生产崩溃,主要是旧三星设备 - (注3和S4是最糟糕的),并始终在棒棒糖上。Android应用崩溃三星棒棒糖设备上的NoClassDefFoundError

该错误是

Fatal Exception: java.lang.NoClassDefFoundError: com.retailconvergence.ruelala.delegate.GoogleLoginDelegate 
    at com.retailconvergence.ruelala.delegate.LifecycleDelegateManager.addDelegateOfType(LifecycleDelegateManager.java:48) 
    at com.retailconvergence.ruelala.extensions.activity.LifecycleDelegateActivity.addDelegateOfType(LifecycleDelegateActivity.java:55) 
    at com.retailconvergence.ruelala.activity.SplashActivity.setupDelegates(SplashActivity.java:198) 
    at com.retailconvergence.ruelala.activity.SplashActivity.onCreate(SplashActivity.java:60) 
    at android.app.Activity.performCreate(Activity.java:6288) 
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119) 
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758) 
    at android.app.ActivityThread.access$900(ActivityThread.java:177) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:145) 
    at android.app.ActivityThread.main(ActivityThread.java:5942) 
    at java.lang.reflect.Method.invoke(Method.java) 
    at java.lang.reflect.Method.invoke(Method.java:372) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194) 

的SplashActivity是应用程序的初始空空活性。没有找到的班级是一个历史悠久的班级,而不是新引入的班级。作为最新版本的一部分,我们升级到了Android Studio 3并引入了Kotlin代码,但我不认为这些与这个问题有关。我们没有在构建中使用proguard。

我知道,当minSdkVersion是21岁及以上,与使用ART而不是Dalvik有关的构建有重大变化,所以我想知道是否有一些缺陷与三星棒棒糖设备仍然看现在为主要的dex文件中的类吗?

的模块级的build.gradle:

import java.text.SimpleDateFormat 
import java.util.concurrent.TimeUnit 

apply plugin: 'com.android.application' 
apply plugin: 'kotlin-android' 
apply plugin: 'kotlin-android-extensions' 
apply plugin: 'io.fabric' 
apply plugin: 'spoon' 

// Manifest version information 
def versionMajor = 4 
def versionMinor = 2 
def versionPatch = 0 
def versionBuild = 0 // bump for dogfood builds, public betas, etc. 
ext.versionReleaseDate="OCT-13-2017" // UPDATE THIS WHEN YOU BUMP THE VERSIONS ABOVE FOR A NEW RELEASE MMM-dd-yyy 


repositories { 
    mavenCentral() 
    maven { url 'https://maven.fabric.io/public' } 
    maven { url 'http://salesforce-marketingcloud.github.io/JB4A-SDK-Android/repository' } 
    maven { url "https://maven.google.com" } 
    maven { url "http://maven.tealiumiq.com/android/releases/" } 
} 

def getCountOfHoursSinceVersionUpdate() { 
    def currentDate = new Date() 
    def format = new SimpleDateFormat("MMM-dd-yyyy") 
    def buildDate = (Date)format.parse(versionReleaseDate) 
    return (Integer)((currentDate.getTime() - buildDate.getTime())/TimeUnit.HOURS.toMillis(1)) 
} 

android { 
    compileSdkVersion 26 
    buildToolsVersion '26.0.1' 

    defaultConfig { 
     targetSdkVersion 25 

     /** 
     * Increment versionCode by commit count 
     */ 

     versionCode  versionMajor * 100000 + versionMinor * 10000 + versionPatch * 1000 + versionBuild + getCountOfHoursSinceVersionUpdate() 


     testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 

     // Enabling multidex support. :(
     multiDexEnabled true 

     def manifestPath = project(':').file('app/src/androidTest/AndroidManifest.xml') 
     buildConfigField "String", "MANIFEST_PATH", "\"" + manifestPath + "\"" 

     def resPath = project(':').file('app/src/main/res/') 
     buildConfigField "String", "RES_PATH", "\"" + resPath + "\"" 

     def assetPath = project(':').file('app/src/prod/assets/') 
     buildConfigField "String", "ASSET_PATH", "\"" + assetPath + "\"" 

    } 

    dexOptions { 
     javaMaxHeapSize "8g" 
     dexInProcess true // the magic line 
    } 


    flavorDimensions "debugDimension" 

    /** 
    * productFlavors override defaultConfig properties as well as force gradle to look in the new 
    * folders that we have created to differentiate the build assets and manifests. 
    * src/dev, src/prod 
    */ 
    productFlavors { 
     dev { 
      minSdkVersion 21 
      applicationId "com.retailconvergence.ruelala.dev" 
      versionName "${versionMajor}.${versionMinor}.0${versionPatch}" 
      manifestPlaceholders = [optimizelyId: "optly4740131949"] 
      dimension "debugDimension" 
     } 

     prod { 
      minSdkVersion 21 
      applicationId "com.retailconvergence.ruelala" 
      versionName "${versionMajor}.${versionMinor}.${versionPatch}" 
      manifestPlaceholders = [optimizelyId: "optly4752051515"] 
      dimension "debugDimension" 
     } 
    } 

    signingConfigs { 
     prod { 
      //the key is up a level, don't include in the modules 
      storeFile file("../RueLaLaKeystore") 
      storePassword "Boutiques" 
      keyAlias "rue la la" 
      keyPassword "Boutiques" 
     } 
    } 

    buildTypes { 
     release { 
      signingConfig signingConfigs.prod 


      ext.betaDistributionReleaseNotesFilePath = 'release_notes.txt' 
      ext.betaDistributionGroupAliases = 'AndroidTesters' 
      ext.betaDistributionNotifications = true 
     } 

     debug { 
      versionNameSuffix '-dev' 

      signingConfig signingConfigs.prod 
      // to get coverage report, set testCoverageEnabled to true and run gradle task called createDevelopmentDebugAndroidTestCoverageReport 
      // Note that test coverage doesn't seem to work on Samsung devices, other brand or emulator should work though 
      testCoverageEnabled = false 

      ext.betaDistributionReleaseNotesFilePath = 'release_notes.txt' 
      ext.betaDistributionGroupAliases = 'AndroidTesters' 
      ext.betaDistributionNotifications = true 

     } 
    } 

    packagingOptions { 
     exclude 'META-INF/LICENSE.txt' 
     exclude 'META-INF/NOTICE.txt' 
     exclude 'META-INF/LICENSE' 
     exclude 'META-INF/NOTICE' 
     exclude 'LICENSE.txt' 
     exclude 'LICENSE' 
     exclude 'READ.ME' 
     exclude 'README' 
    } 

    compileOptions { 
     sourceCompatibility JavaVersion.VERSION_1_8 
     targetCompatibility JavaVersion.VERSION_1_8 
    } 
} 

configurations { 
} 

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 

    //include our modules 
    compile project(':core') 
    compile project(':data') 

    //android 
    final APP_COMPAT_VERSION = '26.1.0' 

    compile "com.android.support:appcompat-v7:$APP_COMPAT_VERSION" 
    compile "com.android.support:recyclerview-v7:$APP_COMPAT_VERSION" 
    compile "com.android.support:design:$APP_COMPAT_VERSION" 
    compile "com.android.support:multidex:1.0.0" 
    compile "com.android.support:cardview-v7:$APP_COMPAT_VERSION" 

    // google 
    final PLAY_SERVICES_VERSION = '10.2.4' 
    compile "com.google.android.gms:play-services-wallet:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-location:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-gcm:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-plus:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-identity:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-analytics:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-auth:$PLAY_SERVICES_VERSION" 
    compile "com.google.android.gms:play-services-maps:$PLAY_SERVICES_VERSION" 

    // facebook 

    compile 'com.facebook.android:facebook-android-sdk:4.8.+' 
    compile 'com.facebook.stetho:stetho:1.1.0' 

    //markdown4j 
    compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0' 

    //crashlytics 
    compile('com.crashlytics.sdk.android:crashlytics:[email protected]') { 
     transitive = true; 
    } 

    //image zoom 
    compile 'com.github.chrisbanes.photoview:library:1.2.3' 

    //square 
    compile 'com.squareup.picasso:picasso:2.5.2' 
    compile 'com.makeramen:roundedimageview:2.2.1' 


    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1' 
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' 
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' 

    compile 'com.jakewharton:butterknife:8.6.0' 
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' 

    // optimizely 
    compile('com.optimizely:optimizely:[email protected]') { 
     transitive = true 
    } 

    //braintree 
    compile 'com.braintreepayments.api:braintree:2.6.0' 
    compile 'com.braintreepayments.api:data-collector:2.+' 

    // guava 
    compile 'com.google.guava:guava:19.0' 

    // sticky headers 
    compile 'com.github.mtotschnig:StickyListHeaders:2.7.1' 

    // expandable recyclerview 
    compile 'eu.davidea:flexible-adapter:5.0.0-rc2' 

    //recyclerview animations 
    compile 'jp.wasabeef:recyclerview-animators:2.2.3' 

    // tooltip 
    compile 'com.github.michaelye.easydialog:easydialog:1.4' 

    // tealium 
    compile 'com.tealium:library:5.3.0' 

    // circle indicator 
    compile 'me.relex:circleindicator:[email protected]' 

    //testing 
    final HAMCREST_VERSION = '1.3' 

    def jUnit = "junit:junit:4.12" 

    // ExactTarget SDK 
    compile ('com.salesforce.marketingcloud:marketingcloudsdk:5.0.5') { 
     exclude module: 'android-beacon-library' //remove to use Proximity messaging 
     exclude module: 'play-services-location' //remove to use Geofence or Proximity messaging 
    } 

    androidTestCompile jUnit 

    // Unit tests dependencies 
    testCompile jUnit 
    testCompile "org.hamcrest:hamcrest-core:$HAMCREST_VERSION" 
    testCompile "org.hamcrest:hamcrest-library:$HAMCREST_VERSION" 
    testCompile "org.hamcrest:hamcrest-integration:$HAMCREST_VERSION" 
    testCompile 'org.robolectric:robolectric:3.1' 
    testCompile 'org.mockito:mockito-core:1.+' 
    testCompile 'com.google.guava:guava:19.0' 
    testCompile("com.android.support:support-v4:$APP_COMPAT_VERSION") { 
     exclude module: 'support-annotations' 
    } 
    testCompile('org.powermock:powermock-api-mockito:1.6.4') { 
     exclude module: 'objenesis' 
    } 
    testCompile('org.powermock:powermock-module-junit4:1.6.4') { 
     exclude module: 'objenesis' 
    } 
    testCompile 'io.reactivex:rxandroid:1.0.1' 
    testCompile 'io.reactivex:rxjava:1.1.0' 

    // Espresso 
    androidTestCompile('com.android.support.test:runner:0.5') { 
     exclude module: 'support-annotations' 
    } 
    androidTestCompile('com.android.support.test:rules:0.5') { 
     exclude module: 'support-annotations' 
    } 
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') { 
     exclude module: 'support-annotations' 
    } 
    androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') { 
     exclude module: 'support-annotations' 
    } 
    androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.2') { 
     exclude module: 'support-annotations' 
    } 
    androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') { 
     exclude module: 'support-annotations' 
     exclude module: 'recyclerview-v7' 
     exclude module: 'appcompat-v7' 
     exclude module: 'design' 
     exclude module: 'support-v4' 
    } 

    // allows java 8 compile 
    compile 'com.annimon:stream:1.1.2' 

    // For taking screenshots 
    androidTestCompile 'com.squareup.spoon:spoon-client:1.7.0' 

    testCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' 
    compile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' 
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 
} 

apply plugin: 'com.google.gms.google-services' 

spoon { 
    noAnimations = true 
    grantAllPermissions = true 
} 

apply plugin: 'devicefarm' 

devicefarm { 
    projectName "Rue Mobile" 
    devicePool "Smoke Test Pool" 
    useUnmeteredDevices() 
    authentication { 
     accessKey System.getenv("AWS_DEVICE_FARM_ACCESS_KEY") 
     secretKey System.getenv("AWS_DEVICE_FARM_SECRET_KEY") 
    } 
} 

此此修复程序是停用预德兴:

dexOptions { 
    preDexLibraries false 
} 
在应用中的build.gradle

。灵感这个来自关于在棒棒糖未找到毕加索类错误此链接:see here

它并不完全清楚,我为什么禁用预德兴解决了这个问题,但我只能推断,有一些优化与回事构建过程会影响apk的dex文件中的类的排序方式,从而影响这些Samsung Lollipop设备上的应用程序安装。理论上ART应该照顾所有这些,但显然在pre-dex优化和一些棒棒糖设备之间存在依赖关系。