Dynamic Code Loading in Android
We have often come across the problem when we make little changes to our code and we have to publish a new version of our library or app. Well, we can use a better way to manage this by loading code dynamically.
DCL(Dynamic code loading) allows an application to load code that is not part of its static, initial codebase. The additional code can be retrieved from a remote location and executed at runtime.
Benefits:
Code Reuse: If you have multiple apps with similar functionalities, then instead of rewriting the code you can store them in a common location and reuse the code. By not embedding the code in APK, the size of the app will also be less.
Extensibility: Applications can use DCL to extend their functionality. For example, if your app has two types of users free and premium then you can inject code based on user types. This will also reduce APK size.
Self-upgrade: Applications can use DCL to upgrade specific parts of their apps without letting the user know. This also removes the frequent releases of new versions when there are minor changes.
Limitations:
Complex Model: The model designing is complex and requires proper knowledge otherwise leads to unexpected behavior.
Implementation:
Code Retrieval: The application needs to retrieve code which is to be dynamically loaded. The code can be fetched locally or from a remote location. In most cases, it is fetched from the backend server. This code is in the form of .apk or .jar file.
Code Storage: The fetched code needs to be stored so that it can be loaded dynamically. The recommended storage is the app’s private directory.
Code Loading: The .apk file is ready to be loaded. DexClassLoader is a class provided by Android Framework is used to load classes from .apk or .jar files.
I have created a simple calculator app as an example for this post.
Step 1: Code Retrieval
I have created an APK file with just one class in it and stored it in firebase storage with a version number. Whenever the app is opened it checks for the version and fetches the APK from Firebase.
Step 2: Code Storage
Once the APK is downloaded successfully, it gets stored in the app's private directory. To get the path of directory use:
Context.getFilesDir().getAbsolutePath()
Step 3: Code Loading
We have the stored APK file, now we can load the APK by using the following code:
String apkPath = context.getFilesDir().getAbsolutePath() + "/app-debug.apk";
final DexClassLoader classLoader = new DexClassLoader(apkPath, context.getCacheDir().getAbsolutePath(), null, this.getClass().getClassLoader());
The code snippet creates an instance of DexClassLoader. The apkPath is the path of stored APK.
The DexClassLoader takes 4 arguments as:
DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)
dexPath: The path where your APK is stored. The recommended way is to store the APK in the app’s private directory.
optimizedDirectory: It is the path where DexClassLoader will put your loaded code.
Note: “optimizedDirectory” parameter is deprecated and has no effect since API level 26.
librarySearchPath: Path of native libraries, null if you don’t want to load any native library.
parent: The parent class loader. A class loader is used to load classes dynamically.
try {
classLoader.loadClass("com.example.calculator.DynamicCalculator");
} catch (Exception e) {
e.printStackTrace();
}
The loadClass method takes a String argument specifying the fully packaged name of the class. You should have knowledge of Reflection API’s in order to use DCL.
The DynamicCalculator class contains two methods:
package com.example.calculator;
public class DynamicCalculator {
public static double add(double d1, double d2) {
return d1 + d2;
}
public static double subtract(double d1, double d2) {
return d2 - d1;
}
}
The advantage is I can change the logic of calculation without releasing a new version of my app or library, the code will get updated.
I have only created one class in my APK, you can create as many as you want based on your requirements.
Step 4: Using dynamically loaded code
The methods of classes can be called by using Reflection. In the code snippet, I am getting the method add which takes two arguments of type double.
Method addMethod = LoadDynamicClasses.getDymnamicClass().getMethod("add", double.class, double.class);
Now to call the method, we use:
double result = (double) addMethod.invoke(null, firstNum, secondNum);
Since the add method is static, we pass the first parameter null. This is how we can implement DCL in our app. The code is available here.
Thanks for reading.