APKAnalyzer Error

Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema

Recently, I was tasked with using Google provided tools to decompile an APK in a CI/CD pipeline. This is great, because Google provides a tool called APK Analyzer, and provide tools both within the IDE itself and options to run it via the Command Line. I can talk more about the actual task later. For now, I want to discuss the issue that I first ran into when attempting to run apkanalyzer from the terminal.

When I attempted to run apkanalyzer, I was immediately met with the following error:

Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
        at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156)
        at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75)
        at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81)
        at com.android.tools.apk.analyzer.ApkAnalyzerCli.getAaptInvokerFromSdk(ApkAnalyzerCli.java:277)
        at com.android.tools.apk.analyzer.ApkAnalyzerCli.main(ApkAnalyzerCli.java:129)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 5 more

I did some quick searches and noticed some developers failed attempts at correcting this issue, so I figured I would create a post about it :)

If you’re like me (an Android Developer), you will probably get this error because your $JAVA_HOME is set to the built in JDK provided by Android Studio.

java --version
java 14.0.1 2020-04-14
Java(TM) SE Runtime Environment (build 14.0.1+7)
Java HotSpot(TM) 64-Bit Server VM (build 14.0.1+7, mixed mode, sharing)

javac --version
javac 14.0.1

echo $JAVA_HOME
/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/

Well… oddly enough, that is not the version that is compatible to run apkanalyzer, so you need to set ths to a compatible version.

To see which versions of java you have installed on your machine (assuming you’re running a MAC), just use the following command.

/user/libexec/java_home -V
    14.0.1 (x86_64) "Oracle Corporation" - "Java SE 14.0.1" /Library/Java/JavaVirtualMachines/jdk-14.0.1.jdk/Contents/Home
    11.0.12.1 (x86_64) "Amazon.com Inc." - "Amazon Corretto 11" /Users/dustinsummers/Library/Java/JavaVirtualMachines/corretto-11.0.12/Contents/Home
    1.8.0_312 (x86_64) "Amazon" - "Amazon Corretto 8" /<path to library>/Java/JavaVirtualMachines/corretto-1.8.0_312/Contents/Home

With this command, you will see a number of locations for the different Java versions installed on your machine.

As you can see from earlier, I was running Java 14, but you will need to update to Java 18 (1.8) in order to use the apkanalyzer.

To do that as a one-time function, simply type the following:

export JAVA_HOME=/<path to library>/Java/JavaVirtualMachines/corretto-1.8.0_312/Contents/Home

Now, if you re-run apkanlyzer, you will see the following:

apkananlyzer

Subject must be one of: apk, files, manifest, dex, resources

apk summary              Prints the application Id, version code and version name.
apk file-size            Prints the file size of the APK.                         
apk download-size        Prints an estimate of the download size of the APK.      
apk features             Prints features used by the APK.                         
apk compare              Compares the sizes of two APKs.                          
files list               Lists all files in the zip.                              
files cat                Prints the given file contents to stdout                 
manifest print           Prints the manifest in XML format                        
manifest application-id  Prints the application id.                               
manifest version-name    Prints the version name.                                 
manifest version-code    Prints the version code.                                 
manifest min-sdk         Prints the minimum sdk.                                  
manifest target-sdk      Prints the target sdk                                    
manifest permissions     Prints a list of used permissions                        
manifest debuggable      Prints if the app is debuggable                          
dex list                 Prints a list of dex files in the APK                    
dex references           Prints number of references in dex files                 
dex packages             Prints the class tree from DEX.                          
                         P,C,M,F: indicates packages, classes methods, fields     
                         x,k,r,d: indicates removed, kept, referenced and defined 
                           nodes                                                  
dex code                 Prints the bytecode of a class or method in smali format 
resources packages       Prints a list of packages in resources table             
resources configs        Prints a list of configurations for a type               
resources value          Prints the given resource's value                        
resources names          Prints a list of resource names for a type               
resources xml            Prints the human readable form of a binary XML           

Usage:
apkanalyzer [global options] <subject> <verb> [options] <apk> [<apk2>]

Option            Description                         
------            -----------                         
--human-readable  Print sizes in human readable format

It works!!

If you want to set this permanently, you will need to update your $JAVA_HOME variable in your bash_profile or, if you’re a fish user like me, inside of your config.fish file.

Hope this helps!


comments powered by Disqus