Android Plugin Application
For one of the project I am working at the moment I have the need to implement a plug-in architecture where the main application is just a “view holder” and all the behaviors of the application are provided by a set of plug-ins.
The advantage of this approach is that in the future I will not need to re-distribute my entire framework over the marketplace but simply release new plug-ins that the customer can add or update the existing one.
Note: the code presented in this article is not optimized and its only purpose is to explain one of the possible solutions to implement a custom plug-in architecture in Android. The methods exposed are not optimized and do not use an asynchronous pattern so I suggest to refactor them before adopting this code into your real applications
What is a plug-in architecture?
Before deep diving into the code I want to take a moment and explain what I personally mean for plug-in architecture, using the following diagram:
The previous picture represents a description of the JPF, the Java Plugin Framework, which is a similar solution to the one we are going to implement. The architecture is primarily composed by two different components.
One component is the main application, the agnostic framework capable to load plug-ins. The second component is the plug-in registry, a repository that inform the system about the available plug-ins installed into the system.
In Android we have tons of different ways to implement a plug-in architecture. For example we can have a plug-in composed by a .JAR package that contains activities and code related to the plug-in. But I found out that in Android the best way to package things is to use the .apk system. With an .apk I can include Activities, Fragments, resources and layouts like a standalone application with the advantage of using some sort of contracts to force the code to be in a certain way.
Retrieve available packages
The basic project is a simple Android Application with a main activity. The main activity contains a ListView that will display all the available .apk that we can consider a plug-in for our application.
But first of all, let’s see how we can retrieve a list of installed .apk using some basic Android APIs. First of all I need to create a custom ListView item that can be used to display the information relative to a package. And this is the custom class:
package ltd.netarchitectures.na_plugins; import android.graphics.drawable.Drawable; public class ApplicationDetail { private CharSequence label; private CharSequence name; private Drawable icon; public ApplicationDetail(CharSequence label, CharSequence name, Drawable icon) { this.label = label; this.name = name; this.icon = icon; } public CharSequence getLabel() { return label; } public CharSequence getName() { return name; } public Drawable getIcon() { return icon; } }
So with this custom class we can represents an available package. For now the information exposed are enough but we can retrieve more information like the company who made the package, the size of the package, the version and so on. We can also expose the package to see how many Fragments or Activities are available. Again, here the limit is your imagination.
Now we need to fetch all available packages. Because we do not have any plug-in available yet, let’s see how we can fetch all the available applications just to start to have a look at the android API.
private void loadApplication(){ // package manager is used to retrieve the system's packages packageManager = getPackageManager(); applications = new ArrayList<ApplicationDetail>(); // we need an intent that will be used to load the packages Intent intent = new Intent(Intent.ACTION_MAIN, null); // in this case we want to load all packages available in the launcher intent.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> availableActivities = packageManager.queryIntentActivities(intent,0); // for each one we create a custom list view item for(ResolveInfo resolveInfo:availableActivities){ ApplicationDetail applicationDetail = new ApplicationDetail( resolveInfo.loadLabel(packageManager), resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.loadIcon(packageManager)); applications.add(applicationDetail); } }
At the end (I skip the code to create a custom list view item cause it should be quite easy to implement, anyway you can find it in the source code of this article) we will have a list view populated with all available applications. In my case in alphabetical order:
Create a plug-in
In order to not loose any of the advantages of the android application package we want to distribute our plug-ins as standalone packages but we don’t want that the user is capable to execute the packages as stand alone packages.
First of all, I want to make a little explanation of how android works and how each package is treated by the underlying Linux system:
- The Android operating system is a multi-user Linux system in which each app is a different user.
- By default, the system assigns each app a unique Linux user ID (the ID is used only by the system and is unknown to the app). The system sets permissions for all the files in an app so that only the user ID assigned to that app can access them.
-
Each process has its own virtual machine
(VM), so an app’s code runs in isolation from other apps.
- By default, every app runs in its own Linux process. Android starts the process when any of the app’s components need to be executed, then shuts down the process when it’s no longer needed or when the system must recover memory for other apps.
All right, so first of all we need to create a new Android project and set the project to run in background. The project will have its own icon and activities and bla bla but it cannot be shown into the home launcher.
<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <!-- <category android:name="android.intent.category.LAUNCHER" /> --> </intent-filter> </activity>
And then the application is installed into our system, but is not visible in the launcher like the following screenshot:
The next step is to share a custom INTENT between my plug-in application and my main application and set the category of the plug-in to this custom intent. In this way my list view will be populated only with the list of available plugins.
If you pay attention to the Android manifest the INTENT is nothing more than a custom string, so in my plugin manifest I will change the intent in the following one:
<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="ltd.netarchitectures.PLUGIN" /> </intent-filter> </activity>
And inside my ListView adapter I will load the activities that only implement my INTENT like this one:
packageManager = getPackageManager(); applications = new ArrayList<>(); Intent intent = new Intent(Intent.ACTION_MAIN, null); intent.addCategory("ltd.netarchitectures.PLUGIN");
And now when I start my main application only my plugins will be loaded:
Easy, isn’t it? In the next article I will explain how we can execute this plugin within the same thread (Linux VM) of the main activity and how we can control when a new plug-in is installed into the system.