Making the most of the APK analyzer
One of my favorite recent additions to Android Studio is the APK Analyzer, which you can find in the top menu under Build → Analyze APK.
APK Analyzer lets you open and inspect the contents of any APK file you have on your computer, either built from your local Android Studio project or acquired from your build server or other artifact repository. It doesn’t have to be built from any project you have open in Android Studio and you don’t need the source code for that particular APK.
Note: APK Analyzer works best with release builds. If you need to analyze a debug build of your app, make sure you are using an APK that is not instrumented for Instant Run. To obtain it, you can use the Build → Build APKcommand. You can see if you’ve opened an Instant Run instrumented APK by checking the presence of an instant-run.zip file inside the archive.
Using the APK analyzer is a great way to poke around APK files and learn about their structure, verify the file contents before releasing or debug some common problems, including APK size and DEX issues.
Reducing app size with the APK Analyzer
The APK analyzer can give you a lot of useful, actionable info about app size. At the top of the screen, you can see the Raw File Size which is just the APK on-disk size. The Download size shows an estimate of how much data will used to download your app by taking into account compression applied by the Play Store.
The list of files and folders is sorted by total size in descending order. This makes it great for identifying the low hanging fruit of APK size optimization. Each time you drill down into a folder, you can see the resources and other entities that take up the most space in your APK.
In this example, when examining an APK for possible size reductions, I was able to immediately notice that a 3-frame PNG animation is the single biggest thing in our drawable resources, weighing in at 1.5MB, and that’s just for the xxhdpi density!
Since these images look like perfect candidates for storing as vectors, we found the source files for the artwork and imported them as VectorDrawables using the new PSD support in the Vector Asset import toolin Android Studio 2.2.
By going through the same process for the other remaining animation (instruction_touch_*.png) and removing these PNG files across all densities, we were able to save over 5MB. To maintain backward compatibility we used VectorDrawableCompat from the support library.
Looking at other resource folders, it was easy to spot some uncompressed WAV files that could be converted to OGG, which meant even more savings without touching a line of code.
Next on the list of things to check was the lib/ folder, which contains native libraries for the three ABIs that we support.
The decision was made to use APK splits support in our Gradle build to create separate versions of the app for each ABI.
I quickly looked over the AndroidManifest.xml next and noticed that <application> is missing the android:extractNativeLibs attribute. Setting this attribute to false can save some space on the device as it prevents copying out the native libraries from the APK to the filesystem. The only requirement is that the files are page aligned and stored uncompressed inside the APK, which is supported with the new packager in the Android Gradle plugin version 2.2.0+.
After I made these modifications, I was curious to see how the new version of the app compares to the previous one. To do that, I checked out the source from the git commit with which I started, compiled the APK and saved it in another folder. I then used the Compare with… feature to see a breakdown of size differences between the old and new builds.
We made a lot of progress on the resources and native libraries, saving a total of 17MB with very little changes in the app. However, I can see that our DEX size regressed, with the classes2.dex growing by 400KB.
Debugging DEX issues
In this case, the difference came from upgrading our dependencies to newer versions and adding new libraries. Proguard and Multidex were already enabled for our builds so there’s not much that can be done about our DEX size. Still, APK analyzer is a great tool to debug any issues with this setup, especially when you’re enabling Multidex or Proguard for your project for the first time.
When you click on any DEX file, you will see a summary of how many classes and methods it defines, and how many total references it contains (it’s references that count against the 64K limit in a single DEX file). In this example screenshot, the app is just about to reach the limit, which means it will need MultiDex to split the classes into separate files in the near future.
You can drill down into the packages to see which ones are using up all of the references. In this case, we can see that the Support library and Google Play Services are the main causes for this situation:
Once you’ve enabled MultiDex and compiled your app, you will notice a second classes2.dex file (and possibly classes3.dex, and so on). The MultiDex solution in the Android gradle plugin figures out which classes are needed to start your app and puts them into the primary classes.dex file, but in the rare case when it doesn’t work and you get a ClassNotFoundException, you can use APK Analyzer to inspect the DEX files, and then force the missing classes to be put in the primary DEX file.
You will encounter similar issues when enabling Proguard and using classes or methods by reflection or from XML layouts. The APK Analyzer can help you with verifying that your Proguard configuration is correct, by letting you easily check if the methods and classes you require are present in the APK and if they are being renamed (obfuscated). You can also make sure that classes you want gone are actually removed, and not taking up your precious reference method count.
We’re curious to hear what other uses you find for the APK Analyzer and what other features you’d like to see integrated in the tool!