public override IJavaPeerable PeekPeer(JniObjectReference reference)
        {
            if (!reference.IsValid)
            {
                return(null);
            }

            var hash = JNIEnv.IdentityHash(reference.Handle);

            lock (instances) {
                IdentityHashTargets targets;
                if (instances.TryGetValue(hash, out targets))
                {
                    for (int i = targets.Count - 1; i >= 0; i--)
                    {
                        var wref = targets [i];
                        if (!wref.TryGetTarget(out var result) || !result.PeerReference.IsValid)
                        {
                            targets.RemoveAt(i);
                            continue;
                        }
                        if (!JniEnvironment.Types.IsSameObject(reference, result.PeerReference))
                        {
                            continue;
                        }
                        return(result);
                    }
                }
            }
            return(null);
        }
        internal void AddPeer(IJavaPeerable value, IntPtr handle, JniHandleOwnership transfer, out IntPtr handleField)
        {
            if (handle == IntPtr.Zero)
            {
                handleField = handle;
                return;
            }

            var transferType = transfer & (JniHandleOwnership.DoNotTransfer | JniHandleOwnership.TransferLocalRef | JniHandleOwnership.TransferGlobalRef);

            switch (transferType)
            {
            case JniHandleOwnership.DoNotTransfer:
                handleField = JNIEnv.NewGlobalRef(handle);
                break;

            case JniHandleOwnership.TransferLocalRef:
                handleField = JNIEnv.NewGlobalRef(handle);
                JNIEnv.DeleteLocalRef(handle);
                break;

            case JniHandleOwnership.TransferGlobalRef:
                handleField = handle;
                break;

            default:
                throw new ArgumentOutOfRangeException("transfer", transfer,
                                                      "Invalid `transfer` value: " + transfer + " on type " + value.GetType());
            }
            if (handleField == IntPtr.Zero)
            {
                throw new InvalidOperationException("Unable to allocate Global Reference for object '" + value.ToString() + "'!");
            }

            IntPtr hash = JNIEnv.IdentityHash(handleField);

            value.SetJniIdentityHashCode((int)hash);
            if ((transfer & JniHandleOwnership.DoNotRegister) == 0)
            {
                AddPeer(value, new JniObjectReference(handleField, JniObjectReferenceType.Global), hash);
            }

            if (Logger.LogGlobalRef)
            {
                JNIEnv._monodroid_gref_log("handle 0x" + handleField.ToString("x") +
                                           "; key_handle 0x" + hash.ToString("x") +
                                           ": Java Type: `" + JNIEnv.GetClassNameFromInstance(handleField) + "`; " +
                                           "MCW type: `" + value.GetType().FullName + "`\n");
            }
        }
        public override void RemovePeer(IJavaPeerable value)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (!value.PeerReference.IsValid)
            {
                throw new ArgumentException("Must have a valid JNI object reference!", nameof(value));
            }

            var reference = value.PeerReference;
            var hash      = JNIEnv.IdentityHash(reference.Handle);

            RemovePeer(value, hash);
        }