AndroidJavaProxy equals

Hi,

We are developing a wrapper for an Android plugin. We are using the AndroidJavaProxy in a very straightforward way, and I hope that in the way the author intended.

In Java code, we have a list of IListeners:

private List<IPlayerListener> listeners;

public void addPlayerUpdates(IPlayerListener listener)
{
    listeners.add(listener);
}
public void removePlayerUpdates(IPlayerListener listener)
{
    listeners.remove(listener);
}

In Unity3D, we inherit from AndroidJavaProxy and then call addPlayerUpdates/removePlayerUpdates passing instances of MyAndroidJavaProxyListener.

This is the issue: listeners.remove(listener) is unexpectedly calling back to the C# code trying to determine if two listener are equal:

I/Unity: Exception: No such proxy method: MyAndroidJavaProxyListener.equals(UnityEngine.AndroidJavaObject)
I/Unity: at UnityEngine.AndroidJavaProxy.Invoke (System.String methodName, System.Object[ ] args)
I/Unity: at UnityEngine.AndroidJavaProxy.Invoke (System.String methodName, UnityEngine.AndroidJavaObject[ ] javaArgs)
I/Unity : at UnityEngine._AndroidJNIHelper.InvokeJavaProxyMethod (UnityEngine.AndroidJavaProxy proxy, IntPtr jmethodName, IntPtr jargs)
I/Unity: UnityEngine.AndroidJNI:CallVoidMethod(IntPtr, IntPtr, jvalue[ ])
I/Unity: UnityEngine.AndroidJNISafe:CallVoidMethod(IntPtr, IntPtr, jvalue[ ])
I/Unity: UnityEngine.AndroidJavaObject:_Call(String, Object[ ])
I/Unity: UnityEngine.AndroidJavaObject:Call(String, Object[ ])
I/Unity: com.theroofisonfire.impl.Player:removePlayerUpdates(IPlayerListener)

So, the question is how to implement this equals method:

public class MyAndroidJavaProxyListener : AndroidJavaProxy
{
    internal bool equals(AndroidJavaObject other)
    {
         return other.GetRawObject() == ????????
    }
}

I just don’t see how to get the IntPtr to the AndroidJavaProxy. Where is it?

Thanks

Just in case this is useful for somebody, this is what I did:

internal bool equals(AndroidJavaObject other)
{
	bool ret = false;

	if (mComparingWho != null)
	{
		ret = mComparingWho == this;
		mComparingWho = null;
	}
	else
	{
		mComparingWho = this;
		ret = other.Call<bool>("equals", other);
	}

	return ret;
}
	
static Listener mComparingWho = null;
1 Like

Thanks Zincoontrin.

Unfortunately there are problems with this solution. I’d thought I would post a note so it doesn’t catch anyone out in future.

Firstly, if ‘other’ is any type other than the Listener class then this function will always return true (see line 13).

Also, consider this case:

  • There are 2 instances of this AndroidJavaProxy class: A and B.
  • There is another object of a different kind: C.
  • Calling A==C, incorrectly returns true, and mComparingWho will still hold a reference to A.
  • Calling B==B will now return false.

This implementation will only work if you only ever have your objects compared to other objects of the same proxy class.

This is the solution I came up with in the end.

public class MyProxy : AndroidJavaProxy
{
    // ...

    static bool thatWasMe;

    // proxy for int java.lang.Object.hashCode()
    int hashCode() {
        thatWasMe = true;
        return this.GetHashCode();
    }

    // proxy for boolean java.lang.Object.equals(Object o)
    bool equals(AndroidJavaObject o) {
        thatWasMe = false;
        o.Call<int>("hashCode");
        return thatWasMe;
    }
}

I stumbled into this too, i ended up with something like this

public class MyProxy : AndroidJavaProxy
{
    private static int lastId = 0;

    private int id;
    public MyProxy(): base("com.example.game.MyProxy")
    {
        this.id = ++lastId;
    }

    int hashCode() {
        return this.id;
    }

    bool equals(AndroidJavaObject o) {
        return hashCode() == o.Call<int>("hashCode");
    }
}

We have added equals, hashCode and toString implementations to AndroidJavaProxy class in Unity2017.1