Context
The trimmable typemap runtime intentionally removes net.dot.jni.ManagedPeer from java_runtime_trimmable.jar and sets Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration=false. Trimmable-generated Java/proxy/native-registration code should use generated UCO/proxy paths such as mono.android.Runtime.registerNatives(Class) instead of the legacy dynamic Java.Interop bridge:
net.dot.jni.ManagedPeer.registerNativeMembers(Class<?> nativeClass, String methods)
net.dot.jni.ManagedPeer.construct(Object self, String constructorSignature, Object... args)
This is desirable for app size and trim/AOT safety, but old generated or handwritten Java bytecode can still reference ManagedPeer, which would fail in trimmable apps with NoClassDefFoundError.
Question
Should we add an explicit opt-in compatibility path for trimmable apps that still package Java bytecode referencing net.dot.jni.ManagedPeer?
Possible shape
Default behavior should stay strict:
- Do not package
ManagedPeer.class in trimmable apps.
- Keep
ManagedPeerNativeRegistration=false.
- Ideally detect Java bytecode references to
net/dot/jni/ManagedPeer at build time and fail with an actionable diagnostic.
Potential opt-in behavior, behind a property/feature switch, e.g. AndroidEnableManagedPeerCompatibility=true:
- Package a tiny trimmable-compatible
net.dot.jni.ManagedPeer shim.
registerNativeMembers(Class, String) logs a warning and remaps to generated trimmable registration, likely mono.android.Runtime.registerNatives(Class).
construct(Object, String, Object...) probably remains unsupported and throws a clear error unless/until we add generated per-type constructor dispatch.
- Build-time detection could downgrade from error to warning when the opt-in is enabled.
Things to decide
- Is a compatibility shim worth carrying at all, even behind opt-in?
- Should
registerNativeMembers(Class, String) ignore the legacy methods string and rely entirely on generated trimmable metadata?
- Should
construct(...) always fail, or should we eventually support it through generated dispatch metadata?
- Where should detection happen: Java source scan, class/jar scan, dex scan, or a combination?
- What property name and diagnostic code should we use?
Why this matters
We want to keep default trimmable apps free of the old dynamic bridge, but we also want a clear story for users who have older JavaInterop1-generated Java wrappers or handwritten Java copied from Java.Interop samples that still call ManagedPeer.*.
Context
The trimmable typemap runtime intentionally removes
net.dot.jni.ManagedPeerfromjava_runtime_trimmable.jarand setsJava.Interop.RuntimeFeature.ManagedPeerNativeRegistration=false. Trimmable-generated Java/proxy/native-registration code should use generated UCO/proxy paths such asmono.android.Runtime.registerNatives(Class)instead of the legacy dynamic Java.Interop bridge:This is desirable for app size and trim/AOT safety, but old generated or handwritten Java bytecode can still reference
ManagedPeer, which would fail in trimmable apps withNoClassDefFoundError.Question
Should we add an explicit opt-in compatibility path for trimmable apps that still package Java bytecode referencing
net.dot.jni.ManagedPeer?Possible shape
Default behavior should stay strict:
ManagedPeer.classin trimmable apps.ManagedPeerNativeRegistration=false.net/dot/jni/ManagedPeerat build time and fail with an actionable diagnostic.Potential opt-in behavior, behind a property/feature switch, e.g.
AndroidEnableManagedPeerCompatibility=true:net.dot.jni.ManagedPeershim.registerNativeMembers(Class, String)logs a warning and remaps to generated trimmable registration, likelymono.android.Runtime.registerNatives(Class).construct(Object, String, Object...)probably remains unsupported and throws a clear error unless/until we add generated per-type constructor dispatch.Things to decide
registerNativeMembers(Class, String)ignore the legacymethodsstring and rely entirely on generated trimmable metadata?construct(...)always fail, or should we eventually support it through generated dispatch metadata?Why this matters
We want to keep default trimmable apps free of the old dynamic bridge, but we also want a clear story for users who have older JavaInterop1-generated Java wrappers or handwritten Java copied from Java.Interop samples that still call
ManagedPeer.*.