If your product depends upon a mobile SDK, then you must be knowing the real pain of shipping the latest version of your SDK onto the live devices through host apps. There is a way to tackle this issue with an interesting approach.
We are going to use dynamic class loading using classloaders provided by Android system.
The Java classloader is a part of the Java Runtime Environment that dynamically loads Java classes into the Java Virtual Machine. Usually, classes are only loaded on demand. The Java runtime system does not need to know about files and file systems because of classloaders.
We are gonna use a special class loader from Android system called PathClassLoader, which provides a simple ClassLoader implementation that operates on a list of files and directories in the local file system, but does not attempt to load classes from the network. Android uses this class for its system class loader and for its application class loader(s).
Chaining the ClassLoaders
So we can create a hierarchy of classloaders that can share the definition of the classes without any duplication. So in this case, classloader A has already loaded class A, so
loaderB.loadClass('A') will delegate the request to
loaderA instead of reloading it. This feature comes handy for our purpose.
Application lifecycle on Android OS
When a user clicks on the app icon, a new application thread is started which contains a new instance of the dalvik vm. This dalvik vm has a classloader that loads the dex file (APK file) and kicks off the lifecycle of the app.
What if we provide one more dex to load at runtime?
Consider the AAR file that we ship, contains one more DEX file which is loaded by the AAR at the time of initialization. This will allow us to change the functionality without having to force update the AAR on the devices.
Structure of the project
app : Host app where you will integrate the SDK
1 2 3 4 5
lib : Our static SDK code that ships with the app. This has the secret sauce of loading the functionality runtime.
dynamiclib : SDK that does the implementation of the dynamic functionality of the SDK.
Let’s divide our tasks and decode one by one.
To bring in the dynamic features, I am going with an interface approach. So our dynamic lib and lib will share one common interface.
1 2 3 4
Make sure that the package name for this interface will be the exactly same in both
IMethods is implemented by a class called
MethodsImpl in dynamic lib.
1 2 3 4 5 6 7 8 9 10 11
Now build this dynamiclib to an APK (as we need a dex file), and host it. I use SimpleHttpServer.
This makes dynamic lib available on a link.
Let’s build the last leg of the solution, downloading the APK and loading the classes using a PathClassLoader.
Downloading the APK
Use any way to download the APK, I have used an AsyncTask
Store the downloaded APK on internal storage sandboxed for our package
context.getFilesDir() gives the path to the internal sandboxed storage for the package. Store the downloaded APK in this folder.
Load the downloaded APK and cast it to the IMethods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Logs when you run the app is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
This approach will allow you to update the functionality with a strong interface defined between shipped code and dynamic part of the SDK. This is the case where host app keeps interacting with your SDK using a set of functions. If your SDK is, initialize once and forget, then you can keep entire logic in dynamic part of the SDK.
Security is going to be one of the biggest concern in this approach. There are some standard ways to validate the dex file like using MD5 hashes. Once you securely download the DEX file in the internal storage then other concerns are as same as they are for shipped SDK.
Github repo for the source code.
Get in touch with me if you need any help or you find something wrong with this post. Happy coding :).