Unity 项目中一些需要访问安卓操作系统的功能,比如获取电量,wifi 状态等,需要 Unity 启动安卓系统的 BroadcastReceiver
监听状态,并在状态更新后通知到 Unity 界面。这就需要一种 Unity 与 Android 互相调用的机制,直观地看就是 C# 与 Java 互相调用的方法。
有 Unity 与 Android 互相调用需求的项目需要在两个开发环境中同时进行,创建两个工程,这时就涉及到如何将两个工程连接起来,有两种方式来连接:
- Android 工程生成 aar/jar 文件,复制到 Unity 工程中,最终使用 Unity 的 Build 机制生成 apk。
- Unity 工程将所有内容和代码导出为一个 Android gradle 项目,然后使用 Android Studio 打开项目进行开发,最终使用 Android Studio 打包 apk。
对比一下两者的优缺点:
Unity 使用 jar/aar 库 | Unity 导出 gradle 项目 | |
---|---|---|
Unity 与 Android 依赖性 | Unity 只依赖 Android 库文件,分割清晰,需要同步的文件只有库文件 | Android 依赖 Unity 导出的场景数据,需要同步的文件太多 |
开发调试速度 | Android 库文件比较小,调试较快 | Unity 工程较大,同步较慢,调试周期长 |
Build机制 | Unity 内置的 Android Build 机制,类似于 eclipse 编译 Android 项目 | Android Studio gradle |
Build灵活性 | 较差,无法深度定制,库有依赖时需要将全部依赖显式拷贝到 Unity 工程中 | 非常自由,可以使用最新的 Android Build 机制 |
如何打包apk | Unity Build 机制直接打包 | Android Studio 打包 |
本项目使用的是第一种方法,因为这个项目中 Unity 工程特别大,导出 Unity 工程的代价太大。但也遇到了库文件依赖问题,不过由于依赖项不是很多,可以手动解决。以下是解决思路:
- 运行 gradle task
dependencies
,可以在 “Gradle projects” 窗口中目标项目的 help 目录中找到,这个 task 会打印出树形结构的依赖关系。 - 将所有的依赖项单独下载到本地,放到 Unity 工程中。
从这两个步骤可以看出,如果依赖层次比较少、数量比较少,还是可以接受的,但如果有大量深层的依赖就会变得特别麻烦。
Unity 调用 Android
Unity官方文档说明需要通过Plugin的方式调用Java代码,但实际上不需要引入任何Plugin就可以调用Java代码。只是一般情况下需要调用的都是封装好的库,这时才需要将 jar 或者 aar 放到 Unity 项目中,然后通过 C# 来访问其中的内容。
jar 或者 aar 文件可以放在Unity任意目录下,为了方便管理,都放在了 Assets/Plugins/Android
目录下。
C# 调用 Java 方法,获取 Java 字段
C# 调用 Java 的底层原理是使用JNI调用,Unity已经提供了很方便的接口:
- 创建对象:C#中使用
AndroidJavaObject
类封装 Java 对象,new 一个AndroidJavaObject
对象相当于调用对应的 Java 对象的构造函数。借助 C# 可变参数列表,可以给 Java 对象的构造函数传递任意数量的参数。
// 第一个参数是 Java 类的完整包名,剩下的其他参数会传递给构造方法。
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string");
- 调用对象方法:使用
AndroidJavaObject
类的 Call 方法,有泛型与非泛型的两个版本。
// 泛型版本,目的是指定返回值的类型
int hash = jo.Call<int>("hashCode");
// 非泛型版本,处理返回值是void的情况。
jo.Call("aMethodReturnVoid"); // String中没有返回void的简单方法。。。
- 获取类,主要用于获取静态字段或调用静态方法,常用来获取 UnityPlayer。
// 传入类的完整包名
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
- 获取静态字段,只有泛型版本,因为不会有void类型的字段。。。
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
设置字段、获取对象字段、调用静态方法的代码类似,略。
类型映射
调用 Java 方法时,直接将 C# 变量/常量 传递给 Java 方法,会自动处理 C# 类型到 Java 类型的转换。通过 C# 泛型,可以指定 Java 方法的返回值类型,也就是将 Java 类型转换为了 C# 类型。C# 类型与 Java 类型是可以自动转换的,规则如下:
Java 类型 | C# 类型 |
---|---|
基本类型,比如 int , boolean |
对应的值类型 int , bool |
String |
string |
数组类型 | 数组类型 (不能是多维数组) |
其他继承自 Object 的类型 |
AndroidJavaObject |
Android 调用 Unity
从 Android 端并不能直接调用 Unity 脚本,而是通过消息发送或者接口回调的方式。
消息发送方式
消息发送是一个非常简单的调用机制,建立在一个发消息的接口之上:
// objectName: Unity 对象的名称
// methodName: Unity 对象绑定的脚本方法名
// message: 自定义消息
UnityPlayer.UnitySendMessage(String objectName, String methodName