Hot swapping code in Android

Dave Thomas
Quick Code
Published in
4 min readFeb 4, 2018

I’ve recently been looking into ways to update parts of a running Android application silently.

One option I found is loading byte code at run time using a ClassLoader. It actually wasn’t too difficult and I am going to share with you a simple way to do this.

You can find an example here: github.com/davethomas11/AndroidHotSwapCodeExample

On the Android platform there is a specific class loader we can utilize for loading compiled Dalvik bytecode, DexClassLoader.

You can load compiled Dalvik bytecode using this class. Give it the path to a dex file and call loadClass to retrieve your implementations at runtime.

The example code

One problem we will have when loading code dynamically, is how do we reference it in our main application’s code?

I recommend programming to common interfaces.

In the example I have created a simple interface for a class with one method that returns a String. The implementation of that interface can then be determined at run time.

I’ve created a new Android application, and two Android library modules in my project. All three of those modules contain that interface in their source code.

Important note, the main Android application does not contain references to either of the libraries in it’s build.gradle. So neither of those modules are compiled into the main Application’s bytecode.

In the main Application I’ve created a class for loading implementations of that interface.

This ModuleLoader class takes the path to dex file containing dex bytecode, and loads it’s implementation of the IDynamicModule interface.

Update: I noticed my original implementation worked on newer devices, but crashed with a null pointer on older devices. DexClassLoader documentation says cache dir is no longer needed, but it will crash on older SDKs if you pass null for the cache dir.

Note; this file can’t be Java bytecode it must be Dalvik bytecode. You can create a dex file easily from a Java jar using the following dx command:

Each of the library projects contain implementations of IDynamicModule. To build these libraries into Dalvik bytecode I used the following work flow;

  • I clicked on the module in the Android view of the project tab in AndroidStudio and chose the option Build -> Make Module from the file menu.

This results in an aar file being created in the folder build/outputs/aar/

Sadly that aar file does not contain Dalvik bytecode. The classes.jar file contained with in it is Java bytecode. Further processing is needed to get the dex file you need.

Unzip the aar file, and locate file classes.jar. Run the dx command on the jar file to obtain a dx file. Learn Cordova online with the best Cordova tutorials to work more efficiently in android development.

Note: the dx command can be found in your build tools folder at {ANDROID_HOME}/build-tools/{your build tools version}/

To simplify this process for myself, I’ve added build tasks to the build.gradle for each of the dynamicmodules that do this and copy resulting dex file to the app module’s asset folder.

Running ./gradlew dynamicmodule:copyDexToApp will build and dex the first dynamicmodule and copy it to the assets folder of the app module.

In a true hot swap situation you’d instead be downloading the dex file from your server.

Side notes:

The process of creating the dex can be found in https://github.com/davethomas11/AndroidHotSwapCodeExample/blob/master/buildSrc/src/main/groovy/ExtractDexTask.groovy

This is a custom task I’ve added to buildSrc which makes it available to all the build.gradle files in my project.

Loading the modules from the assets folder then is simply done in the example MainActivity:

The dex files need to be copied from assets first into the data directory for the application, then that file path can be used with ModuleLoader.

Check out github.com/davethomas11/AndroidHotSwapCodeExample and give it a try.

Important considerations:

  • Security: If you are downloading the dex file from a server, make sure you include a checksum so that not any dex file can be loaded into your app, and only accept code from a trusted source.
  • Memory & Efficiency: Make sure you take care to dereference on all threads references to your ClassLoader when you are done with an implementation or are upgrading to a new version. The dynamically loaded classes will be garbage collected when the DexClassLoader you instantiate goes out of scope and has no references to it.
  • Architecture: Keep your wrapping interface simple with very simple single entry points to make implementations more flexible.
  • Android resources: Because what you are loading is just Dalvik bytecode take note that you can not include any string, layout or similar assets in your dynamic module, and will have to use alternative methods for dynamically loading that kind of data.

--

--