在上一篇文章中,我们介绍了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("StaticAdd", 1, 1);

Debug.Log("结果1:" + result1);

Unity2JavaClass.Call("DynamicPrint", "Hello World from Android dynamic method ");

int result2 = Unity2JavaClass.Call("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("StaticAdd", 1, 3);

Debug.Log("结果3:" + result3);

Unity2JavaObject.Call("DynamicPrint", "Hello World from Android dynamic method ");

int result4 = Unity2JavaObject.Call("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 结果1:2

3 Unity

4 Unity 结果2:0

5 System.out Hello World from Android static method

6 Unity 结果3:4

7 System.out Hello World from Android dynamic method

8 Unity 结果4:5

  上述代码首先演示了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_EDITOR

using (AndroidJavaObject contentResolver = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic("currentActivity").Call("getContentResolver"))

{

using (AndroidJavaClass secure = new AndroidJavaClass("android.provider.Settings$Secure"))

{

androidID = secure.CallStatic("getString", contentResolver, "android_id");

}

}

#endif

return 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("currentActivity"))

{

bool isSuccess = Unity2JavaObject.Call("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("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 javaObjectCallBack;

public Action boolCallBack;

public Action stringCallBack;

public Action intCallBack;

public Action floatCallBack;

public Action 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通过链接连接到相应的文件或文件夹,从而可以保持文件同步,无需进行复制代码文件的操作。

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: