首页 > 解决方案 > 从android java返回字节数组引用到csharp unity

问题描述

字节[]字节=调用(“getBytes”);其中 getBytes 函数返回一个字节 []。

调用上述函数以在 csharp 中获取图像 rgb 数据。返回的 byte[] 被深度复制到 bytes 数组中。

由于返回字节数组很大,因此深度复制会增加更多时间。

如何在 csharp 中制作字节数组以仅保存 java byte [] 的引用?


public class TestUtil : MonoBehaviour
{

    public static string TAG = "--------TestUtil------------> ";

    private static AndroidJavaObject pluginClass;    
    public static List<byte[]> rgbList = new List<byte[]>();

    void Start()
    {


        Debug.Log(TAG + "start called");

        //mainDataArray = new byte[1280*720*4];
        Debug.Log(TAG + "creating java object");

        initializePlayer();

    }

    public void initializePlayer()
    {
        // StreamHandler is the Javaclass. here i am creating a object StreamHandler
        pluginClass = new AndroidJavaObject("com.test.android.decoder.StreamHandler");

       // using this code to get the context
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

        // setting context StreamHandler object
        pluginClass.Call("setActivity", activity);

        // setting the interface object, where java class will call the respective function
        pluginClass.Call("setOnDecodeListener", new AndroidPluginCallback());       

        // initializing the player
        pluginClass.Call("init", ipAddress, port, outWidth, outHeight);

        Debug.Log(TAG + " Initialization done");
    }

    public void quitApplication(string sid)
    {
        Application.Quit();
    }

    private void Update()
    {
        if (Input.GetKey(KeyCode.Escape)) {
            Debug.Log(TAG + "Escape");
            quitApplication(sId);
        }        
    }

    int count;
    private void LateUpdate()
    {



            if (0 != rgbList.Count) {
                // here i am updating the rgb texture to Quad gameobject
        }

    }

    class AndroidPluginCallback : AndroidJavaProxy
    {
        public AndroidPluginCallback() : base("com.test.android.OnDecodeListener") { }

        public void success(byte[] videoPath)
        {            
        }

        public void onFrameAvailable()
        {

            // Called when java code successfully generate RGBA data

            long startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();

            // Need to call "getBytes()" to get the RGBA frame. 
            //Note: generally if you call same function from another java class it do shallow copy to byte[] object 
            // but in this case it is doing deep copy or i am not sure whats going on.

            byte[] rawBytes = pluginClass.Call<byte[]>("getBytes"); // width and height of frame is 1088x1088

            rgbList.Add(rawBytes);

            long diff = DateTimeOffset.Now.ToUnixTimeMilliseconds() - startTime;
            Debug.Log(TAG + "Entered into onFrameAvailable callback. time taken to copy to rawbytes: " + diff); // diff is 14ms on average. Not sure why.
        }

        public void fail(string errorMessage)
        {
            Debug.Log(TAG + "ENTER callback onError: " + errorMessage);
        }

    }
}

标签: c#androidunity3d

解决方案


AndroidJavaObject使用 JNI(Java 本机接口)将数据编组到 Java 土地和从 Java 土地传出。根据 Java 在内存中存储数组的方式,JNI 可能需要进行深度复制以形成 C# 可以理解的数组,例如 JVM 最初将数组存储在非连续块中的情况。

这是IBM 的描述

JNI 在 Java 代码和本机代码之间提供了一个干净的接口。为了保持这种分离,数组作为不透明的句柄传递,并且本机代码必须回调到 JVM 以便使用 set 和 get 调用来操作数组元素。Java 规范将这些调用提供对数组的直接访问还是返回数组的副本由 JVM 实现决定。例如,当 JVM 以不连续存储数组的方式优化数组时,它可能会返回一个副本。

那么,这些调用可能会导致复制被操作的元素。例如,如果您对具有 1,000 个元素的数组调用 GetLongArrayElements(),您可能会导致分配和复制至少 8,000 个字节(1,000 个元素 * 每个 long 8 个字节)。然后,当您使用 ReleaseLongArrayElements() 更新数组的内容时,可能需要另一个 8,000 字节的副本来更新数组。即使您使用较新的 GetPrimitiveArrayCritical(),规范仍然允许 JVM 制作完整数组的副本。

因此,基本上,尽量避免跨 JNI 编组数组(例如尽可能AndroidJavaObject多地编组,因为 JNI 是否进行深拷贝并不取决于 C#。


推荐阅读