Unity 插件开发 - Android与iOS

由于工作需要在接入第三方 SDK ,而一些第三方 SDK 没有提供 Unity 版本,就需要自己去包装一层需要调用的接口,之后就可以在 Unity 端统一接入,直接导出可用的对应平台的工程或程序包。
前几天开始封装 iOS 平台的 SDK,至此,已经接入过手机上主流的 Android 和 iOS 两个平台的SDK,这篇文章就是对之前开发插件的一个总结。

概述

Unity 端调用 Android 端的方法,可以使用 AndroidJavaClass, AndroidJavaObject 和 AndroidJavaProxy 这三个类,通过 JNI 对应了 Java 中的类,对象和接口。

Unity 端调用 iOS 端的方法,则相对容易一些,也就是 C# 和 Objective-C 两个语言相互交互,可以注意到这两个都是 C 系语言,所以实际上 Objective-C 写的本地插件会被编译成 DLL 来给 C# 调用,处理好原始类型和引用类型的相互转换就可以了。

而反过来,插件回调至 Unity 端则有两种方法:

Unity 在 Android 和 iOS 平台上都支持调用 UnitySendMessage 方法来向 Unity 传递信息,Unity 端根据传递来的信息执行对应的逻辑。不过使用这一方法会有一些限制,我们先看一下这个方法的签名:void UnitySendMessage("GameObjectName1", "MethodName1", "Message to send");,需要在 Unity 端有一个 GameObject 来接收信息,这个方法不能返回值,只能传递一个字符串,也就导致类型信息在这个过程中是丢失的。

下面详细介绍对应这两个平台的另一种回调方法:

Unity 与 Android

Android 端有一个 bar 方法,接受一个 ICallback 的实例作为参数:

1
2
3
4
5
6
7
8
9
10
11
package com.test.sdk;

public class Foo {
public void bar(ICallback callback) {
if(success) {
callback.onSuccess();
} else {
callback.onFailure("Something wrong");
}
}
}

为了能够给 bar 方法传递一个 ICallback 实例,我们需要在 Unity 端对应定义一个接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ICallback : AndroidJavaProxy
{
//这里的 Action 只是为了方便传递匿名方法
Action onSuccessAction;
Action<string> onFailureAction;

public ICallback(Action onSuccessAction,
Action<string> onFailureAction) :
base("com.test.sdk.ICallback")
//此处传递了对应 Android 端接口的类路径
{
this.onSuccessAction = onSuccessAction;
this.onFailureAction = onFailureAction;
}

//这里的方法名要对应 Android 端接口的方法名
public void onSuccess()
{
if (onSuccess != null)
{
onSuccess.Invoke(UserInfo.FromJavaObject(userInfo));
}
}

public void onFailureAction(string message)
{
if (onFailureAction != null)
{
onFailureAction.Invoke(message);
}
}
}

接下来我们就可以在 Unity 端直接调用 bar 方法了:

1
2
3
4
5
6
public void bar(ICallback callback) {
using (AndroidJavaObject fooObj = new AndroidJavaClass("com.test.sdk.Foo").CallStatic<AndroidJavaObject>("getInstance");)
{
fooObj.Call("bar", callback);
}
}

可以看到以上场景中,我们使用 AndroidJavaProxy 对应实现了接口,使用 AndroidJavaClass 获取到 Java 类对象,通过 CallStatic 方法获取到用 AndroidJavaObject 对应表示的实例对象。

基本上这样就完成 Unity 和 Java 两端交互的功能了,Unity 端包装好之后,使用者是不需要接触到 AndroidJavaObject 这些类的。

Unity 与 iOS

iOS端有一个 bar 方法,定义如下:

1
2
3
typedef void (*BarHandler) (const char* message);

void bar(BarHandler barHandler);

对应的我们需要在 Unity 端这样定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1. 声明 iOS 端的导出方法

[DllImport("__Internal")]
//IntPtr 即表示一个函数指针
static extern void bar(IntPtr barHandler);

//2. 声明回调方法的接口

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void BarHandler(string message);

//3. 实现回调方法

[MonoPInvokeCallback(typeof(BarHandler))]
public static void onBar(string message)
{
Debug.Log("[ReturnValue] " + message);
}

然后在 Unity 端这样调用:

1
2
3
BarHandler handler = new BarHandler(onBar);
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(handler);
bar (functionPointer);

拓展阅读