Summary
The UCO constructor callback (nctor_0_uco) skips activation entirely when ShouldSkipActivation returns true (peer exists and is not Activatable/Replaceable). However, when the peer IS Activatable or Replaceable, the UCO currently also skips — it should instead invoke the constructor on the existing peer instance.
Current behavior
// nctor_0_uco — skips if peer exists and is NOT Activatable/Replaceable
if (!WithinNewObjectScope && !ShouldSkipActivation(self))
{
// create new instance via GetUninitializedObject + ctor
}
// Otherwise: return (no-op) — even for Activatable/Replaceable peers
Expected behavior
Should mirror ManagedPeer.Construct (lines 80-88, 117-119):
var self = PeekPeer(r_self);
if (self != null) {
var state = self.JniManagedPeerState;
if ((state & Activatable) != Activatable && (state & Replaceable) != Replaceable) {
return; // skip — peer exists and is stable
}
}
// ... later:
if (self != null) {
cinfo.Invoke(self, pvalues); // re-invoke ctor on existing Activatable/Replaceable peer
return;
}
// else: create new instance via ActivatePeer
The Activatable state is set by ConstructPeer and Replaceable is used for types like Application that may be replaced during process lifecycle. If such a type's nctor_0_uco is called, the constructor should be invoked on the existing instance rather than being silently skipped.
Impact
May affect Activity recreation after configuration changes or Application replacement. Needs investigation to determine if this code path is actually hit in practice for trimmable typemap apps.
Context
ManagedPeer.Construct reference: external/Java.Interop/src/Java.Interop/Java.Interop/ManagedPeer.cs:80-119
- UCO emission:
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs EmitUcoConstructor
ShouldSkipActivation: src/Mono.Android/Java.Interop/JavaPeerProxy.cs:95-105
Part of #10788
Summary
The UCO constructor callback (
nctor_0_uco) skips activation entirely whenShouldSkipActivationreturnstrue(peer exists and is not Activatable/Replaceable). However, when the peer IS Activatable or Replaceable, the UCO currently also skips — it should instead invoke the constructor on the existing peer instance.Current behavior
Expected behavior
Should mirror
ManagedPeer.Construct(lines 80-88, 117-119):The Activatable state is set by
ConstructPeerand Replaceable is used for types likeApplicationthat may be replaced during process lifecycle. If such a type'snctor_0_ucois called, the constructor should be invoked on the existing instance rather than being silently skipped.Impact
May affect Activity recreation after configuration changes or Application replacement. Needs investigation to determine if this code path is actually hit in practice for trimmable typemap apps.
Context
ManagedPeer.Constructreference:external/Java.Interop/src/Java.Interop/Java.Interop/ManagedPeer.cs:80-119src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csEmitUcoConstructorShouldSkipActivation:src/Mono.Android/Java.Interop/JavaPeerProxy.cs:95-105Part of #10788