AndroidJavaObject与 AndroidJavaClass 源码分析

3,588 阅读4分钟

在学习 Unity 的过程中接触到了 Unity 与 Android 的互相调用,通过 AndroidJavaObjectAndroidJavaClass 两个类来实现在 Unity 中调用 Android 代码,查阅Unity 手册时发现 使用上面两个类调用 Android 在首次运行时比较昂贵,而第二次之后就能够有性能优势。本文就是通过分析 Untiy 与 Android 端的代码来探索 -> 为什么第二次调用会比第一次性能要好

image.png

源码资源

在查看 Unity 项目中的AndroidJavaObject 类,发现方法声明,没有具体的实现。可以通过查看 Unity 在 Github 开源代码找到对应的具体实现。
AndroidJava.cs
AndroidJNISafe.cs
AndroidJNI.bindings.cs
上面这三个类包含了 Unity 端 C# 层用于与 Android 交互的代码。 ​

ReflectionHelper.java
unity-class.jar 中包含的Android类,用于反射获取方法和变量的ID

调用过程

通过跟踪观察单个函数的调用过程,分析 Unity 调用 Android 方法过程 以 上文图片中的 jo.Call<int>("hashcode") 官方示例为例

整体函数调用逻辑

//AndroidJavaObject 

        public ReturnType Call<ReturnType>(string methodName, params object[] args)
        {
            return _Call<ReturnType>(methodName, args);
        }

        protected ReturnType _Call<ReturnType>(string methodName, params object[] args)
        {
            if (args == null) args = new object[] { null };
            //获取对应的方法 ID
            IntPtr methodID = AndroidJNIHelper.GetMethodID<ReturnType>(m_jclass, methodName, args, false);
			//创建参数数组
            jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
            try
            {
                //判断是原始类型,直接调用对应的方法
                if (AndroidReflection.IsPrimitive(typeof(ReturnType)))
                {
                    if (typeof(ReturnType) == typeof(Int32))
                        return (ReturnType)(object)AndroidJNISafe.CallIntMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Boolean))
                        return (ReturnType)(object)AndroidJNISafe.CallBooleanMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Byte))
                    {
                        Debug.LogWarning("Return type <Byte> for Java method call is obsolete, use return type <SByte> instead");
                        return (ReturnType)(object)(Byte)AndroidJNISafe.CallSByteMethod(m_jobject, methodID, jniArgs);
                    }
                    else if (typeof(ReturnType) == typeof(SByte))
                        return (ReturnType)(object)AndroidJNISafe.CallSByteMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Int16))
                        return (ReturnType)(object)AndroidJNISafe.CallShortMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Int64))
                        return (ReturnType)(object)AndroidJNISafe.CallLongMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Single))
                        return (ReturnType)(object)AndroidJNISafe.CallFloatMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Double))
                        return (ReturnType)(object)AndroidJNISafe.CallDoubleMethod(m_jobject, methodID, jniArgs);
                    else if (typeof(ReturnType) == typeof(Char))
                        return (ReturnType)(object)AndroidJNISafe.CallCharMethod(m_jobject, methodID, jniArgs);
                }
                else if (typeof(ReturnType) == typeof(String))
                    return (ReturnType)(object)AndroidJNISafe.CallStringMethod(m_jobject, methodID, jniArgs);
                else if (typeof(ReturnType) == typeof(AndroidJavaClass))
                {
                    IntPtr jclass = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
                    return (jclass == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJavaClassDeleteLocalRef(jclass);
                }
                else if (typeof(ReturnType) == typeof(AndroidJavaObject))
                {
                    IntPtr jobject = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
                    return (jobject == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
                }
                else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(ReturnType)))
                {
                    IntPtr jobject = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
                    return (jobject == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJNIHelper.ConvertFromJNIArray<ReturnType>(jobject);
                }
                else
                {
                    throw new Exception("JNI: Unknown return type '" + typeof(ReturnType) + "'");
                }
                return default(ReturnType);
            }
            finally
            {
                //销毁运行过程中产生内存
                AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
            }
        }

//AndroidJNISafe
        public static Int32 CallIntMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
        {
            try { return AndroidJNI.CallIntMethod(obj, methodID, args); } finally { CheckException(); }
        }

//AndroidJNI
        [ThreadSafe]
        public static extern Int32 CallIntMethod(IntPtr obj, IntPtr methodID, jvalue[] args);

分析代码,发现在 Unity 中实际上也是通过 JNI 层与 Android 交互。 ​

  1. 获取目标方法的 ID
  2. 创建目标方法参数数组
  3. 执行对应的 JNI 方法

获取方法签名

//AndroidJNIHelper

        // Get a JNI method ID based on calling arguments.
        public static System.IntPtr GetMethodID<ReturnType>(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
        {
            return _AndroidJNIHelper.GetMethodID<ReturnType>(jclass, methodName, args, isStatic);
        }

//_AndroidJNIHelper
        public static System.IntPtr GetMethodID<ReturnType>(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
        {
            return AndroidJNIHelper.GetMethodID(jclass, methodName, GetSignature<ReturnType>(args), isStatic);
        }

//AndroidJNIHelper
        // Scans a particular Java class for a method matching a name and a signature.
        public static IntPtr GetMethodID(IntPtr javaClass, string methodName, [UnityEngine.Internal.DefaultValue("")] string signature, [UnityEngine.Internal.DefaultValue("false")] bool isStatic)
        {
            return _AndroidJNIHelper.GetMethodID(javaClass, methodName, signature, isStatic);
        }

//_AndroidJNIHelper
        public static IntPtr GetMethodID(IntPtr jclass, string methodName, string signature, bool isStatic)
        {
            IntPtr method = IntPtr.Zero;
            try
            {
                method = AndroidReflection.GetMethodMember(jclass, methodName, signature, isStatic);
                return AndroidJNISafe.FromReflectedMethod(method);
            }
            catch (Exception e)
            {
                // Make sure this method does not throw to keep e intact
                IntPtr memberID = GetMethodIDFallback(jclass, methodName, signature, isStatic);
                if (memberID != IntPtr.Zero)
                    return memberID;
                throw e;
            }
            finally
            {
                AndroidJNISafe.DeleteLocalRef(method);
            }
        }


//AndroidReflection
		private const string RELECTION_HELPER_CLASS_NAME = "com/unity3d/player/ReflectionHelper";
        private static readonly GlobalJavaObjectRef s_ReflectionHelperClass  = new GlobalJavaObjectRef(AndroidJNISafe.FindClass(RELECTION_HELPER_CLASS_NAME));
        private static readonly IntPtr s_ReflectionHelperGetMethodID         = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getMethodID", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/reflect/Method;");
        public static IntPtr GetMethodMember(IntPtr jclass, string methodName, string signature, bool isStatic)
        {
            jvalue[] jniArgs = new jvalue[4];
            try
            {
                jniArgs[0].l = jclass;
                jniArgs[1].l = AndroidJNISafe.NewString(methodName);
                jniArgs[2].l = AndroidJNISafe.NewString(signature);
                jniArgs[3].z = isStatic;
                return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetMethodID, jniArgs);
            }
            finally
            {
                AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
                AndroidJNISafe.DeleteLocalRef(jniArgs[2].l);
            }
        }

//AndroidJNISafe
        public static IntPtr CallStaticObjectMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
        {
            try { return AndroidJNI.CallStaticObjectMethod(clazz, methodID, args); } finally { CheckException(); }
        }

//AndroidJNI
        [ThreadSafe]
        public static extern IntPtr CallStaticObjectMethod(IntPtr clazz, IntPtr methodID, jvalue[] args);

        // Converts a <tt>java.lang.reflect.Method</tt> or <tt>java.lang.reflect.Constructor</tt> object to a method ID.
        [ThreadSafe]
        public static extern IntPtr FromReflectedMethod(IntPtr refMethod);

核心部分是通过调用AndroidReflection的反射方法,调用到 Android端的反射函数。获取方法的 ID,然后再通过 JNI 转换一下。 ​

Android 端方法调用

//ReflectionHelper
protected static Method getMethodID(Class clazz, String method, String signature, boolean isStatic) {
    if (LOG) {
      g.Log(3, "? getMethodID(\"" + clazz.getName() + "\", \"" + method + "\", \"" + var2 + "\", " + (isStatic ? "static)" : "non-static)"));
    }

    Method androidMethod = null;
    ReflectionHelper.ReflectionData reflectiondata;
    int methodCount;
    if (hasReflectionDataCache(reflectiondata = new ReflectionHelper.ReflectionData(clazz, method, signature))) {
      androidMethod = (Method)reflectiondata.member;
    } else {
      Class[] paramClass = GetAndroidMenthodFunParamClass(method);

      for(float i = 0.0F; clazz != null; clazz = clazz.getSuperclass()) {
        Method[] methods;
        methodCount = (methods = clazz.getDeclaredMethods()).length;

        for(int j = 0; j < methodCount; ++j) {
          Method localMethod = methods[j];
          float var12;
          if (isStatic == Modifier.isStatic(localMethod.getModifiers()) && localMethod.getName().compareTo(method) == 0 && (percent = GetFunctionMatchPercent(localMethod.getReturnType(), localMethod.getParameterTypes(), var6)) > i) {
            androidMethod = localMethod;
            i = percent;
            if (percent == 1.0F) {
              break;
            }
          }
        }

        if (i == 1.0F || clazz.isPrimitive() || clazz.isInterface() || clazz.equals(Object.class) || clazz.equals(Void.TYPE)) {
          break;
        }
      }

      SetReflectionDataToCache(reflectiondata, androidMethod);
    }
    return androidMethod;
}

通过分析源码能够发现,ReflectionHelper 类中保存这当前反射过的方法引用,在之后的调用中如果缓存中存在,就直接返回,不会再次获取。 ​

  1. 查看缓存,存在则直接返回
  2. 不存在,遍历当前类和所有父类,查找方法签名一致的方法
  3. 添加到缓存中
  4. 返回查找到的方法

总结

在实际使用中 ,第二次调用能够比第一次调用性能优秀的关键在于 ReflectionHelper中的缓存数组,在每次获取到新的方法,变量,构造函数之后都会添加到 缓存数组中,用于之后的再次调用。