首页 » 技术文章 » C/Java/Unity(C#)三者间大数据交互简析

C/Java/Unity(C#)三者间大数据交互简析

 

最近,在实际工作中,遇到以下一些问题

  • java层有大量的二进制数据需要传送给脚本层
  • java层调用Unity是通过UnityPlayer.UnitySendMessage函数
  • 而该函数只能将字符串作为参数,传递给Unity层的脚本函数

这就需要把二进制数据进行Base64编码,形成字符串,才能作为参数传递。在Unity层获取到该数据后,又得解码Base64才能得到需要的二进制数据。从这一个过程可以看出:

  • 对二进制的Base64编码和解码较浪费资源

有没有更好的办法呢?
网上有很多文档,这里做个整理,希望能说的更清晰,有助于理解和往后的应用。
大致的思路就是:

  • 大数据都存放在C代码里,Java和Unity需要存储数据的时候,都直接面向C操作。

Java独立线程中,将数据存放到C内存块后,通过UnityPlayer.UnitySendMessage函数,调用Unity层函数,Unity层就可以立刻获取到该数据。
下面的实例中,Java层函数callWriteBuffer执行最后,会调用UnityPlayer.UnitySendMessage函数通知Unity层去读取,非常方便。
以下是各个模块实现的功能或接口:
1)C动态库,输出接口函数:
1

  • 该动态库内部有一个内存块,供Java和Unity读写。
  • 供C调用的函数,和就是普通的动态库输出做法是一样的。
  • 供Java调用的函数,需要注意:
    1. 函数名命名规范:包名_类名_函数名(包名中的点用下划线替换)
    2. Java与C之间数据类型对应关系
    3. 前面两个参数是固定的,必须的

2)Java层,输出接口函数:
2

  • 动态库函数声明时候,必须注意:包名、类名、函数名,与动态库里面的实现要一致

3)Unity层,输出接口函数:
3

  • Java调用Unity(C#)是通过UnityPlayer.UnitySendMessage实现的。

下面是主要代码:
创建Android工程,添加下面的代码,编译生成libDataExchangeDemo.so文件。

// C代码: DataExchangeDemo.cpp  
#include   
#include <android/log.h>  
#include   
#include   
  
#define LOGD(...)   __android_log_print(ANDROID_LOG_DEBUG  , "DbgDemo", __VA_ARGS__)  
  
#ifdef __cplusplus  
extern "C" {  
#endif  
  
unsigned char* g_byBuffer = NULL;  
int g_nLen = 0;  
  
// 供Unity调用  
int WriteBuffer(char* buffer, int size)  
{  
    if (g_byBuffer) {  
        delete[] g_byBuffer;  
        g_byBuffer = NULL;  
    }  
    if (g_byBuffer == NULL) {  
        g_byBuffer = new unsigned char[size];  
        g_nLen = size;  
    }  
    memcpy(g_byBuffer, buffer, size);  
    return 1;  
}  
  
int ReadBuffer(char* buffer, int size)  
{  
    if (size >= g_nLen) {  
        memcpy(buffer, g_byBuffer, g_nLen);  
        return g_nLen;  
    }  
    return 0;  
}  
  
int GetBufferSize()  
{  
    return g_nLen;  
}  
  
int Add(int a, int b){  
    return a + b;  
}  
  
// 供Java调用  
JNIEXPORT void JNICALL Java_com_xsj_soapi_KSOAPI_WriteBuffer(JNIEnv* env, jclass jc, jbyteArray inByteArray){  
    int nLen = 0;  
    jbyte* pInBuf = NULL;  
  
    LOGD("%s", "write buffer");  
    nLen = env->GetArrayLength(inByteArray);  
    pInBuf = env->GetByteArrayElements(inByteArray, 0);  
  
    if (g_byBuffer != NULL) {  
        delete[] g_byBuffer;  
        g_byBuffer = NULL;  
    }  
    g_byBuffer = new unsigned char[nLen];  
  
    nLen = env->GetArrayLength(inByteArray);  
    memcpy(g_byBuffer, pInBuf, nLen);  
    g_nLen = nLen;  
    LOGD("%s:%d", "save buffer", g_nLen);  
}  
  
JNIEXPORT jbyteArray JNICALL Java_com_xsj_soapi_KSOAPI_ReadBuffer(JNIEnv* env, jclass jc){  
    jbyteArray rval;  
    jbyte* pOutBuf;  
  
    LOGD("%s:%d", "read buffer", g_nLen);  
  
    rval = env->NewByteArray(g_nLen);  
    pOutBuf = env->GetByteArrayElements(rval, 0);  
    memcpy(pOutBuf, g_byBuffer, g_nLen);  
    env->ReleaseByteArrayElements(rval, pOutBuf, 0);  
  
    return rval;  
}  
  
  
JNIEXPORT jint JNICALL Java_com_xsj_soapi_KSOAPI_Add(JNIEnv* env, jclass jc, jint a, jint b){  
    return a + b;  
}  
  
#ifdef __cplusplus  
}  
#endif 

生成dataexchangedemo.jar
创建KSOAPI.java文件,调用libDataExchangeDemo.so动态库。内容如下:

package com.xsj.soapi;  
   
import com.unity3d.player.UnityPlayer;  
   
public final class KSOAPI {  
    static{  
        // 加载动态库  
       System.loadLibrary("DataExchangeDemo");  
    }  
     
    // 函数声明  
    privatestatic native void WriteBuffer(byte[] buffer);  
    privatestatic native byte[] ReadBuffer();  
    privatestatic native int Add(int a, int b);  
     
    // Java调用Unity函数  
    privatestatic String mUnityObjName; // 脚本挂接的对象名  
    privatestatic String mUnityFunName; // 脚本里公有的函数,如:voidfunName(string msg);  
     
    publicstatic int getPid(){  
        returnandroid.os.Process.myPid();  
    }  
     
    publicstatic void SetCallbackInfo(String objName, String funName)   {  
        mUnityObjName= objName;  
        mUnityFunName= funName;  
    }  
     
    publicstatic void callWriteBuffer(byte[] byIn){  
        WriteBuffer(byIn);  
         
        // 如果修改UI,必须这样写  
        UnityPlayer.currentActivity.runOnUiThread(newRunnable()  
        {  
            publicvoid run()  
            {  
                UnityPlayer.UnitySendMessage(mUnityObjName,mUnityFunName, "write ok");  
            }  
        });  
    }  
     
    publicstatic byte[] callRead(){  
        returnReadBuffer();  
    }  
     
    publicstatic int callAdd(int a, int b){  
        returnAdd(a, b);  
    }  
     
}  

创建Unity测试工程。
创建文件DataExchangeAPI.cs,内容如下:

using System;  
usingUnityEngine;  
usingSystem.Collections;  
usingSystem.Runtime.InteropServices;  
usingSystem.Collections.Generic;  
   
public classDataExchangeAPI {  
   
    // 调用Java函数  
    static public int getPid()  
    {  
        AndroidJavaClass sdk = new AndroidJavaClass("com.xsj.soapi.KSOAPI");  
        returnsdk.CallStatic("getPid", new object[] {});  
    }  
   
         // 用于设置,当callWriteBuffer函数执行完成,回调Unity脚本哪个函数  
    static public void SetCallbackInfo(stringobjName, string funName)  
    {  
        AndroidJavaClass sdk = newAndroidJavaClass("com.xsj.soapi.KSOAPI");  
       sdk.CallStatic("SetCallbackInfo", new object[] { objName,funName });  
    }  
   
    static public int callAdd(int a, int b)  
    {  
        AndroidJavaClass sdk = newAndroidJavaClass("com.xsj.soapi.KSOAPI");  
        returnsdk.CallStatic("callAdd", new object[] { a, b });  
    }  
   
    static public void callWriteBuffer(byte[]buf)  
    {  
        AndroidJavaClass sdk = new AndroidJavaClass("com.xsj.soapi.KSOAPI");  
       sdk.CallStatic("callWriteBuffer", new object[] { buf });  
    }  
   
    static public byte[] callRead()  
    {  
        AndroidJavaClass sdk = newAndroidJavaClass("com.xsj.soapi.KSOAPI");  
        return sdk.CallStatic<byte[]>("callRead",new object[] {});  
    }  
   
    // 调用C函数  
    [DllImport("DataExchangeDemo")]  
    private static extern intWriteBuffer(IntPtr buffer, int size);  
   
    [DllImport("DataExchangeDemo")]  
    private static extern int ReadBuffer(IntPtrbuffer, int size);  
   
    [DllImport("DataExchangeDemo")]  
    private static extern int GetBufferSize();  
   
    static public void cWriteBuffer(byte[]buf){  
        IntPtr buffer =Marshal.AllocHGlobal(buf.Length);  
       System.Runtime.InteropServices.Marshal.Copy(buf, 0, buffer, buf.Length);  
        WriteBuffer(buffer, buf.Length);  
        Marshal.FreeHGlobal(buffer);  
    }  
   
    static public byte[] cReadBuffer()  
    {  
        int nLen = GetBufferSize();  
        IntPtr buffer =Marshal.AllocHGlobal(nLen);  
        byte[] byBuf = new byte[nLen];  
        ReadBuffer(buffer, nLen);  
       System.Runtime.InteropServices.Marshal.Copy(buffer, byBuf, 0, nLen);  
        Marshal.FreeHGlobal(buffer);  
        return byBuf;  
    }  
}  

提醒:Unity中必须添加AndroidManifest.xml文件,否则所有的调用都会失败的。

本文作者:帮帮哥

原文链接:C/Java/Unity(C#)三者间大数据交互简析,转载请注明来源!

5