2017-10-18 7 views
2

Nous avons récemment dépassé la version minSdk de notre application de 16 (Jellybean) à 21 (Lollipop). Bien que nous ayons effectué de nombreux tests avec notre application utilisant principalement des versions de débogage, nous sommes confrontés à une série de plantages au démarrage de l'application, principalement sur les anciens appareils Samsung (Note3 et S4 sont les meilleurs) et toujours sur Lollipop.Crash de l'application Android NoClassDefFoundError sur les appareils Samsung Lollipop

L'erreur est

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) 

Le SplashActivity est l'activité initiale Lancement de l'application. La classe non trouvée est une classe établie depuis longtemps et non quelque chose de nouvellement introduit. Comme une note latérale, dans le cadre de cette dernière version, nous avons mis à niveau vers Android Studio 3 et introduit le code Kotlin, mais je ne pense pas que ceux-ci sont liés au problème. Nous n'utilisons pas proguard dans la construction. Je suis conscient qu'il y avait un changement significatif pour les builds lorsque la version minSdk est 21 et plus, concernant l'utilisation de ART au lieu de Dalvik, donc je me demande s'il y a un défaut avec les appareils Samsung Lollipop pour une classe dans le fichier dex primaire maintenant?

Le module niveau 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") 
    } 
} 

Répondre

2

Le correctif de c'était désactiver pré-Dexing:

dexOptions { 
    preDexLibraries false 
} 

dans l'application build.gradle. Inspiration pour ce qui vient de ce lien concernant une erreur de classe Picasso introuvable sur Lollipop: see here

Il n'est pas tout à fait clair pour moi pourquoi désactiver le pré-dexing résout le problème, mais je peux seulement théoriser qu'il y a une certaine optimisation en cours avec le processus de construction qui affecte la façon dont les classes sont classées dans les fichiers dex de l'apk et qui affecte ensuite l'installation de l'application sur ces périphériques Samsung Lollipop. En théorie, ART devrait s'occuper de tout cela, mais il existe clairement une dépendance entre l'optimisation pré-dex et certains dispositifs Lollipop.