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

  在上一篇文章中,我们介绍了Unity和Android交互通信的原理及在Unity中直接调用Java代码的方式,但没有给出代码示例,下面通过实际例子演示上篇文章中AndroidJavaClass、AndroidJavaObject两个类的基本用法,由于交互通信涉及到两端,我们先使用Android Studio创建Unity2Java类,Java代码如下:

//代码片断1
package com.example.davidwang;public class Unity2Java {public static void StaticPrint(String str){System.out.println(str);}public static int StaticAdd(int a,int b){return a+b;}public void DynamicPrint(String str){System.out.println(str);}public int DynamicAdd(int a,int b){return a+b;}
}

  找到该类所在的.java文件,并将该文件复制到Unity工程Assets/Plugins/Android目录或其子目录下,然后在Unity工程窗口(Project窗口)中选中该java文件,在属性窗口(Inspector窗口)中查看其导入设置,确保Android平台被选择,如图1所示。
在这里插入图片描述

图1 在Java文件导入设置中勾选Android多选框



  为在Unity端调用Java代码,通过Unity创建Android2Unity.cs脚本文件,代码如下:

//代码片断2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Android2Unity : MonoBehaviour
{void Start(){using (AndroidJavaClass Unity2JavaClass = new AndroidJavaClass("com.example.davidwang.Unity2Java")){Unity2JavaClass.CallStatic("StaticPrint", "Hello World from Android static method ");int result1 = Unity2JavaClass.CallStatic<int>("StaticAdd", 1, 1);Debug.Log("结果1:" + result1);Unity2JavaClass.Call("DynamicPrint", "Hello World from Android dynamic method ");int result2 = Unity2JavaClass.Call<int>("DynamicAdd", 1, 2);Debug.Log("结果2:" + result2);}using (AndroidJavaObject Unity2JavaObject = new AndroidJavaObject("com.example.davidwang.Unity2Java")){Unity2JavaObject.CallStatic("StaticPrint", "Hello World from Android static method ");int result3 = Unity2JavaObject.CallStatic<int>("StaticAdd", 1, 3);Debug.Log("结果3:" + result3);Unity2JavaObject.Call("DynamicPrint", "Hello World from Android dynamic method ");int result4 = Unity2JavaObject.Call<int>("DynamicAdd", 1, 4);Debug.Log("结果4:" + result4);}}
}

  在Unity中,将Android2Unity脚本挂载到场景中的任意对象上,连接手机,打包运行[ Java代码编译后运行于dalvik / art虚拟机,其控制台输出不能输出到Unity Debug窗口,所以只能通过真机运行,Logcat查看输出。],输出结果如下:

行号  类型       输出信息     
1   System.out Hello World from Android static method 
2   Unity       结果12
3   Unity  
4   Unity       结果20
5   System.out Hello World from Android static method 
6   Unity       结果34
7   System.out Hello World from Android dynamic method 
8   Unity       结果45

  上述代码首先演示了Java端无返回值、有返回值方法的调用,通过泛型方法定义返回值类型获取Java端方法执行结果,由于数据类型的不同,Java端与C#端交互支持的类型有string、int、float、bool、AndroidJavaObject共5类(也可以返回这些数据类型的数组);其次演示了静态方法和实例方法的调用,类静态方法使用带static后辍的方法访问,而对象实例方法则使用不带Static后辍的方法调用;再次演示了AndroidJavaClass和AndroidJavaObject类使用上的区别,通过输出结果,可以看到,AndroidJavaClass调用对象实例方法既不报错,也不执行;AndroidJavaObject类调用类静态方法可以正常执行[ Java语言支持实例对象调用类静态方法或者获取类静态属性,这与C#语言不同。],虽然我们使用时都使用了new关键字,但AndroidJavaClass类不会生成实例对象,而AndroidJavaObject类会实例化对象。

  类与实例的属性获取/设置与上述方法使用基本一致,通过这种方式,就可以直接在C#代码中调用Java端的原生类,代码如下[ 为了简化排版,后续Java代码与C#代码将放置于同一个代码片断中,并使用注释进行说明。]:

//代码片断3
//Java端代码
package com.example.davidwang;
import android.app.Activity;
import android.widget.Toast;public class Unity2Java {public boolean ShowToast(Activity activity, String str){Toast.makeText(activity,str,Toast.LENGTH_SHORT).show();return true;}
}//C#端代码
//获取设备UUID
private string GetAndroidID()
{string androidID = "NONE";
#if UNITY_ANDROID && !UNITY_EDITORusing (AndroidJavaObject contentResolver = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity").Call<AndroidJavaObject>("getContentResolver")){using (AndroidJavaClass secure = new AndroidJavaClass("android.provider.Settings$Secure")){androidID = secure.CallStatic<string>("getString", contentResolver, "android_id");}}
#endifreturn androidID;
}//调用Andriod端Toast
private void ShowToast()
{if (Application.platform == RuntimePlatform.Android)using (AndroidJavaObject Unity2JavaObject = new AndroidJavaObject("com.example.davidwang.Unity2Java")){using (AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity")){bool isSuccess = Unity2JavaObject.Call<bool>("ShowToast", activity, "From Unity");Debug.Log("ShowToast Status :" + isSuccess);}}
}

  在上述代码片断3中,我们演示了两种直接调用Android端原生类的方式,第一种方法是通过直接获取Android端的原生类,调用其静态方法;第二种是通过调用自定义的Java类间接调用Android原生类方法。同时,由于C#代码运行平台不确定,为确保代码兼容多平台,我们也使用了两种判断代码执行平台的方法,第一种使用预编译指令区分平台,另一种通过Application类直接判断当前运行平台,这也是在多平台开发中经常使用的技巧。

  除此之外,代码还演示了获取当前活动Activity的方法,即通过com.unity3d.player.UnityPlayer类获取当前Activity,Android很多类都需要传递活动的Activity或者Context上下文对象,通过这种方式获取当前Activity是一种常用方法。

  使用AndroidJavaClass和AndroidJavaObject类直接调用Java代码或Android原生类非常方便,但只能是单向由C#调用Java代码,Java没办法反向调用C#代码,实现反向调用则必须使用AndroidJavaProxy类,正如其名,这是个代理类,负责在Java和C#代码之间桥接,后文我们还会详细介绍该类。

  如前文所述,Java端与C#端交互支持的类型有string、int、float、bool、AndroidJavaObject共5类,AndroidJavaObject可以表示所有对象类型,因此,除string、int、float、bool 4种基本类型,其余对象都可由AndroidJavaObject表达。C#端调用Java端方法千差万别,但Java端调用C#端就以上5类(包括数组则共10类),因此我们可以编写一个通用的框架,因为结构稍微有点复杂,涉及到C#端AndroidCallbackManager.cs、AndoridCallbackInterface.cs两个脚本文件,Java端CallUnityInterface.java、Java2Unity.java两个代码文件。

  其中,AndroidCallbackManager.cs文件代码如下:

//代码片断4  
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AndroidCallbackManager : MonoBehaviour
{void Start(){using (AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity")){using (AndroidJavaObject appController = new AndroidJavaObject("com.example.davidwang.Java2Unity")){AndoridCallbackInterface callback = new AndoridCallbackInterface("com.example.davidwang.CallUnityInterface");callback.stringCallBack = StringProcess;callback.intCallBack = IntProcess;callback.floatArrayCallBack = FloatArrayProcess;appController.Call("Init", activity, callback);Debug.Log("In Start");}}}public void StringProcess(string str){Debug.Log("string callback :"+str);}public void IntProcess(int value){Debug.Log("int callback :" + value);}public void FloatArrayProcess(float[] arr){foreach (var value in arr)Debug.Log("float in arr:" + value);}
}

  在代码片断4中,AndroidCallbackManager类是Unity端的使用类,是调用入口,其首先获取到当前Activity,实例化Java端的Java2Unity类,并且同时实例化了一个C#端的AndoridCallbackInterface类。然后设置AndoridCallbackInterface类的回调方法之后调用了Java端的初始化方法。

  AndoridCallbackInterface.cs文件代码如下:

//代码片断5 
using System;
using UnityEngine;
public class AndoridCallbackInterface : AndroidJavaProxy
{public Action<AndroidJavaObject> javaObjectCallBack;public Action<bool> boolCallBack;public Action<string> stringCallBack; public Action<int> intCallBack;public Action<float> floatCallBack;public Action<float[]> floatArrayCallBack;   //构造方法public AndoridCallbackInterface(string interfaceName) : base(interfaceName){}public void JavaObjectCallBack(AndroidJavaObject _data){if (javaObjectCallBack != null)javaObjectCallBack(_data);}public void BoolCallBack(bool _data){if (boolCallBack != null)boolCallBack(_data);}public void StringCallBack(string _data){if (stringCallBack != null)stringCallBack(_data);}public void IntCallBack(int _data){if (intCallBack != null)intCallBack(_data);}public void FloatCallBack(float _data){if (floatCallBack != null)floatCallBack(_data);}public void FloatArrayCallBack(float[] _data){if (floatArrayCallBack != null)floatArrayCallBack(_data);}
}

  在代码片断5中,AndoridCallbackInterface类继承自AndroidJavaProxy类,定义了回调方法,如前文所述,Java端与C#端交互支持的类型共有5种,这里为了通用将这5种回调方法都进行了演示(还包括一个Float数组的演示方法)。该类构造方法很重要,这里需要通过AndroidJavaProxy类将C#端实例与Java端的CallUnityInterface接口对应起来[ 可以理解为建立了一个从Java端到C#端AndoridCallbackInterface类实例的指针,这样Java端就可以通过这个指针访问到C#端的实例。],这样Java端就可以调用C#端实例的方法。

  CallUnityInterface.java文件代码如下:

//代码片断6  
package com.example.davidwang;public interface CallUnityInterface {public void JavaObjectCallBack(Object _data);public void BoolCallBack(boolean _data);public void StringCallBack(String _data);public void IntCallBack(int _data);public void FloatCallBack(float _data);public void FloatArrayCallBack(float[] _data);
}

  在代码片断6中,CallUnityInterface接口方法签名与AndoridCallbackInterface类中对应方法签名需要完全一致,这样才能确保正确相互调用。

  CallUnityInterface.java文件代码如下:

//代码片断7 
package com.example.davidwang;import android.content.Context;
public class Java2Unity {private Context context = null;                //上下文对象private CallUnityInterface callback = null;  //缓存回调public  void  Init(Context context , CallUnityInterface callback){this.context = context;this.callback = callback;try {java.lang.Thread.sleep(5000);Run();}catch (InterruptedException e){e.printStackTrace();}}private  void Run(){int i = 100;String str = "From Java";float[] floatArr = {1.01f,1.02f,1.03f};this.callback.IntCallBack(i);this.callback.StringCallBack(str);this.callback.FloatArrayCallBack(floatArr);}
}

  在代码片断7中,Java2Unity类首先通过Init()方法获取到当前上下文对象[ 在本示意中,上下文对象并没有使用,但很多时间都需要上下文对象或者当前Activity。]和回调实例,通过回调接口即可以调用C#端的方法,这里通过延时5秒触发回调方法。

  在这个通用框架中,这4个类的相互关系如图2所示。
在这里插入图片描述

图2 Java端与C#端相互调用关系示意图
  在Unity中,将AndroidCallbackManager脚本挂载到场景中的任意对象上,连接手机,打包运行[ Java代码编译后运行于dalvik / art虚拟机,其控制台输出不能输出到Unity Debug窗口,所以只能通过真机运行,Logcat查看输出。],输出结果如下:
行号 类型       输出信息     
1   Unity     int callback :100
2   Unity     string callback :From Java
3   Unity     float in arr:1.01
4   Unity     float in arr:1.02
5   Unity     float in arr:1.03
6   Unity     In Start

  通过结果看到,Java端正确的向C#端回调了相应方法并且参数传递无误。而且也可以看到,这种调用是同步的,会阻塞当前线程。


提示

         在开发中,通常我们都是在Android Studio中进行Java代码编写,然后将代码复制到Assets/Plugins/Android目录中供Unity使用,如果改动了Android Studio中的Java代码,则需要再次复盖Unity中的对应文件,而且同时存在两份一样的文件,文件内容同步维护会是非常大的问题,这时我们可以通过创建文件/文件夹链接确保只有一份文件。文件夹链接类似于Windows操作系统中的快捷方式,但可供第三方软件使用。

         Windows下创建方式:mklink /D    LINK_PATH    SOURCE_PATH

         Linux下创建方式:ln -s   SOURCE_PATH    LINK_PATH

         其中参数LINK_PATH为链接的路径,这个路径无真实文件夹或者文件;SOURCE_PATH为源文件/文件夹路径,即真实的文件路径。通过创建文件/文件夹链接,就可以只在Assets/Plugins/Android目录下保留一份代码文件,Android Studio通过链接连接到相应的文件或文件夹,从而可以保持文件同步,无需进行复制代码文件的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/232581.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

net6使用StackExchangeRedis实现分布式缓存

上一篇讲解了Redis的搭建及ServiceStack.Redis 与 StackExchange.Reids 的区别https://blog.csdn.net/qq_39569480/article/details/105249607 这篇文章遗我们来说下使用Microsoft.Extensions.Caching.StackExchangeRedis来对redis进行操作及帮助类。 首先在windows上安装red…

【Java JVM】运行时数据区

JVM 在执行 Java 程序的过程中会把它管理的内存分为若干个不同的数据区域, 这些区域有着各自的用途。 根据《Java虚拟机规范》中规定, JVM 所管理的内存大致包括以下几个运行时数据区域, 如图所示: 这个运行时数据区被分为了 5 大块 方法区 (Method Area)堆 (Heap)虚拟机栈 (V…

Ubuntu20.04.2 Mate 安装后基本初始设置要点笔记

序言&#xff1a; 有几款Linux比较稳定而且LTS长期支持&#xff0c;窗口也比较干净有特色&#xff0c;CentOS、Ubuntu、Debian、Mint 都是挺不错的OS&#xff0c;因为LTS&#xff0c;所以不像Rolling版那样改动频发&#xff0c;为长期应用提供了比较好的保障。下面是 Ubuntu20…

从Gitee克隆项目、启动方法

从gitee克隆VUE项目到本地后&#xff0c;不能直接运行&#xff0c;需要进行npm install安装node_modules文件夹里面的内容&#xff0c;因为在git上传的时候&#xff0c;一般都会过滤到node_modules中的依赖文件。 安装依赖以后&#xff0c;启动通过npm run serve启动项目出错。…

linux xxd命令(将文件或标准输入转换为hex(十六进制)和ASCII(美国信息交换标准代码)表示,或者从hex dump(十六进制转储)反向到二进制)

文章目录 Linux xxd命令安装xxd基本使用方法创建hex dump从hex dump恢复到二进制 命令选项疑难技术点解析在脚本中使用xxd从hex dump恢复数据 总结 Linux xxd命令 xxd是一个在Linux和UNIX系统中常用的工具&#xff0c;主要用于将文件或标准输入转换为hex&#xff08;十六进制&…

Python框架篇(6):FastApi-配置管理

提示: 微信搜索【猿码记】回复 【fastapi】即可获取源码信息~ 在这一篇文章中,对fastapi框架和pydantic进行了升级&#xff0c;然后就是各种不兼容&#xff0c;以后再也不敢轻易升级.... pydantic&#xff1a;从 1.10.11升级到 2.5.2&#xff0c;这里有坑&#xff0c;里面有很多…

蓝凌OA getLoginSessionId.html 信息泄露漏洞

文章目录 产品简介漏洞概述指纹识别漏洞利用修复建议 产品简介 蓝凌核心产品EKP平台定位为新一代数字化生态OA平台&#xff0c;数字化向纵深发展&#xff0c;正加速构建产业互联网&#xff0c;对企业协作能力提出更高要求&#xff0c;蓝凌新一代生态型OA平台能够支撑办公数字化…

SpringCloud微服务 【实用篇】| Docker镜像、容器、数据卷操作

目录 一&#xff1a;Docker基本操作 1. 镜像操作 镜像相关命令 2. 容器操作 容器相关命令 3. 数据卷&#xff08;容器数据管理&#xff09; 数据卷 操作数据卷 挂载数据卷 挂载的方式区别 前些天突然发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0…

【数据可视化】Pyecharts的实际使用

Pyecharts的实际使用 前言正文环境分析Prometheus数据处理Gauge图Bar图-横向如何整合进Flask中 附录 前言 一个多月前参加公司的一个产品会的时候&#xff0c;有和同事聊到日常巡检报表的一些东西&#xff0c;现在虽然项目上搭建的有监控平台、数据稽核平台、调度平台等业务系…

JavaWeb 学生信息管理系统

介绍 ServletMysqlJdbcjQuery 实现学生信息管理系统 学生 班级 教师 系统设置 登陆 软件架构 软件架构说明 基于ServletMysqlJdbcjQuery 实现学生信息的增删改查功能 文件目录声明 src/dao 数据库的增删改查功能src/filter 网页的过滤拦截功能src/model 登陆的实体对象信息…

Gazebo11更新安装

ROS Melodic版本安装的是Gazebo9&#xff0c;Gazebo 最新版本是11 dpkg -l | grep gazebo 出现的是 gazebo9 相关的插件&#xff0c;需要卸载全部插件。 sudo apt-get remove gazebo9 gazebo9-common gazebo9-plugin-base libgazebo9:amd64 libgazebo9-dev:amd64 ros-melodic…

从账户取款和存款的操作

public class Account {private double balance;public Account(double balance){super();this.balancebalance;}public Account(){super();}public void withdraw(double money) throws NotFullBalanceException{//取款if(money<balance){balance - money;}else{throw new …

flink 读取 apache paimon表,查看source的延迟时间 消费堆积情况

paimon source查看消费的数据延迟了多久 如果没有延迟 则显示0 官方文档 Metrics | Apache Paimon

Ring Co-XOR encryption based reversible data hiding for 3D mesh model

期刊&#xff1a;Signal Processing 作者&#xff1a;Lingfeng Qu et al. -- 摘要&#xff1a; 加密域可逆数据隐藏被广泛应用于云存储数字媒体的内容安全、隐私保护和便捷管理。然而&#xff0c;RDH-ED技术在三维网格模型载体中的应用研究仍处于起步阶段。为解决现有针对三…

美化Pandas数据图表

二、数据条显示 Excel条件格式里&#xff0c;有一个数据条显示方式&#xff0c;用以可视化表达数据大小。 Pandas Style方法中也有数据条的表达形式&#xff0c;用df.style.bar来实现。 还是用前面人口数据的例子&#xff0c;我们来看下如何操作数据条。 import pandas as …

初探 Reactor、Proactor 线程模型与 BIO、AIO、NIO

1 前言 工作中或者是技术上经常会遇到 I/O 、线程模型相关的问题&#xff0c;以及同步、异步、阻塞、非阻塞等各种基础问题&#xff0c;之前上学时候的概念认知总是模糊的&#xff0c;一知半解。趁这次了解希望能够更加深入的去了解这方面的知识&#xff0c;于是有了接下来这篇…

(7)nacos集群搭建方法-有状态负载

前期准备 1.准备好nacos镜像文件,上传至镜像仓库: 2.创建自己集群下-命名空间下的有状态负载: 3.填写工作负载名称、设置实例数量、选择自己的命名空间: 4.选择镜像(目前最新的是2.2.3):

yolov5单目测距+速度测量+目标跟踪(算法介绍和代码)

要在YOLOv5中添加测距和测速功能&#xff0c;您需要了解以下两个部分的原理&#xff1a; 单目测距算法 单目测距是使用单个摄像头来估计场景中物体的距离。常见的单目测距算法包括基于视差的方法&#xff08;如立体匹配&#xff09;和基于深度学习的方法&#xff08;如神经网…

[自动化运维工具]ansible简单介绍和常用模块

ansible 源操作主机功能 自动化运维&#xff08;playbook剧本yaml&#xff09; 是基于python开发的一个配置管理和应用部署工具&#xff0c;在自动化运维中&#xff0c;现在还是异军突起 ansible能批量配置&#xff0c;部署&#xff0c;管理上千台主机&#xff0c;类似于xshell…

使用vite搭建项目时,在启动vite后,浏览器显示页面:找不到localhost的网页

现象 在使用前端工具vite&#xff08;版本5&#xff09;&#xff0c;搭建vue3项目时&#xff0c;启动vite&#xff0c;浏览器显示页面&#xff1a;找不到localhost的网页, 起初怀疑是 未加参数 --host0.0.0.0,导致&#xff0c;后加上该参数后问题依旧 解决 将index.html页面…