Introduction to the

Apply Changes is a feature in Android Studio that we introduced in Android Studio 3.5 to help developers quickly iterate through the Changes you make to your application. Apply Changes Uses the JVMTI API to determine whether Changes can be made in this way. On Android 11, ART (Android Runtime) extends the JVMTI API to introduce a new feature called Structural Class Redefinition (Structural Redefinition of classes). This feature enables Apply Changes to add a new class of application scenarios to Android 11 devices. You can now use Apply Changes to quickly deploy more complex Changes to your running application, including:

  • Add method (Android Studio 4.1)
  • Adding resource Files (Android Studio 4.2)
  • Add static fields (Android Studio 4.2)

This allows you to reduce development cycles and maximize productivity. In this article we’ll explore how this functionality is implemented in Android Studio.

More power through Android Studio

Apply Changes was designed from the ground up with the Android Runtime feature, so you can take advantage of its updates to evolve.

For structural redefinition of classes, classes with new methods are sent to ART, no different from previous versions of Android. There is now a new entry API, for which you will need to upgrade Android Studio to version 4.1 or higher to take advantage of dynamically adding new methods at run, both static and virtual.

However, adding variables requires new analysis in Android Studio. When adding a new variable, ART does not attempt to assign it a specific value. Stay tuned for a future article on structural redefinition of ART implementation classes. Instead, added variables are only initialized to default initializers or NULL, and how they are initialized is up to Android Studio.

This process is complicated, so consider adding a static variable y of type Long to the class (the initialization of Y occurs during class loading). Such as:

public class example {

   public final static long x = System.currentTimeMillis();
   public final static long y = System.currentTimeMillis();

}
Copy the code

If the class is loaded, the values of x and y will be very close. In cases where y is incremented by using Apply Code Changes, it is difficult to calculate the correct y value. In fact, assignment of y, even with the closest analog of class loading and initializing Y, is controversial. Because both curentTimeMillis() are called in static initialization (the

method), Apply Changes will continue to adhere to the policy of not re-executing any part of the < Clinit > method, so the new y value is 0.

Fortunately, Apply Changes already uses D8 to analyze DEX files, and as part of that process, in the latest version of Android Studio, Apply Changes is able to take advantage of the Inspector API introduced by D8. This lightweight checking API can calculate some additional information during DEX comparisons with a small amount of overhead (checking only the Modified Java classes). A set of meta-information about the new variable is appended to the ProtoBuf message sent to the Apply Changes request to the corresponding device.

On the device, before Android Studio communicates our changes to the VM, the Java Agent checks for the currently loaded classes that will be replaced. By comparing the fields of the currently loaded class with those of the newly compiled class, you can calculate the list of newly added fields and the initial values for each field. The agent then suspends all other threads temporarily, preventing uninitialized new fields from being accessed until replacement. If the replacement request executes successfully, it initializes the new field with the appropriate variable.

Limitations and upcoming features

In Android Studio 4.2 Canary 3, this feature only supports scenarios where new static primitives are added. As a derivative, this helps to add value in R.class so that Apply Changes supports new resources.

In all scenarios using Apply Changes, keep in mind that when you recompile and re-run a program, any semantics will be different. Consider an example where the constructor changes, but all objects initialized from the original constructor are not reinitialized. This rule also applies to static variables because they are not called again.

Hopefully, this new feature in Android Studio will lead to productivity gains for developers. As always, we welcome feedback and let us know what improvements you’d like to see.