Unity与Android交互通信系列(3)

在上两篇文章中,我们已经能够通过直接使用Java,或者通过AndroidJavaClass、AndroidJavaObject这两个类实现在Unity端和Android原生端的通信。这已经可以解决很多问题,但这种方式不够模块化,不够优雅。

在实际使用中,将Android端代码编译成aar包供Unity端调用更有利于代码管理、人员分工、降低开发工具之间的耦合,而且目前有大量第三方库是通过aar包的形式提供,本节我们主要演示通过Android Studio生成aar包及在Unity中调用aar包中功能的流程及方法。

在Android Studio中生成aar包流程如下:

(1)创建新Android Studio工程。在Android Studio中,依次选择File →New → New Project,如图1所示。

图1 创建新工程

选择New Project之后会打开新工程创建面板,如图2所示,在面板左侧列表中选择Phone and Tablet,然后在右侧图像列表中选择No Activity、Basic Activity、Empty Activity模板之一,本节选择Empty Activity模板。

图2 新工程创建面板

选择好所使用的工程模板后,点击Next进入工程信息填写面板,如图3所示,其中Package name即为所使用的包名,需要认真填写;Minimum SDK建议选择Android 8.0以上(ARCore不支持Android 7.0以下版本);其余项可根据需要填写,点击Finish按钮完成工程创建。


图3 填写工程信息

工程创建完成后,因为我们希望以模块的形式导出aar包,所以首先创建应用模块。在新创建的工程左侧Project列表面板app目录上鼠标右键,在弹出的级联菜单中依次选择New →Module,如图4所示。


图4 新建Module模块

在打开的新建模块面板中,因为我们希望创建Android类库,在其左侧列表中选择Android Library,然后在右侧面板中填写Module name(模块名)与其它信息,如图5所示,最后点击Finish按钮完成android2unity模块的创建[ 在本示例中,工程名与模块名重名,实际使用中建议根据模块功能命名并避免与工程重名]。


图5 Module模块信息填写面板

Android Studio会自动生成模块的层级结构,依次选择android2unity模块下的java →com.davidwang.android2unity包名,并在其上鼠标右键打开级联菜单,如图6所示。


图6 在模块下新建Java类

在弹出的级联菜单中依次选择New →Java Class创建新类,本节类名命名为example,并编写代码如下:

java 复制代码
//代码片段1
//Java端代码
package com.davidwang.android2unity;

import android.app.Activity;
import android.widget.Toast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class example {
    private Activity _unityActivity;
    private String _unityObject;
private String _unityMethod;
//初始化,注册Unity对象与回调方法
    public void Init(String unityObject,String unityMethod){
        try {
            Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
            Activity activity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);
            _unityActivity = activity;
        }
        catch (ClassNotFoundException e) { }
        catch (IllegalAccessException e) { }
        catch (NoSuchFieldException e) { }
        _unityObject = unityObject;
        _unityMethod = unityMethod;
}
//通过UnitySendMessage方法传播消息
    private boolean callUnity(String args){
        try {
            Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
            Method method =classtype.getMethod("UnitySendMessage", String.class,String.class,String.class);
            method.invoke(classtype,_unityObject,_unityMethod,args);
            return true;
        }
        catch (ClassNotFoundException e) {}
        catch (NoSuchMethodException e) {}
        catch (IllegalAccessException e) {}
        catch (InvocationTargetException e) {}
        return false;
}
//调用Android端本地方法
    public boolean showToast(String content){
        Toast.makeText(_unityActivity,content,Toast.LENGTH_SHORT).show();
        callUnity("Call from android");
        return true;
    }
}

在上述代码中,我们采用UnitySendMessage()消息传播方式回调Unity端的方法,由于UnitySendMessage()方法需要明确的场景中游戏对象、游戏对象脚本中方法名作为参数,为方便Unity中的调用,这里通过Init()方法由Unity端注入这两个参数,从而不需要一一绑定游戏对象与脚本方法名。同时,为避免引入Unity引擎的Classes.jar包,这里采用了反射的方式获取当前Activity。

至此,Android端的模块已完成,在左侧Project列表中选中android2unity模块,然后在Android Studio菜单中依次选择Build →Rebuild Project开始构建模块aar包,如图7所示,构建完成后,可以在android2unity模块下的build →outputs →aar目录中找到生成的aar包。


图7 构建模块aar包

将该aar包文件复制到Unity工程Assets/Plugins/Android目录或其子目录下,完成aar包的导入。为测试其功能,在Unity中新建Android2Unity.cs脚本文件,代码如下:

csharp 复制代码
//代码片段2
//C#端代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Android2Unity : MonoBehaviour
{
    void Start()
    {
        using(AndroidJavaObject jo = new AndroidJavaObject("com.davidwang.android2unity.example"))
        {
            jo.Call("Init",gameObject.name, "AndroidCallback");
            bool success = jo.Call<bool>("showToast", "Content from unity");
            if (true == success)
            {
                Debug.Log("Method executed");
            }
        }
}
//Android端回调的方法
    public void AndroidCallback(string str)
    {
        Debug.Log(str);
    }
}

该脚本逻辑比较简单,首先通过实例化Android端的类,并通过其Init()方法注入回调游戏对象名及脚本方法名。将该脚文件挂载到场景中的任意对象上,连接手机,打包运行,可以看到,Unity端的方法参数可以正确传递到Android端,Android端也可以正确回调Unity端的脚本方法。

由于UnitySendMessage()方法需要遍历场景中的游戏对象,效率并不很高,特别是在频繁调用或者场景比较复杂的情况下可能会导致性能问题,在实际开发中,建议通过AndroidJavaProxy类交互。通过构建模块aar包的形式,有利于代码管理,并且可以方便的整合第三方类库,提供一个整体的模块方案,简化Unity端的调用形式。

通过这种包/库的方式调用就舒服多了。

相关推荐
阿巴斯甜14 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker14 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952715 小时前
Andorid Google 登录接入文档
android
黄林晴16 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android