Apps

Information in Android Studio Flamingo


Posted by Clément Béra, Senior software program engineer

Information are a brand new Java function for immutable knowledge service lessons launched in Java 16 and Android 14. To make use of data in Android Studio Flamingo, you want an Android 14 (API stage 34) SDK so the java.lang.File class is in android.jar. That is out there from the “Android UpsideDownCake Preview” SDK revision 4. Information are primarily lessons with immutable properties and implicit hashCode, equals, and toString strategies based mostly on the underlying knowledge fields. In that respect they’re similar to Kotlin knowledge lessons. To declare a Particular person report with the fields String title and int age to be compiled to a Java report, use the next code:

@JvmRecord
knowledge class Particular person(val title: String, val age: Int)

The construct.gradle file additionally must be prolonged to make use of the right SDK and Java supply and goal. At the moment the Android UpsideDownCake Preview is required, however when the Android 14 ultimate SDK is launched use “compileSdk 34” and “targetSdk 34” instead of the preview model.

android {
compileSdkPreview "UpsideDownCake"

defaultConfig {
targetSdkPreview "UpsideDownCake"
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}

Information don’t essentially deliver worth in comparison with knowledge lessons in pure Kotlin applications, however they let Kotlin applications work together with Java libraries whose APIs embrace data. For Java programmers this permits Java code to make use of data. Use the next code to declare the identical report in Java:

public report Particular person(String title, int age) {}

Moreover the report flags and attributes, the report Particular person is roughly equal to the next class described utilizing Kotlin supply:

class PersonEquivalent(val title: String, val age: Int) {

override enjoyable hashCode() : Int {
return 31
* (31 * PersonEquivalent::class.hashCode()
+ title.hashCode())
+ Integer.hashCode(age)
}

override enjoyable equals(different: Any?) : Boolean {
if (different == null || different !is PersonEquivalent) {
return false
}
return title == different.title && age == different.age
}

override enjoyable toString() : String {
return String.format(
PersonEquivalent::class.java.simpleName + "[name=%s, age=%s]",
title,
age.toString()
)
}
}

println(Particular person(“John”, 42).toString())
>>> Particular person[name=John, age=42]

It’s doable in a report class to override the hashCode, equals, and toString strategies, successfully changing the JVM runtime generated strategies. On this case, the conduct is user-defined for these strategies.

File desugaring

Since data usually are not supported on any Android machine immediately, the D8/R8 desugaring engine must desugar data: it transforms the report code into code appropriate with the Android VMs. File desugaring includes remodeling the report right into a roughly equal class, with out producing or compiling sources. The next Kotlin supply exhibits an approximation of the generated code. For the appliance code measurement to stay small, data are desugared in order that helper strategies are shared in between data.

class PersonDesugared(val title: String, val age: Int) {
enjoyable getFieldsAsObjects(): Array<Any> {
return arrayOf(title, age)
}

override enjoyable hashCode(): Int {
return SharedRecordHelper.hash(
PersonDesugared::class.java,
getFieldsAsObjects())
}

override enjoyable equals(different: Any?): Boolean {
if (different == null || different !is PersonDesugared) {
return false
}
return getFieldsAsObjects().contentEquals(different.getFieldsAsObjects())
}

override enjoyable toString(): String {
return SharedRecordHelper.toString(
getFieldsAsObjects(),
PersonDesugared::class.java,
"title;age")
}

class SharedRecordHelper {
companion object {
enjoyable hash(recordClass: Class<*>, fieldValues: Array<Any>): Int {
return 31 * recordClass.hashCode() + fieldValues.contentHashCode()
}

enjoyable toString(
fieldValues: Array<Any>,
recordClass: Class<*>,
fieldNames: String
)
: String {
val fieldNamesSplit: Record<String> =
if (fieldNames.isEmpty()) emptyList() else fieldNames.break up(";")
val builder: StringBuilder = StringBuilder()
builder.append(recordClass.simpleName).append("[")
for (i in fieldNamesSplit.indices) {
builder
.append(fieldNamesSplit[i])
.append("=")
.append(fieldValues[i])
if (i != fieldNamesSplit.measurement - 1) {
builder.append(", ")
}
}
builder.append("]")
return builder.toString()
}
}
}
}

File shrinking

R8 assumes that the default hashCode, equals, and toString strategies generated by javac successfully characterize the interior state of the report. Subsequently, if a subject is minified, the strategies ought to replicate that; toString ought to print the minified title. If a subject is eliminated, for instance as a result of it has a relentless worth throughout all situations, then the strategies ought to replicate that; the sector is ignored by the hashCode, equals, and toString strategies. When R8 makes use of the report construction within the strategies generated by javac, for instance when it appears to be like up fields within the report or inspects the printed report construction, it is utilizing reflection. As is the case for any use of reflection, you will need to write keep rules to tell the shrinker of the reflective use in order that it could possibly protect the construction.

In our instance, assume that age is the fixed 42 throughout the appliance whereas title isn’t fixed throughout the appliance. Then toString returns completely different outcomes relying on the principles you set:

Particular person(“John”, 42).toString();

>>> Particular person[name=John, age=42]

>>> a[a=John]

>>> Particular person[b=John]

>>> a[name=John]

>>> a[a=John, b=42]

>>> Particular person[name=John, age=42]

Reflective use instances

Protect toString conduct

Say you might have code that makes use of the precise printing of the report and expects it to be unchanged. For that you will need to preserve the total content material of the report fields with a rule akin to:

-keep,allowshrinking class Particular person
-keepclassmembers,allowoptimization class Particular person { <fields>; }

This ensures that if the Particular person report is retained within the output, any toString callproduces the very same string as it could within the unique program. For instance:

Particular person("John", 42).toString();
>>> Particular person[name=John, age=42]

Nevertheless, if you happen to solely need to protect the printing for the fields which are really used, you’ll be able to let the unused fields to be eliminated or shrunk with allowshrinking:

-keep,allowshrinking class Particular person
-keepclassmembers,allowshrinking,allowoptimization class Particular person { <fields>; }

With this rule, the compiler drops the age subject:

Particular person("John", 42).toString();
>>> Particular person[name=John]

Protect report members for reflective lookup

If you must reflectively entry a report member, you usually must entry its accessor technique. For that you will need to preserve the accessor technique:

-keep,allowshrinking class Particular person
-keepclassmembers,allowoptimization class Particular person { java.lang.String title(); }

Now if situations of Particular person are within the residual program you’ll be able to safely search for the existence of the accessor reflectively:

Particular person("John", 42)::class.java.getDeclaredMethod("title").invoke(obj);
>>> John

Discover that the earlier code accesses the report subject utilizing the accessor. For direct subject entry, you must preserve the sector itself:

-keep,allowshrinking class Particular person
-keepclassmembers,allowoptimization class Particular person { java.lang.String title; }

Construct techniques and the File class

In the event you’re utilizing one other construct system than AGP, utilizing data might require you to adapt the construct system. The java.lang.File class is just not current till Android 14, launched within the SDK from “Android UpsideDownCake Preview” revision 4. D8/R8 introduces the com.android.instruments.r8.RecordTag, an empty class, to point {that a} report subclass is a report. The RecordTag is used in order that directions referencing java.lang.File can immediately be rewritten by desugaring to reference RecordTag and nonetheless work (instanceof, technique and subject signatures, and so on.).

Which means that every construct containing a reference to java.lang.File generates an artificial RecordTag class. In a scenario the place an utility is break up in shards, every shard being compiled to a dex file, and the dex information put collectively with out merging within the Android utility, this might result in duplicate RecordTag class.

To keep away from the problem, any D8 intermediate construct generates the RecordTag class as a world artificial, in a unique output than the dex file. The dex merge step is then capable of appropriately merge international synthetics to keep away from surprising runtime conduct. Every construct system utilizing a number of compilation akin to sharding or intermediate outputs is required to assist international synthetics to work appropriately. AGP absolutely helps data from model 8.1.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button