Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Base64 dependencies issue in Android Studio

We're using Android Studio 0.6.1 with Gradle plugin 0.11.+ in our current project, and we're running into a dependency issue with commons-codec. We're pulling in a dependency class from our local Artifactory instance that contains a "crypto" service that uses the following two lines of code:

byte[] encryptedOutput = cipherFactory.getEncryptCipher().doFinal(plaintext.getBytes());
byte[] encryptedCipherText = Base64.encodeBase64URLSafe(encryptedOutput);

The problem is that even if we define a specific dependency of commons-codec in our Gradle configuration, we get the following exception

java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.encodeBase64URLSafe

At first, we were manually including a dependency for 'commons-codec:commons-codec:1.9', but according to Android Studio when I drill down into the code in the IDE, it's looking at the version of that method in 1.9, but when the app runs, we get the exception. Even changing the dependency to 1.4 it still fails, even though according to the Javadocs that's when that method became available. Even removing the manual dependency altogether causes the same thing to happen.

Is there any way I can find out where the running app is pulling this dependency from? This is our complete dependency list at the moment, and I can't find commons-codec from any of these

compile files('libs/HockeySDK-3.0.2.jar')
compile files('libs/PushIOManager.jar')
compile 'commons-lang:commons-lang:2.6@jar'
compile 'org.codehaus.jackson:jackson-core-asl:1.9.2@jar'
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.2@jar'
compile 'com.google.android.gms:play-services:4.4.52'
compile 'com.mcxiaoke.volley:library:1.0.4'
compile 'fr.avianey:facebook-android-api:3.14.1@aar'
compile 'javax.validation:validation-api:1.0.0.GA'

My fear is that this class is buried somewhere with the Android SDK and we'll have no way to override it to use a version of commons-codec that will allow us to use our library. And even if we could do that, I'm concerned doing so might cause some fundamental issue with Android itself. We can (and currently do) have the source for that crypto service class pulled into our app and tweaked it to use the appropriate equivalent, but this means any time we make a change to one or the other version, we'd have to keep them in sync.

Any ideas?

UPDATE: What seems to work in this specific case is to scan the dependencies in the Gradle build files and once the dependency is found you're looking for, override is with the version you want to use. For example:

def versionOverrides = [
    "commons-codec:commons-codec": "1.9",
]

subprojects {
    configurations.all {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->

            def overrideVersion = versionOverrides[details.requested.group + ":" + details.requested.name]

            if (overrideVersion != null && details.requested.version != overrideVersion) {
                logger.info "Overriding dependency ${details.requested.group}:${details.requested.name} version ${details.requested.version} --> $overrideVersion"
                details.useVersion overrideVersion
            }
        }
    }
}
like image 671
TheIcemanCometh Avatar asked Jun 19 '14 12:06

TheIcemanCometh


1 Answers

My fear is that this class is buried somewhere with the Android SDK and we'll have no way to override it to use a version of commons-codec that will allow us to use our library.

This is exactly what's happening.
The boot classloader is pre-loaded with classes from the version 1.3 of Commons Codec library.

You can repackage (rename classes's package/namespace) the Commons Codec library to avoid this conflict. See my answer here for more detailed description.

like image 128
Alex Lipov Avatar answered Oct 30 '22 01:10

Alex Lipov