Android tools to build applications need to provide a better iterative coding experience. Rather than focusing on rebuilding the entire application, tools should deliver changes in small increments that can be built, installed and loaded rapidly. Furthermore, relatively simple code changes should be delivered in the live process by patching existing loaded code with newer versions, eliminating the need to restart the applications.
Although the Gradle build-system can function independently of the IDE (it can compile from Java sources up to packaging the final APK ready to be uploaded on the Play Store, all from command line invocations) it was decided instant run would be an integrated experience from the IDE. There is no command line interface to the features described in this document.
A small server is embedded within the application running on the Android device. This server will open a port to communicate with the Android Studio instance.
When the user modifies code within the IDE and selects re-run, Studio calls the build system to perform an incremental build. The build system returns the list of artifacts produced (details in the following paragraphs).
In the case of hot or warm swap, the artifacts are sent by the instant run client in Studio to the instant run server in the application The server will then be notified what type of restart is necessary (none, activity, process) and will perform it. For a cold swap the changed apks are redeployed using
adb install-multiple -p partial install.
When a build is invoked from studio it is not necessarily known what types of artifacts will be produced. The
InstantRunBuildContext keeps track of the verifier status, which is updated during the build. At the end of the build the
InstantRunBuildContext writes the build info file with all of the artifacts produced and the verifier status, which studio reads and deploys the appropriate artifacts.
The verifier can be set in the following ways:
InstantRunVerifierTransformchecks that the classes can be hot swapped.
NoChangesVerifierTransformhas two instances, one for java resources (
JAVA_RESOURCES_CHANGED) and the other for dependent projects (
MANIFEST_FILE_CHANGED. A change to the resource IDs that get inlined into the compiled manifest file give
InstantRunVerifierStatus for the list of possible states for verification. Depending on the patching policy (
InstantRunPatchingPolicy), each verifier status maps to an
InstantRunBuildMode. For example, a java resource change results in a full build when using a target device before Lollipop and multidex but a cold swap for multi-apk. When there are multiple verifier failures only the first one is stored, but the build modes are combined. For example
HOT_WARM, the downstream cold swap tasks are disabled in
PreColdSwapTask. If the build mode is
InstantRunBuildContext collapses all the previously built artifacts into the current build, so studio knows to deploy them all.
When the build-system determines that all the code changes since the last build are compatible with the current hot swap implementation, it creates a reload.dex with all the changed classes instrumented with the incremental instrumentation. This reload.dex is sent by studio with the Instant Run client library to the running application's Instant Run server which writes it on the disk, create a class loader and register the updated classes, so next time the updated methods are called, they are redirected to their new implementations.
When resources are changed in a compatible way, a new
resources._ap is produced with the updated resources.
When the instant run server receives updated resources, it constructs a new asset manager and uses reflection to replace the existing asset manager with one containing the updated resources. This is implemented in
A warm swap can also include hot swap changes.
When a code change is outside of the boundaries of the hot swap implementation (for example, when adding or removing a method), a cold swap is performed.
The cold swap dexing and packaging tasks, which were disabled during the previous hot and warm swap builds, run incrementally picking up all changes since the last cold swap or full build. They produce the split APKs for the classes that have been updated since the last cold swap or full build.
The classes are sharded by java package, so if the user has only been editing files in one package, only one split apk will be rebuilt.
The updated splits are then pushed and installed on to the device using an adb partial install.
Cold swap used to produce dex files, which we would add to the application classpath via classloader hacks.
The impacted slices are then pushed to the device via the runtime, which writes them to a known ‘inbox’ location in the app’s data directory. The application is then restarted. Upon restart, the application will find updated slices in its incoming mailbox, will overwrite the outdated ones with the new ones and will then create the application class loader with the updated slices before delegating back to the user’s application code.
When the application is not running but the user is touching multiple files and rebuilding. Following the same scenario as cold swap, the changes are accumulated until the end user finally deploys and run the application. When the deploy command is invoked, the impacted slices will be first copied over to the inbox location and the same startup code as for cold swap will be involved to set up the new class loader. On some devices, run-as is broken, so freeze-swap is not possible. In this case we fall back to a full build.