Skip to content

Commit

Permalink
[Java.Interop] "Java Activation": API Declaration
Browse files Browse the repository at this point in the history
Next can-o'-worms to start implementing: Java Activation:

http://docs.xamarin.com/guides/android/under_the_hood/architecture/#Java_Activation

There are logically two ways to create a "managed peer"/"native peer"
instance pair:

 1. By creating the managed peer, which will automagically create the
    native peer instance (see the JavaObject default constructor); or

 2. By creating the native peer.

(1) is implemented. (2) has not been, meaning at present if Java code
were to create an instance of a "native peer", no corresponding
managed peer instance would be created, and thus any `native` methods
wouldn't be registered, and things would blow up rather quickly.

How should this be exposed?

Xamarin.Android has two related methods:

  * mono.android.Runtime.register(): registers native methods for a
    Java class.
  * mono.android.TypeManager.Activate(): invoked by the Java
    constructor, and responsible for associating the native peer with
    a managed peer, creating a new managed peer if necessary.

The pattern for TypeManager.Activate() invocation is as follows:

	// Xamarin.Android constructor style
	public JavaPeerType(Args... args) {
		super (args...);
		if (getClass() == JavaPeerType.class)
			TypeManager.Activate (
					AssemblyQualifiedTypeName,
					ConstructorSignature,
					this,
					new java.lang.Object[]{ args }
			);
	}

The need for the getClass() check is so that TypeManager.Activate() is
only executed once, on the most derived native peer type, and not once
per type in the inheritance chain.

My feeling is that Runtime.register() and TypeManager.Activate()
should be more closely related, because they both operate on peer
types. (Runtime.register() is also a semantically poor name, and the
it's only in Runtime for "where else should we put it?" reasons.)

The alternative Java.Interop API? ManagedPeer.runConstructor() (and
later, ManagedPeer.registerNativeMethods()), which also alters
parameter ordering and now takes the Class for the declaring class:

	// Java.Interop constructor style:
	public JavaPeerType(Args... args) {
		super (args...);
		ManagedPeer.runConstructor (
				JavaPeerType.class,
				this,
				AssemblyQualifiedTypeName,
				ConstructorSignature,
				args...
		);
	}

This "moves" the getClass() check into ManagedPeer.runConstructor(),
allowing ManagedPeer methods to consistently take a Class instance,
and also provides possible future flexibility on behavior.

ManagedPeer.runConstructor() doesn't do anything at this time; this is
just stubbing out the API, adding the appropriate calls to our Java
"native peer" test types, and providing the initial C# implementation.
  • Loading branch information
jonpryor committed Apr 25, 2014
1 parent 084a8f4 commit 8c83f64
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/Java.Interop/Java.Interop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,12 @@
<Compile Include="Java.Interop\JavaProxyThrowable.cs" />
<Compile Include="Java.Interop\JniEnvironment.Errors.cs" />
<Compile Include="Java.Interop\JniMarshalMethod.cs" />
<Compile Include="Java.Interop\ManagedPeer.cs" />
</ItemGroup>
<ItemGroup>
<CompileJavaInteropJar Include="java\com\xamarin\android\internal\JavaProxyObject.java" />
<CompileJavaInteropJar Include="java\com\xamarin\android\internal\JavaProxyThrowable.java" />
<CompileJavaInteropJar Include="java\com\xamarin\android\ManagedPeer.java" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<PropertyGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/Java.Interop/Java.Interop/JavaVM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ protected JavaVM (JavaVMOptions options)
}

JavaVMs.TryAdd (SafeHandle.DangerousGetHandle (), this);

ManagedPeer.Init ();
}

~JavaVM ()
Expand Down
50 changes: 50 additions & 0 deletions src/Java.Interop/Java.Interop/ManagedPeer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;

namespace Java.Interop {

[JniTypeInfo (ManagedPeer.JniTypeName)]
/* static */ class ManagedPeer : JavaObject {

internal const string JniTypeName = "com/xamarin/android/ManagedPeer";


static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (ManagedPeer));

static ManagedPeer ()
{
_members.JniPeerType.RegisterNativeMethods (
new JniNativeMethodRegistration ("runConstructor", RunConstructorSignature, (Action<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr>) RunConstructor)
);
}

ManagedPeer ()
{
}

internal static void Init ()
{
// Present so that JavaVM has _something_ to reference to
// prompt invocation of the static constructor & registration
}

public override JniPeerMembers JniPeerMembers {
get {return _members;}
}

const string RunConstructorSignature = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V";

static void RunConstructor (
IntPtr jnienv,
IntPtr klass,
IntPtr n_self,
IntPtr n_assemblyQualifiedName,
IntPtr n_constructorSignature,
IntPtr n_constructorArguments)
{
}
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Tests/Java.Interop-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
</ItemGroup>
<Target Name="BuildJars" Inputs="@(JavaInteropTestJar)" Outputs="@(TestJar)">
<MakeDir Directories="$(OutputPath)it-classes" />
<Exec Command="javac -d &quot;$(OutputPath)it-classes&quot; @(JavaInteropTestJar -&gt; '%(Identity)', ' ')" />
<Exec Command="javac -d &quot;$(OutputPath)it-classes&quot; -classpath &quot;$(OutputPath)\java-interop.jar&quot; @(JavaInteropTestJar -&gt; '%(Identity)', ' ')" />
<Exec Command="jar cf &quot;$(OutputPath)interop-test.jar&quot; -C &quot;$(OutputPath)it-classes&quot; ." />
</Target>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

public class CallNonvirtualBase {

public CallNonvirtualBase () {
com.xamarin.android.ManagedPeer.runConstructor (
CallNonvirtualBase.class,
this,
"Java.InteropTests.CallNonvirtualBase, Java.Interop-Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
""
);
}

boolean methodInvoked;
public void method () {
System.out.println ("CallNonvirtualBase.method() invoked!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

public class CallNonvirtualDerived extends CallNonvirtualBase {

public CallNonvirtualDerived () {
com.xamarin.android.ManagedPeer.runConstructor (
CallNonvirtualDerived.class,
this,
"Java.InteropTests.CallNonvirtualDerived, Java.Interop-Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
""
);
}

boolean methodInvoked;
public void method () {
System.out.println ("CallNonvirtualDerived.method() invoked!");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
package com.xamarin.interop;

public class CallNonvirtualDerived2 extends CallNonvirtualDerived {

public CallNonvirtualDerived2 () {
com.xamarin.android.ManagedPeer.runConstructor (
CallNonvirtualDerived2.class,
this,
"Java.InteropTests.CallNonvirtualDerived2, Java.Interop-Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
""
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
public class CallVirtualFromConstructorBase {

public CallVirtualFromConstructorBase (int value) {
com.xamarin.android.ManagedPeer.runConstructor (
CallVirtualFromConstructorBase.class,
this,
"Java.InteropTests.CallVirtualFromConstructorBase, Java.Interop-Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"System.Int32",
value
);
calledFromConstructor (value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ public class CallVirtualFromConstructorDerived extends CallVirtualFromConstructo

public CallVirtualFromConstructorDerived (int value) {
super (value);
com.xamarin.android.ManagedPeer.runConstructor (
CallVirtualFromConstructorDerived.class,
this,
"Java.InteropTests.CallVirtualFromConstructorDerived, Java.Interop-Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"System.Int32",
value
);
}

public native void calledFromConstructor (int value);
Expand Down
9 changes: 9 additions & 0 deletions src/Java.Interop/Tests/java/com/xamarin/interop/TestType.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

public class TestType {

public TestType () {
com.xamarin.android.ManagedPeer.runConstructor (
TestType.class,
this,
"Java.InteropTests.TestType, Java.Interop-Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
""
);
}

public void runTests () {
int n = getInt32Value ();
if (getInt32Value() != 42)
Expand Down
26 changes: 26 additions & 0 deletions src/Java.Interop/java/com/xamarin/android/ManagedPeer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.xamarin.android;

This comment has been minimized.

Copy link
@dellis1972

dellis1972 Dec 1, 2015

Contributor

@jonpryor I was looking at this last night.. Just a thought, currently Java.Interop is not dependent on Android as far as I can tell. do we really want to use an "android" package name here. perhaps com.xamarin.javainterop might be more generic.

This comment has been minimized.

Copy link
@jonpryor

jonpryor Feb 17, 2016

Author Member

This was fixed in 105e252.


public /* static */ class ManagedPeer {
private ManagedPeer () {
}

// public static native void registerNativeMethods (java.lang.Class<?> nativeClass, String managedType, String methods);

public static void runConstructor (
Class<?> declaringClass,
Object self,
String assemblyQualifiedName,
String constructorSignature,
Object... arguments) {
if (self.getClass() != declaringClass)
return;
runConstructor (self, assemblyQualifiedName, constructorSignature, arguments);
}

static native void runConstructor (
Object self,
String assemblyQualifiedName,
String constructorSignature,
Object... arguments
);
}

0 comments on commit 8c83f64

Please sign in to comment.