HTML5技术

Unity3D手游开发实践 - 吴秦(2)

字号+ 作者:H5之家 来源:H5之家 2016-04-21 13:00 我要评论( )

将回调函数指针注册到非托管C/C++代码中(C#中回调函数指委托delegate) 调用注册过的托管C#函数指针 相比较托管调用非托管,回调函数方式稍微复杂一些。回调函数非常适合重复执行的任务、异步调用等情况下使用。

  • 将回调函数指针注册到非托管C/C++代码中(C#中回调函数指委托delegate)
  • 调用注册过的托管C#函数指针
  • 相比较托管调用非托管,回调函数方式稍微复杂一些。回调函数非常适合重复执行的任务、异步调用等情况下使用。

    由上面的介绍可以知道CLR提供了C#程序运行的环境,与非托管代码的C/C++交互调用也由它来完成。CLR提供两种用于与非托管C/C++代码进行交互的机制:

  • 平台调用(Platform Invoke,简称PInvoke或者P/Invoke),它使托管代码能够调用从非托管DLL中导出的函数。
  • COM 互操作,它使托管代码能够通过接口与组件对象模型 (COM) 对象交互。考虑跨平台性,Unity3D不使用这种方式。
  •  平台调用依赖于元数据在运行时查找导出的函数并封送(Marshal)其参数。 下图显示了这一过程。

    注意:1.除涉及回调函数时以外,平台调用方法调用从托管代码流向非托管代码,而绝不会以相反方向流动。 虽然平台调用的调用只能从托管代码流向非托管代码,但是数据仍然可以作为输入参数或输出参数在两个方向流动。2.图中DLL表示动态库,Windows平台指.dll文件、Linux/Android指.so文件、Mac OS X指.dylib/framework文件、iOS中只能使用.a。后文都使用DLL代指,并且DLL使用C/C++编写。

    当"平台调用"调用非托管函数时,它将依次执行以下操作:

    注意

    只在第一次调用函数时,才会查找和加载 DLL 并查找函数在内存中的地址。iOS中使用的是.a已经静态打包到最终执行文件中。

  • 将控制权转移给非托管函数。
  •  

     2.2Android插件

    Java同样提供了这样一个扩展机制JNI(Java Native Interface),能够与C/C++互相通信。

    注:

  • 作为知识扩展,提一下Android Java虚拟机。Android的Java虚拟机有2个,最开始是Dalvik,后面Google在Android 4.4系统新增一种应用运行模式ART。ART与Dalvik 之间的主要区别是其具有提前 (AOT) 编译模式。 根据 AOT 概念,设备安装应用时,DEX 字节代码转换仅进行一次。 相比于 Dalvik,这样可实现真正的优势 ,因为 Dalvik 的即时 (JIT) 编译方法需要在每次运行应用时都进行代码转换。下文中用Java虚拟机代指Dalvik/ART。
  •  

    C#/Java都可以和C/C++通信,那么通过编写一个C/C++模块作为桥接,就使得C#与Java通信成为了可能,如下图所示:

    注:C/C++桥接器本身跟Unity3D没有直接关系,不属于Android和Unity3D,图中放在Unity3D中是为了代指libunity.so中实现的桥接器以表示真实的情况。

    C/C++要访问的Java代码,必须要能访问到Java虚拟机,获取虚拟机有2中方法:

  • 在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved),第一个参数会传入JavaVM指针。
  • 在C/C++中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)创建JavaVM指针
  • 所以,我们只需要在编写C/C++桥接器so的时候定义JNI_OnLoad(JavaVM* jvm, void* reserved)方法即可,然后把JavaVM指针保存起来作为上下文使用。

    获取到JavaVM之后,还不能直接拿到JNI函数去获取Java代码,必须通过线程关联的JNIENV指针去获取。所以,作为一个好的开发习惯在每次获取一个线程的JNI相关功能时,先调用AttachCurrentThread();又或者每次通过JavaVM指针获取当前的JNIENV:java_vm->GetEnv((void**)&jni_env, version),一定是已经附加到JavaVM的线程。通过JNIENV可以获取到Java的代码,例如你想在本地代码中访问一个对象的字段(field),你可以像下面这样做:

    类似地,要调用一个方法,你step1.得获得一个类对象的引用obj,step2.是方法methodID。这些ID通常是指向运行时内部数据结构。查找到它们需要些字符串比较,但一旦你实际去执行它们获得字段或者做方法调用是非常快的。step3.调用jni_env->CallVoidMethodV(obj, methodID, args)。

    从上面的示例代码,我们可以看出使用原始的JNI方式去与Android(Java)插件交互是多的繁琐,要自己做太多的事情,并且为了性能需要自己考虑缓存查询到的方法ID,字段ID等等。幸运的是,Unity3D已经为我们封装好了这些,并且考虑了性能优化。Unity3D主要提供了一下2个级别的封装来帮助高效编写代码:

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 【Vue 入门】使用 Vue2 开发一个展示项目列表的应用 - zhangjk

      【Vue 入门】使用 Vue2 开发一个展示项目列表的应用 - zhangjk

      2017-04-30 16:00

    • 在Delphi下使用迅雷APlayer组件进行免注册开发 - Delphi力量

      在Delphi下使用迅雷APlayer组件进行免注册开发 - Delphi力量

      2017-04-28 15:00

    • 随应潮流-基于ABP+Angularjs现代化应用软件开发框架(1)-总体介绍 - 在路在的张

      随应潮流-基于ABP+Angularjs现代化应用软件开发框架(1)-总体介绍 -

      2017-04-22 08:04

    • vue2.0版cnode社区项目搭建及实战开发 - sandisen

      vue2.0版cnode社区项目搭建及实战开发 - sandisen

      2017-04-20 14:00

    网友点评
    t