blob: 96db8a1c52cf27b4daf42f8c9c1f922911ad4c16 [file] [view]
---
title: Android
sidebar_position: 9
id: android
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---
Fory JSON supports ordinary classes on Android API level 26 and later through the regular
`fory-json` artifact. Runtime JSON code generation and asynchronous compilation are disabled
automatically, so `ForyJson.builder().build()` uses the interpreted object mapper.
Kotlin applications use the ordinary `fory-json-kotlin` runtime. KSP is needed when R8 or ProGuard
can rename or remove Kotlin model members or when a Kotlin-source Mixin adds inferred
`JsonSubTypes` to a Java sealed target.
## Installation and Codec Model
Add Fory JSON to the application:
```kotlin
dependencies {
implementation("org.apache.fory:fory-json:${foryVersion}")
}
```
For Kotlin, apply Kotlin 2.3.20 and add the Kotlin JSON runtime:
```kotlin
plugins {
kotlin("android") version "2.3.20"
}
dependencies {
implementation("org.apache.fory:fory-json-kotlin:${foryVersion}")
}
```
If the application enables R8 or ProGuard, or if a Kotlin-source Mixin adds inferred `JsonSubTypes`
to a Java sealed target, also apply KSP 2.3.8:
```kotlin
plugins {
id("com.google.devtools.ksp") version "2.3.8"
}
dependencies {
ksp("org.apache.fory:fory-json-kotlin-ksp:${foryVersion}")
}
```
Create the runtime with `ForyJsonKotlin.builder()`. For a minified build, annotate every required
Kotlin source model with `@JsonType`. For a third-party Kotlin target, declare a source-owned exact
`@JsonMixin` instead. If a Kotlin-source Mixin adds inferred `JsonSubTypes` to a Java sealed target,
also apply `fory-annotation-processor` and compile on JDK 17 or newer.
## Custom Codecs
`@JsonCodec` has the same declaration behavior on Android and the JVM. It supports complete values,
direct collection and array elements, `Optional` and `AtomicReference` contents, Map keys and
values, ordinary getters, setter value parameters, and `JsonCreator` parameters:
```java
import java.util.List;
import org.apache.fory.json.annotation.JsonCodec;
public final class Invoice {
@JsonCodec(elementCodec = MoneyCodec.class)
public List<Money> items;
private Money primary;
public void setPrimary(@JsonCodec(MoneyCodec.class) Money primary) {
this.primary = primary;
}
public Invoice() {}
}
```
Child codecs act on one direct level only. For example, `elementCodec` on `Money[][]` handles each
`Money[]`, and `elementCodec` on `AtomicReferenceArray<Money>` handles each `Money`. Use a complete
`value` codec when deeper custom behavior is required.
## Java Generated Access and R8 Rules
Add the annotation processor and mark application object models with `JsonType` to generate direct
field, getter, setter, Record constructor, `JsonCreator`, and `JsonValidator` operations together
with exact R8 rules:
```kotlin
dependencies {
annotationProcessor("org.apache.fory:fory-annotation-processor:${foryVersion}")
}
```
```java
import org.apache.fory.json.annotation.JsonType;
import org.apache.fory.json.annotation.JsonValidator;
@JsonType
public final class Invoice {
public long total;
@JsonValidator
public void validate() {
if (total < 0) {
throw new IllegalArgumentException("total must not be negative");
}
}
}
```
## Mixins
The same processor supports Fory JSON Mixins. A Mixin declares one exact target and is registered
on the `ForyJson` builder that should use it:
```java
import org.apache.fory.json.ForyJson;
import org.apache.fory.json.annotation.JsonBase64;
import org.apache.fory.json.annotation.JsonMixin;
@JsonMixin(target = ThirdPartyInvoice.class)
public abstract class ThirdPartyInvoiceMixin {
@JsonBase64 byte[] signature;
}
ForyJson json =
ForyJson.builder().registerMixin(ThirdPartyInvoiceMixin.class).build();
```
Compile a non-empty Java-source Mixin for a Java target with `fory-annotation-processor`. The
registered Mixin remains scoped to its exact target. Registered codecs, effective type codecs, and
built-in mappings keep their normal codec-selection precedence.
A Mixin may place `JsonValidator` on a public abstract zero-argument `void` method that exactly
matches a public target method. The target does not need `JsonType` solely for a Mixin validator.
The target does not need `JsonType` merely because it has a Mixin. If the target also uses
`JsonType`, the registered Mixin still defines the effective annotations for that `ForyJson`
instance.
Only one source is enabled for an exact target in one built `ForyJson` instance. A later
registration for that target replaces an earlier registration on the builder, and `build()`
snapshots the selected mapping.
Use the Fory processors instead of broad package keep rules. A Java-only Mixin pair requires
`fory-annotation-processor`; a pair involving Kotlin requires `fory-json-kotlin-ksp`. A
Kotlin-source Mixin that adds inferred `JsonSubTypes` to a Java sealed target requires both and must
be compiled on JDK 17 or newer.
## Reflection-Based Models
Ordinary non-Record classes that omit `JsonType` can supply equivalent exact rules themselves unless
they declare `JsonValidator`. Direct validators require `JsonType` and
`fory-annotation-processor`; manual reflection rules are not sufficient. Retain every model
constructor, field, method, generic signature, declaration annotation, and parameter annotation
used by Fory JSON, plus the public no-argument constructor of every annotation-selected codec. For
a model without a validator:
```proguard
-keepattributes Signature,RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations
-keepattributes AnnotationDefault,MethodParameters,InnerClasses,EnclosingMethod
-keep,allowoptimization class com.example.Invoice {
public <init>();
public java.util.List items;
public void setPrimary(com.example.Money);
}
-keep,allowoptimization,allowobfuscation class com.example.MoneyCodec {
public <init>();
}
```
The same exact-rule approach supports every `JsonCodec` member; it is not limited to complete-value
codecs. `JsonType` is not required for codec selection on an ordinary class.
This reflection-based section applies to Java models. Kotlin models use the Kotlin JSON module. For
a minified Android build, apply KSP instead of writing broad package keep rules.
Java `@JsonType` models support effective `JsonValidator`, `JsonValue`, `JsonRawValue`,
`JsonBase64`, and `JsonFormat` annotations. Without `@JsonType`, those annotations still work
through reflection, but a release-minified application must keep the exact annotated members,
annotation attributes, and codec constructor itself. A `JsonValue` method may use a non-JavaBean
name, so its manual rule must name that method explicitly.
Kotlin models use the same effective annotations. Enable KSP for minified builds. `JsonFormat` keeps
the same direct-field and one-wrapper-level behavior as on the JVM, including `timezone` for
`Instant`, `ZonedDateTime`, and `OffsetDateTime`.
Android supports inferred `JsonSubTypes` for Java and Kotlin sealed hierarchies. Java sealed
inference requires `fory-annotation-processor` and JDK 17 or newer. Kotlin models in minified builds
require `fory-json-kotlin-ksp`. For a Kotlin-source Mixin that adds inferred `JsonSubTypes` to a Java
sealed target, apply both processors even when shrinking is disabled. Only the sealed hierarchy is
considered; package and classpath subtype scanning are not supported.
Android Fory JSON requires a retained no-argument constructor for an ordinary mutable class; it may
be non-public when Android reflection can make it accessible. `JsonCreator` constructor-backed
classes follow the normal creator rules instead. Retain every field and method used for reflection,
or use an application codec when a model cannot satisfy those requirements. `JsonUnwrapped`
supports mutable classes, creator-backed classes, and Records through their normal property and
construction paths. Apply the normal `JsonType` requirements to the containing model and every
unwrapped child. In a minified Kotlin build, annotate each required model and enable KSP.
## Records
Android-desugared Records require either a direct `@JsonType` declaration or an exact `@JsonMixin`
processed by `fory-annotation-processor`. Manual R8 rules alone are insufficient because Android
does not provide the Java Record reflection APIs. This also applies to a Record whose complete
representation is a `JsonValue` String. Every Record in a `JsonUnwrapped` path needs its own direct
`JsonType` declaration or processed exact `JsonMixin`. Child codecs act on one level exactly as they
do on the JVM; use a complete value codec for deeper nested behavior.