网上关于 unity 调用 java 接口的文章很多,只要去找,大概率都能解决自己的问题。我根据项目中 unity 调用 java 接口的经验,记录一下实践过程中遇到的问题,以备后续查询,若是能够帮到有需要的人,那就是额外收益了。
首先,描述一下遇到的问题:
刚开始想通过unity调用android插件时,不知道必须按照固定的目录结构组织文件,导致打出的android apk在运行时崩溃,提示找不到so文件,解压apk文件一看,果然没有相应的so文件。
下面列出几篇参考文档:
Unity3D项目Plugins目录组织
特殊文件夹名称
一些java方法需要传入 context 参数,unity 提供了专门的方法获取应用的 context,具体代码如下:
private static AndroidJavaObject m_context = null;
private static AndroidJavaObject Context
{
get
{
if( m_context == null )
{
using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
{
m_context = unityClass.GetStatic( "currentActivity" );
}
}
return m_context;
}
}
官方参考文档:
JAR 插件
Building Plugins for Android
C# 和 java 之间的基础类型可以直接转换,包括 string 类型,但是复杂类型必须通过 AndroidJavaObject 实现。
结构体
java 接口中需要传入结构体参数,该怎么办呢?
// java 类型
public class Student {
public String name;
public int age;
public Sex sex;
public enum Sex {
MAN(0),
WOMEN(1);
private int value = 0;
Sex(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
}
unity C# 结构体
// C# 类型
public struct Student {
public string name;
public int age;
public Sex sex;
}
public enum Sex {
Man = 0,
Woman = 1
}
unity 中首先需要获取类型,根据类型创建 AndroidJavaObject,然后对其成员进行赋值。
Student st;
st.name = "test";
st.age = 28;
st.sex = Sex.Man;
// C# 对象转换为 java 对象
AndroidJavaObject student_object = new AndroidJavaObject("com.ss.example.Student");
student_object.Set("name", st.name);
student_object.Set("age", st.age);
student_object.Set("sex", ConvertType(st.sex));
// C#枚举类型转 java 枚举类型
private static AndroidJavaObject ConvertType(Sex type)
{
AndroidJavaClass typeClass = new AndroidJavaClass("com.ss.example.Student$Sex");
switch (type)
{
case Sex.Man:
{
return typeClass.GetStatic("MAN");
break;
}
case Sex.Woman:
{
return typeClass.GetStatic("WOMEN");
break;
}
default:
{
return typeClass.GetStatic("MAN");
}
}
}
当从 unity 环境调用 java 接口时,如果接口需要传入一个java Student 对象,此时传入 student_object 就可以正常运行了。
unity 调用 java API 有专门的方法,通过 AndroidJavaClass
和 AndroiJavaObject 实现。
调用一个类的静态方法,首先必须能够拿到这个类,然后调用对应的方法即可。
java 接口:
public abstract class Writer
public static synchronized Writer create(Context context, Student st);
public int write(String msg);
}
C# 调用静态方法
// 获取 java 类型
AndroidJavaClass writerClass = new AndroidJavaClass( "com.example.Writer" );
// 调用静态方法
AndroidJavaObject object = writerClass.CallStatic("create", Context, student_object);
CallStatic 方法至少有一个参数,那就是被调静态方法的名字,必须和 java 名字保持一致,返回值通过 CallStatic 尖括号里的类型来指定,如果没有指定就表示不返回任何值。
此处的 context 即为前面获取的android 应用的 context。
C# 调用成员方法
成员方法必须基于对象来调用,因此我们可以基于上面通过调用 “create” 方法创建的对象,来调用其成员方法。
int words = object.Call("write", "story");
unity 使用 java 插件,一方面有 API,另一方面肯定有回调方法,需要在特定事件发生时,通知 unity 更新 UI。回调通过 AndroidJavaProxy 实现。
使用方法如下,java 接口定义:
public interface IEventHandler {
void onWriteDone(Student st);
void onClose(String test);
}
C# 层实现回调方法:
public class PluginCallback : AndroidJavaProxy
{
public PluginCallback() : base("com.example.IEventHandler")
{
}
// 注意:回调中的非基础类型,在 C# 层必须用 AndroidJavaObject 来承接
void onWriteDone(AndroidJavaObject st) {
string name = st.Get("name");
int age = st.Get("age");
}
void onClose(String test) {
}
}
IEventHandler 必须是 Interface 类型,不能是 abstract class 类型。
推荐使用 Loom 插件,可以参考 Unity使用Loom实现多线程
在 unity UI 线程中初始化时,调用 Loom 的初始化方法:
Loom.Initialize();
在需要转线程的地方如下调用即可:
[MonoPInvokeCallback(typeof(OnCloseEventHandler))]
public static void onClose(string test) {
if (_instance.OnCloseEvent != null) {
Loom.QueueOnMainThread(() =>
{
_instance.OnCloseEvent(test);
});
}
}
到这里 unity 调用 java 接口的记录就写完了,后续有更新再补充吧。
参考
官方网站
Unity 插件开发 - Android与iOS
Unity中C#与Android中Java的互相调用遇到的一些问题
Wrapping a native SDK for Unity: our challenges and choices