下面是一份Unity Android Plugin开发指南,涵盖你关心的所有要点,包括如何在Unity工程中集成Android/Java库、双向调用、接口设计、混合项目构建、调试方法以及跨虚拟机调用的原理和实践。
Unity Android Plugin开发指南
一、如何在Unity项目中使用Android Plugin
1. 插件文件的准备
- Java代码:编写Android原生功能(如支付、分享、推送等)相关的Java代码。
- 打包为AAR/JAR:用Android Studio将Java代码打包成
.aar
或.jar
文件。 - JNI接口:如需C/C++,可用NDK编写so库。
2. 插件文件的导入
- 将
.aar
、.jar
、.so
文件放入Unity工程的Assets/Plugins/Android/
目录下。 - 若有
AndroidManifest.xml
,也放在此目录,Unity会自动合并。
3. 插件的自动集成
- Unity构建Android包时,会自动将
Plugins/Android
下的内容合并进最终APK/AAB。
二、Unity-Android相互调用
1. Unity调用Android(C#→Java)
基本用法
#if UNITY_ANDROID && !UNITY_EDITOR
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject plugin = new AndroidJavaObject("com.yourcompany.plugin.YourPluginClass");
plugin.Call("yourJavaMethod", "参数1", 123);
#endif
传递参数和返回值
- 支持基本类型、String、数组、Java对象等。
- 返回值用
Call<T>()
泛型方法获取。
2. Android调用Unity(Java→C#)
步骤
- 在Unity C#中定义一个GameObject和方法:
public void OnPluginCallback(string message) { Debug.Log("收到Java回调: " + message); }
- 在Java代码中调用Unity方法:
UnityPlayer.UnitySendMessage("GameObjectName", "OnPluginCallback", "Hello from Java");
GameObjectName
为场景中对象名,OnPluginCallback
为C#方法名。
注意事项
- UnitySendMessage只能传递字符串参数。
- 回调方法需为public,且参数为string。
三、Unity接口设计的最佳实践
- 单例管理:用C#单例类统一管理所有Android交互,便于维护。
- 异步回调:Java侧操作完成后用UnitySendMessage回调C#,避免阻塞主线程。
- 参数封装:复杂参数用JSON序列化传递,C#和Java都易于解析。
- 错误处理:Java侧catch异常后回调Unity,C#侧做好异常兜底。
- 接口文档:约定好C#和Java的接口、参数、回调格式,便于多人协作。
四、如何构建Unity-Android混合项目
- Android Studio开发插件:用Android Studio开发和调试Java代码,打包成AAR/JAR。
- Unity集成插件:将AAR/JAR放入
Plugins/Android
,如需资源(res/、assets/)也一并打包。 - Gradle自定义:如需自定义依赖、Manifest等,可启用
Custom Gradle Template
(mainTemplate.gradle)。 - 多渠道/多包名:可用Gradle脚本或第三方工具自动化多渠道打包。
- 资源合并冲突:多个插件有资源/Manifest冲突时,需手动合并。
五、如何调试Unity和Android代码
1. 日志调试
- C#用
Debug.Log
,Java用Log.d
。 - 用
adb logcat
查看Android日志。
2. 断点调试
- Java代码可用Android Studio直接断点调试。
- Unity C#可用Visual Studio/VSCode调试。
- 可用
Attach to Process
连接到Unity进程。
3. 插件热更新
- 修改Java代码后,重新打包AAR/JAR并替换到Unity工程,无需重启Unity。
4. 日志桥接
- 可将Java日志通过UnitySendMessage回传到C#,便于统一查看。
六、跨虚拟机调用的实现(JNI原理)
1. Unity的C#和Android的Java运行在不同虚拟机
- Unity C#运行在Mono/IL2CPP虚拟机。
- Android Java运行在ART/Dalvik虚拟机。
2. Unity通过JNI桥接
- Unity内置
AndroidJavaObject
、AndroidJavaClass
等C#类,底层通过JNI与Java通信。 - C#调用Java时,Unity自动生成JNI调用代码。
- Java调用C#时,需通过UnityPlayer.UnitySendMessage,底层通过JNI回调C#。
3. 性能与注意事项
- JNI调用有一定性能开销,频繁调用需谨慎。
- 复杂数据建议用JSON等序列化方式传递。
七、实用代码模板
1. C#单例管理Android插件
public class AndroidPluginManager : MonoBehaviour
{
private static AndroidPluginManager _instance;
public static AndroidPluginManager Instance => _instance;
private AndroidJavaObject _plugin;
void Awake()
{
_instance = this;
#if UNITY_ANDROID && !UNITY_EDITOR
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
_plugin = new AndroidJavaObject("com.yourcompany.plugin.YourPluginClass", activity);
}
#endif
}
public void CallJavaMethod(string param)
{
#if UNITY_ANDROID && !UNITY_EDITOR
_plugin.Call("yourJavaMethod", param);
#endif
}
public void OnPluginCallback(string message)
{
Debug.Log("Java回调: " + message);
}
}
2. Java侧回调Unity
import com.unity3d.player.UnityPlayer;
public void sendCallbackToUnity(String message) {
UnityPlayer.UnitySendMessage("AndroidPluginManager", "OnPluginCallback", message);
}
八、常见问题与建议
- 包名/类名要一致,否则C#找不到Java类。
- UnitySendMessage只能传字符串,复杂数据用JSON。
- AndroidManifest冲突,需手动合并。
- 多线程问题,Java侧UI操作需在主线程(runOnUiThread)。
- 权限申请,Android 6.0+需动态申请权限。
九、参考资料
下面继续深入讲解Unity Android Plugin开发的进阶内容,包括AAR/JAR打包流程、复杂数据交互、原生UI嵌入、NDK调用、常见问题排查等。
十、AAR/JAR打包与集成流程
1. 用Android Studio创建插件工程
-
新建Android Library项目
- 在Android Studio选择
File > New > New Module > Android Library
,填写包名、类名等。
- 在Android Studio选择
-
编写Java代码
- 例如:
package com.yourcompany.plugin; public class MyPlugin { public String getMessage(String name) { return "Hello, " + name; } }
- 例如:
-
打包AAR/JAR
- Build > Make Module ‘yourlibrary’
- 输出文件在
yourlibrary/build/outputs/aar/yourlibrary-release.aar
-
拷贝到Unity
- 将AAR/JAR文件放入
Assets/Plugins/Android/
目录。
- 将AAR/JAR文件放入
2. 集成第三方SDK(如微信、支付宝等)
- 通常SDK提供AAR/JAR和文档,按文档放入
Plugins/Android
,如有依赖需一并拷贝。 - 检查
AndroidManifest.xml
,如有冲突需合并。
十一、复杂数据交互(如对象、数组、JSON)
1. C#传递复杂数据到Java
- 推荐将复杂对象序列化为JSON字符串传递。
- C#端:
var data = new { id = 1, name = "test" }; string json = JsonUtility.ToJson(data); plugin.Call("receiveJson", json);
- Java端:
public void receiveJson(String json) { JSONObject obj = new JSONObject(json); // 解析数据 }
2. Java回调复杂数据到Unity
- Java端将对象转为JSON字符串,通过
UnitySendMessage
回调。 - C#端用
JsonUtility
或Newtonsoft.Json
解析。
十二、原生UI嵌入与交互
1. 显示原生Dialog/Toast
- Java端实现弹窗方法,C#调用即可。
- 例如:
public void showToast(final String msg) { activity.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show(); } }); }
2. 原生Activity/Fragment嵌入
- 可启动自定义Activity,或用Fragment嵌入Unity界面。
- 复杂UI建议用Activity/Fragment,Unity与原生通过Intent、Broadcast、文件等通信。
3. Unity与原生UI通信
- 原生UI操作完成后用
UnitySendMessage
通知Unity。 - Unity可通过C#调用Java方法控制原生UI。
十三、NDK(C/C++)调用
1. 编写C/C++代码
- 用Android Studio或CMake编写so库,导出JNI接口。
2. Java调用C/C++(JNI)
- Java端用
System.loadLibrary
加载so库,声明native方法。
3. Unity调用C/C++
- Unity可通过C#的
[DllImport]
直接调用so库(需ABI匹配)。 - 或通过Java中转,C#→Java→JNI→C/C++。
4. so库集成
- 将so库放入
Assets/Plugins/Android/libs/armeabi-v7a/
等对应ABI目录。
十四、常见问题排查
1. 插件找不到类/方法
- 检查包名、类名、方法名是否一致。
- 检查AAR/JAR是否正确放入
Plugins/Android
。
2. AndroidManifest冲突
- 多个插件有
<application>
、<activity>
等重复声明时,需手动合并Manifest。
3. 资源冲突
- 多个AAR有同名资源时,需合并或重命名。
4. 权限问题
- Android 6.0+需动态申请权限,C#用
Permission.RequestUserPermission
。
5. 64位兼容
- so库需同时提供
armeabi-v7a
和arm64-v8a
版本。
十五、进阶技巧与建议
- 多渠道打包:用Gradle脚本或第三方工具自动化多渠道包名、签名、资源替换。
- 热更新:插件功能可通过AssetBundle、Lua、ILRuntime等实现热更新。
- 混淆与安全:发布前用Proguard混淆Java代码,保护核心逻辑。
- 性能优化:JNI调用有开销,频繁调用建议合并为批量操作。
- 文档与注释:接口、参数、回调格式要有详细文档,便于团队协作。
十六、参考项目结构
Assets/
Plugins/
Android/
myplugin.aar
myplugin.jar
AndroidManifest.xml
libs/
armeabi-v7a/
libnative.so
arm64-v8a/
libnative.so
十七、常用调试命令
- 查看日志:
adb logcat | findstr Unity
- 安装APK:
adb install -r your.apk
- 查看设备:
adb devices
- 远程调试:Android Studio Attach Debugger,Unity Profiler远程连接
十八、官方文档与社区
好的,下面继续补充更具体的实战案例和进阶细节,包括AAR/JAR打包详细步骤、NDK调用示例、原生UI与Unity混合界面Demo、复杂数据交互代码等内容。
十九、AAR/JAR打包详细步骤(Android Studio)
1. 新建Android Library项目
- 打开Android Studio,选择
File > New > New Module > Android Library
。 - 填写Module name(如:myplugin),Package name(如:com.example.myplugin)。
2. 编写Java代码
package com.example.myplugin;
import android.app.Activity;
import android.widget.Toast;
public class MyPlugin {
private Activity activity;
public MyPlugin(Activity activity) {
this.activity = activity;
}
public String getMessage(String name) {
return "Hello, " + name;
}
public void showToast(final String msg) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
}
});
}
}
3. 打包AAR
- 在Android Studio菜单栏选择
Build > Make Module 'myplugin'
。 - 打包完成后,AAR文件在
myplugin/build/outputs/aar/myplugin-release.aar
。
4. 集成到Unity
- 将AAR文件拷贝到Unity项目的
Assets/Plugins/Android/
目录下。 - 若有依赖(如support库),也需一并拷贝。
二十、Unity调用AAR插件示例
1. C#代码调用
#if UNITY_ANDROID && !UNITY_EDITOR
public class MyPluginWrapper
{
private AndroidJavaObject plugin;
public MyPluginWrapper()
{
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
plugin = new AndroidJavaObject("com.example.myplugin.MyPlugin", activity);
}
}
public string GetMessage(string name)
{
return plugin.Call<string>("getMessage", name);
}
public void ShowToast(string msg)
{
plugin.Call("showToast", msg);
}
}
#endif
2. 使用
MyPluginWrapper plugin = new MyPluginWrapper();
string msg = plugin.GetMessage("Unity");
plugin.ShowToast(msg);
二十一、NDK调用(C/C++与Unity交互)
1. 编写C/C++代码(JNI接口)
// native-lib.cpp
#include <jni.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myplugin_MyPlugin_nativeHello(JNIEnv *env, jobject /* this */) {
return env->NewStringUTF("Hello from C++");
}
2. Java调用C/C++
public native String nativeHello();
static {
System.loadLibrary("native-lib");
}
3. Unity调用C/C++
- 方式一:通过Java中转(C#→Java→JNI→C/C++)。
- 方式二:直接用C#的
[DllImport]
调用so库(需ABI匹配)。
[DllImport("native-lib")]
private static extern IntPtr Java_com_example_myplugin_MyPlugin_nativeHello(IntPtr env, IntPtr thiz);
- so库放到
Assets/Plugins/Android/libs/armeabi-v7a/
和arm64-v8a/
。
二十二、原生UI与Unity混合界面Demo
1. 启动原生Activity
- Java端新建Activity(如:MyNativeActivity),在Manifest注册。
- C#调用Java方法启动Activity。
// Java
public void startNativeActivity() {
Intent intent = new Intent(activity, MyNativeActivity.class);
activity.startActivity(intent);
}
// C#
plugin.Call("startNativeActivity");
2. 原生Activity与Unity通信
- 在MyNativeActivity中,操作完成后用
UnityPlayer.UnitySendMessage
回调Unity。
UnityPlayer.UnitySendMessage("GameObjectName", "OnNativeResult", "result data");
二十三、复杂数据交互代码(JSON)
1. C#传递JSON到Java
[System.Serializable]
public class UserData
{
public int id;
public string name;
}
UserData data = new UserData { id = 1, name = "Unity" };
string json = JsonUtility.ToJson(data);
plugin.Call("receiveJson", json);
2. Java解析JSON
public void receiveJson(String json) {
try {
JSONObject obj = new JSONObject(json);
int id = obj.getInt("id");
String name = obj.getString("name");
// 处理数据
} catch (JSONException e) {
e.printStackTrace();
}
}
3. Java回调JSON到Unity
JSONObject obj = new JSONObject();
obj.put("result", "ok");
obj.put("score", 100);
UnityPlayer.UnitySendMessage("GameObjectName", "OnPluginCallback", obj.toString());
public void OnPluginCallback(string json)
{
Debug.Log("收到回调: " + json);
// 解析json
}
二十四、常见问题与调试技巧
- AAR/JAR未生效
- 检查是否放在
Assets/Plugins/Android/
,包名、类名是否一致。
- 检查是否放在
- Manifest冲突
- 手动合并Manifest,保留必要的权限、Activity、Service等声明。
- so库未加载
- 检查ABI目录,确保与Unity Player设置一致(armeabi-v7a/arm64-v8a)。
- UnitySendMessage无效
- 检查GameObject名称、方法名、参数类型(必须是string)。
- 调试日志
- 用
adb logcat | findstr Unity
(Windows)或adb logcat | grep Unity
(Mac/Linux)过滤Unity日志。
- 用
- 断点调试
- Android Studio可Attach到Unity进程,调试Java代码。
二十五、进阶建议
- 接口文档:建议用Markdown或Swagger记录C#与Java的接口、参数、回调格式。
- 自动化打包:用Gradle脚本、Jenkins等实现自动化构建和多渠道打包。
- 代码热更新:如需热更,建议用ILRuntime、XLua等方案,插件接口保持稳定。
- 安全性:发布前用Proguard混淆Java代码,防止反编译。
- 团队协作:插件开发与Unity主工程分离,接口通过文档和Demo验证。