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")
}
}