The basic - and very efficient - separation method in Android is the multiprocess capability of the Dalvik VM. Dalvik is able to fork many instances of itself and can run applications in different Linux processes. By default, each application (the application tag in AndroidManifest.xml) has its own process. There is however another, often overlooked separation mechanism inside each VM process that provides further separation. The good old ClassLoader separation - originally designed to separate applets, used heavily in multi-module frameworks like OSGi - is also at work. Inside each VM process, each task - acitivity or service - uses in its own ClassLoader. This means that object instances created in different tasks are not visible to each other. The standard setup for Android applications is such that the classes.dex in the application's APK package is on the path of the application's own classloader. The application's own classloader delegates the loading of system classes to the boot classloader instance which is common for all the tasks in one particular VM process.
Class loading implementation in Dalvik is tied heavily to DEX files. This has visible side effects. Classes loaded by different class loaders that don't delegate to each other should be completely separated. The implementation differentiates the "initiating" and "defining" classloader (the latter is the one that managed to call defineClass() for a particular class from a particular DEX file in a particular VM process). This differentiation does not work very well and results in a glitch: once a class from a particular DEX file is defined, its static fields are visible and shared from all the class loaders that access the DEX file, even if they don't delegate to each other. This means that tasks of the same application (usually packed into the same DEX file) see each other's static fields.
As if this was not enough, Android provides control of task-VM process mapping. As said previously, usually each application goes into its own VM process. It is possible to declare, however, that applications share a process. This works like the following:
- First we declare that applications wishing to share the same VM process belong to the same user ID. This is done by adding the sharedUserId attribute to the manifest tag in AndroidManifest.xml file like the following: android:sharedUserId="aexp.share.sharedapp". The value of the attribute must be a unique string shared among the applications wishing to use the same VM process. This is a powerful mechanism, hence Android enforces strong check of the APKs containing the declaration. Only the first APK for a particular sharedUserId can be installed without limitation. The issuer of the digital signature of that APK is saved and further APKs with the same sharedUserId must have digital signature from the same issuer, else their installation is rejected.
- We have to declare the alias of the process a certain task or the entire application goes into with the process attribute. This attribute can be added to application, activity or service tags in the AndroidManifest.xml file. For example: android:process="aexp.share.sharedappprocess". VM processes are proper Linux processes identified by PIDs but Android does an additional transformation: whenever a process is started which is tagged by the process alias, the Android framework saves the Linux PID and the process alias. If some other application refers to that process alias and the VM process is still running, that process will be used.
Click here to download the example program.
Our two example programs demonstrate the techniques described below. SharedApp1 allows the user to enter a string and it shares that string in different ways. With its own popup, it shares the string by means of a static field of SharedApp1 activity class. SharedApp2 is another application, having its own APK file but is placed into the same process as SharedApp1. SharedApp1 and SharedApp2 communicate through the good old System.getProperty/setProperty mechanism. Observe, that this works only if the sharedUserId/process mechanism is properly used, if you remove e.g. the process attribute from the manifest file of any of the apps, the property set by SharedApp1 is not visible anymore to SharedApp2 because they run in different VM processes. Also note that I compiled these applications with the debug option which means that they are both signed with the debug key. That key is common for both applications therefore they can have the same sharedUserIds. If you use a proper signing key, take care of using the same key when you sign applications that depend on the sharedUserId mechanism.
And now for something completely different. I was one of the reviewers of Roy Osherove's book titled The Art of Unit Testing (Manning, about 27 USD). Even though the book uses .NET examples, it is easily readable for Java programmers too. Unit testing is one of the most useful techniques I am aware of in software engineering, if you feel that your knowledge is not up to date, you might as well read this book. :-)