Unity通过NDK、JNI实现C#、java、C/C++之间的相互调用
发布日期:2021-06-30 19:35:39 浏览次数:2 分类:技术文章

本文共 20049 字,大约阅读时间需要 66 分钟。

文章目录

零、GitHub工程

本文中的Unity工程,包括JavaC/C++以及编译脚本,我都上传到GitHub中了,感兴趣的同学可以下载下来学习。

GitHub地址:
在这里插入图片描述

一、前言

Unity中,我们一般使用C#语言来开发,如果要发布Android平台,则可能会涉及到javaC/C++

C#javaC/C++三者是可以相互调用的,为此,我画了如下这个图,下文中我将详细介绍如何实现。
在这里插入图片描述
下文演示中,工程包名BundleIDcom.linxinfa.game

二、C#调用java

Unity提供了两种方式来支持C#调用java

方式一,通过AndroidJavaClass类,方式二,通过AndroidJNI类。
首先,我们写java接口,可以是非静态接口,也可以是静态接口。
非静态的接口,我们需要先获取到java对象,然后通过java对象调用public接口;
静态接口,我们直接获取java类,然后直接通过java类调用public static接口。

1、编写java接口

为了演示,我这里写一个静态的和一个非静态接口。

package com.linxinfa.game;//注意包名,要和Unity工程的包名一致import android.os.Bundle;import com.unity3d.player.UnityPlayerActivity;//自己写的主Activity,必须继承UnityPlayerActivitypublic class MyActivity extends UnityPlayerActivity{
private String m_hi; @Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); m_hi = "hi, 我是java"; } //非静态接口 public String HelloMethod(String msg) {
return m_hi + ", 我是非静态接口,你传给我的参数是: " + msg; } //静态接口 public static String HelloStaticMethod(int a, int b) {
return "你好,我是java静态接口,a + b = " + (a+b); }}

2、将java代码编译为jar包

可以使用Eclipse或者Android Studio来编译java,相关的教程,可参见我之前的博客:

这里呢,我就直接用jdk命令行来编译,用到的命令为javacjar,相关教程可参见我这篇博客:

这里需要注意的是,我们依赖了Android SDKandroid.jarUnityclasses.jar,编译的时候,需要指明依赖它们。

2.1 Android SDK的android.jar所在目录

Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-28(具体android版本看你下载的版本)
在这里插入图片描述
2.2、Unity的classes.jar所在目录
Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes
在这里插入图片描述
2.3、UnityPlayerActivity.java所在目录
Unity2020UnityPlayerActivity.java没有在Unityclasses.jar中,而是单独以代码的方式提供给我们。
UnityPlayerActivity.java所在目录:
Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Source\com\unity3d\player在这里插入图片描述
2.4、写个bat来执行编译命令
如下,为了方便,我把上文提到的依赖的jarjava文件放在同一个目录中,然后再创建一个makeJar.bat脚本,将编译的命令行放在makeJar.bat
在这里插入图片描述
makeJar.bat内容如下:

rem 创建objs目录,用来存放生成的.class文件@if not exist objs mkdir objs rem 创建output目录,用来存放最后编译成的.jar文件@if not exist output mkdir output rem javac命令,生成.class文件到objs目录中javac -source 1.6 -target 1.6 -nowarn -encoding utf8 -cp "./android_sdk.jar;./unity_classes.jar" -d "./objs" UnityPlayerActivity.java MyActivity.java rem 进入objs目录cd objsrem 讲objs/com/linxinfa/game/目录中所有的.class文件打包成.jar文件jar cvf ../output/game.jar ./com/linxinfa/game/*

注意,由于我们用到了jdk命令行,所以需要配置一下环境变量

在这里插入图片描述
双击运行makeJar.bat,生成了objsoutput两个文件夹
在这里插入图片描述
output文件夹中,game.jar就是我们生成出来的jar
在这里插入图片描述

3、拷贝jar包和AndroidManifest.xml

game.jar拷贝到Unity工程中的Assets/Plugins/Android/libs目录中

在这里插入图片描述
另外,还有一个UnityManifest.xml需要拷贝,UnityManifest.xml所在目录:
Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Apk
在这里插入图片描述
UnityManifest.xml拷贝到Unity工程中的Assets/Plugins/Android目录中,并重命名为AndroidManifest.xml,将package改为包名com.linxinfa.game,将主Activity改为上文中写的javaMyActivity

在这里插入图片描述

接下来,就是写C#代码来调用jar中的java接口了。

4、通过AndroidJavaClass调用java接口(简单明了)

在这里插入图片描述

4.1、编写C#调用接口
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class CsCallJava_AndroidJavaClass : MonoBehaviour{
public Text txt; public Button btn1; public Button btn2; void Start() {
btn1.onClick.AddListener(() => {
//通过对象调用非静态接口 var jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); var jo = jc.GetStatic
("currentActivity"); txt.text = jo.Call
("HelloMethod", "I am Unity") + "\n"; }); btn2.onClick.AddListener(() => {
//通过类调用静态接口 var jc = new AndroidJavaClass("com.linxinfa.game.MyActivity"); txt.text = jc.CallStatic
("HelloStaticMethod", 10, 15); }); }}
4.2、发布APP运行效果

发布apkAndroid模拟器上运行效果

在这里插入图片描述

5、通过AndroidJNI调用java接口(繁琐)

在这里插入图片描述

5.1、编写C#调用接口
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class CsCallJava_AndroidJNI : MonoBehaviour{
public Text txt; public Button btn1; public Button btn2; void Start() {
btn1.onClick.AddListener(() => {
//通过对象调用非静态接口--------------------------------------- //获得类 IntPtr clz = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer"); IntPtr fid = AndroidJNI.GetStaticFieldID(clz, "currentActivity", "Landroid/app/Activity;"); //获得静态属性 IntPtr obj = AndroidJNI.GetStaticObjectField(clz, fid); //获得类 IntPtr clz_OurAppActitvityClass = AndroidJNI.FindClass("com/linxinfa/game/MyActivity"); //获得方法 IntPtr methodId = AndroidJNI.GetMethodID(clz_OurAppActitvityClass, "HelloMethod", "(Ljava/lang/String;)Ljava/lang/String;"); //参数 jvalue v = new jvalue(); v.l = AndroidJNI.NewStringUTF("I am Unity"); var result = AndroidJNI.CallStringMethod(obj, methodId, new jvalue[] {
v }); txt.text =result; AndroidJNI.DeleteLocalRef(clz); AndroidJNI.DeleteLocalRef(fid); AndroidJNI.DeleteLocalRef(obj); AndroidJNI.DeleteLocalRef(clz_OurAppActitvityClass); AndroidJNI.DeleteLocalRef(methodId); }); btn2.onClick.AddListener(() => {
//通过类调用静态接口-------------------------------------- //获得类 IntPtr clz = AndroidJNI.FindClass("com/linxinfa/game/MyActivity"); //调用静态方法 IntPtr methodId = AndroidJNI.GetStaticMethodID(clz, "HelloStaticMethod", "(II)Ljava/lang/String;"); //参数 jvalue v1 = new jvalue(); v1.i = 10; jvalue v2 = new jvalue(); v2.i = 15; var result = AndroidJNI.CallStaticStringMethod(clz, methodId, new jvalue[] {
v1, v2 }); txt.text = result; AndroidJNI.DeleteLocalRef(clz); AndroidJNI.DeleteLocalRef(methodId); }); }}
5.2、发布APP运行效果

发布apkAndroid模拟器上运行效果

在这里插入图片描述
GetMethodID第三个参数是sig,它是对函数的签名,也可以说标识,具体的格式为

Java类型 符号
Boolean Z
Byte B
Char C
Short S
Integer I
Long L
Float F
Double D
Void V
Object对象 L开头,包名/类名,”;”结尾,$标识嵌套类
数组 [签名

例子:

//sig: {}Vpublic void demo1() {
} //sig: ()Ipublic int demo2() {
} //sig: (I)Vpublic void demo3(int a) {
} //sig: (II)Vpublic void demo4(int a, int b) {
} //sig: (Ljava/lang/String;)Vpublic void demo5(String a) {
} //sig: (Ljava/lang/String;Ljava/lang/String;)Vpublic void demo6(String a, String b) {
}//sig: ([Ljava/lang/String;)Vpublic void demo7(String [] arr) {
} //sig: (Ljava/lang/String;)Ljava/lang/String;public String demo8(String a) {
return ""; } //sig: ([java/lang/String;)Ljava/lang/String;public String demo6(String [] a) {
return ""; } //sig: ([Ljava/lang/String;[Ljava/lang/String;)Vpublic void demo8(String[] a, String[] b) {
} //sig: ([Ljava/lang/String;I)Vpublic void demo8(String[] a,int b) {
} //sig: ()Zpublic boolean demo9() {
return false; } //内部类// (Ljava/lang/String;com/linxinfa/game/Demo$DemoInnter;)Z

如果是普通类型的数组不需要加;后缀,如果是Object类型的数组则需要添加;

三、java调用C#

java调用C#一般是通过UnityPlayer.UnitySendMessage的方式发送消息给Unity

在这里插入图片描述

1、编写java接口

首先,封装一个java接口,里面通过UnityPlayer.UnitySendMessage发消息给Unity

package com.linxinfa.game; import android.os.Bundle;import com.unity3d.player.UnityPlayer;import com.unity3d.player.UnityPlayerActivity; public class MyActivity extends UnityPlayerActivity{
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); } public static void JavaCallCSharp(String msg) {
String returnMsg = "hello,我是java, 我通过UnityPlayer.UnitySendMessage返回消息给你: " + msg; //第一个参数是GameObject名字 //第二个参数是GameObject上脚本的public方法 //第三个参数是发送给Unity的参数 UnityPlayer.UnitySendMessage("JavaMsgRecver", "OnJavaMsg", returnMsg); }}

2、Unity创建物体和C#脚本响应java消息

然后,在Unity场景中创建一个物体,取名为JavaMsgRecver,在创建一个脚本JavaCallCs_UnitySendMessage.cs挂到JavaMsgRecver物体上。

在这里插入图片描述
其中HandleJavaMsg.cs代码如下:

using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class JavaCallCs_UnitySendMessage : MonoBehaviour{
public Text txt; public Button btn; void Start() {
btn.onClick.AddListener(() => {
var jc = new AndroidJavaClass("com.linxinfa.game.MyActivity"); jc.CallStatic("JavaCallCSharp", "I am Unity"); }); } public void OnJavaMsg(string msg) {
txt.text = msg; }}

3、发布APP运行效果

发布apkAndroid模拟器上运行效果

在这里插入图片描述

四、C#调用C/C++

在这里插入图片描述

1、关于NDK

Android平台,C/C++需通过NDK编译成动态链接库.so文件,然后C#中通过[DllImport("soname")]声明后即可调用。

NDK,全称Native Development Kit,是Android的一种开发工具包。

目前的Android开发,不再是纯粹的Java层开发,更多的会与C/C++结合,把一些重要的方和行为以及一些私密性质的东西放在C/C++中,通过NDK将其编译成.so动态库文件,放入工程中的libs目录。

下文中我们会使用ndk命令行,所以需要配置一下NDK\build的环境变量。

在这里插入图片描述

2、编写C/C++接口

封装C/C++接口,放在jni文件夹中。

在这里插入图片描述

cs_call_c.c脚本

int AddInt(int a, int b){
return a + b;}

cs_call_cpp.cpp脚本

//声明一个类class  MyClass{
public: static float AddFloat(float a, float b) {
return a + b; }};extern "C"{
float AddFloat(float a,float b) {
return MyClass::AddFloat(a,b); }}

3、Android.mk与Application.mk

接着,我们需要创建Android.mkApplication.mk这两个文件。

在这里插入图片描述

Android.mk文件,主要指明参与编译的C/C++文件和生成的so名字,如下,名字为linxinfa_so,最后生成出来的so文件就是liblinxinfa_so.so

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE    := linxinfa_soLOCAL_SRC_FILES := \cs_call_c.c \cs_call_cpp.cpp include $(BUILD_SHARED_LIBRARY)

Application.mk文件,主要是配置引用的Android.mk文件和生成soCPU架构,如下会生成armeabi-v7ax86两个CPU架构的so

APP_STL := c++_staticAPP_CPPFLAGS := -frtti -std=c++11APP_PLATFORM := android-19APP_CFLAGS += -Wno-error=format-securityAPP_BUILD_SCRIPT := Android.mkAPP_ABI := armeabi-v7a x86

4、通过ndk-build.cmd编译C/C++

现在就可以通过ndk-build.cmd来编译C/C++代码了

在这里插入图片描述
为了方便,我们也写个makeSo.bat脚本来执行
在这里插入图片描述
makeSo.bat脚本

cd jnindk-build

运行makeSo.bat脚本

在这里插入图片描述

5、生成so文件

生成了libsobj两个文件夹

在这里插入图片描述
其中,libs文件夹中,就是我们生成的armeabi-v7ax86两个CPU架构的so
在这里插入图片描述

6、拷贝so文件到Unity工程中

so拷贝到Unity工程中的Assets/Plugins/Android/libs目录中

在这里插入图片描述

7、编写C#调用接口

接着,编写C#代码调用C/C++接口

using System.Collections;using System.Collections.Generic;using System.Runtime.InteropServices;using UnityEngine;using UnityEngine.UI;public class CsCallCCPP_DllImport_SO : MonoBehaviour{
public Text txt; public Button btn1; public Button btn2; void Start() {
btn1.onClick.AddListener(() => {
//C#调用C txt.text = "C#调用C接口: AddInt(2, 6) = " + AddInt(2, 6); }); btn2.onClick.AddListener(() => {
//C#调用C++ txt.text = "C#调用C++接口: AddFloat(2.7, 4.2) = " + AddFloat(2.7f, 4.2f); }); } //C接口 [DllImport("linxinfa_so")] public static extern int AddInt(int a, int b); //C++接口 [DllImport("linxinfa_so")] public static extern float AddFloat(float a, float b);}

8、发布APP运行效果

发布apkAndroid模拟器上运行效果

在这里插入图片描述

五、C/C++调用C#

在这里插入图片描述

1、编写C++接口

cpp_call_cs.h

#pragma once#include
#include
extern "C"{
class Debug {
public: //声明函数指针 static void (*Log)(char* message,int iSize); }; // 注册C#的委托 void InitCSharpDelegate(void (*Log)(char* message, int iSize)); // 给C#调用的C++接口,里面再通过函数指针调用C#的委托 void HelloCppToCs(char* message);}

cpp_call_cs.cpp

#include "cpp_call_cs.h"// 定义函数指针,用来接受C#的委托void(*Debug::Log)(char* message, int iSize); void HelloCppToCs(char* message){
char temp[512]="你好,我是c++, 我收到了你传过来的参数: "; //字符串拼接 strcat(temp, message); // 调用C#的委托 Debug::Log(temp, strlen(temp));} // 注册C#的委托void InitCSharpDelegate(void(*Log)(char* message, int iSize)){
Debug::Log = Log;}

2、Android.mk文件和Application.mk文件

Android.mk文件

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE    := linxinfa_soLOCAL_SRC_FILES := \cpp_call_cs.h \cpp_call_cs.cpp include $(BUILD_SHARED_LIBRARY)

Application.mk文件

APP_STL := c++_staticAPP_CPPFLAGS := -frtti -std=c++11APP_PLATFORM := android-19APP_CFLAGS += -Wno-error=format-securityAPP_BUILD_SCRIPT := Android.mkAPP_ABI := armeabi-v7a

3、通过ndk-build编译成so

在这里插入图片描述

4、拷贝so文件到Unity工程

在这里插入图片描述

5、编写C#调用接口

using AOT;using System;using System.Collections;using System.Collections.Generic;using System.Runtime.InteropServices;using UnityEngine;using UnityEngine.UI;public class CPPCallCs_MonoPInvokeCallback : MonoBehaviour{
public Text txt; public Button btn; public static string s_msg = ""; void Start() {
InitCSharpDelegate(LogMessageFromCpp); btn.onClick.AddListener(() => {
//C#调用C HelloCppToCs("I am Unity"); }); } void Update() {
if(!string.IsNullOrEmpty(s_msg)) txt.text = s_msg; } [MonoPInvokeCallback(typeof(LogDelegate))] public static void LogMessageFromCpp(IntPtr message, int iSize) {
s_msg = "C#被C++调用\n"; s_msg += Marshal.PtrToStringAnsi(message, iSize); Debug.Log(s_msg); } public delegate void LogDelegate(IntPtr message, int iSize); [DllImport("linxinfa_so")] public static extern void InitCSharpDelegate(LogDelegate log); [DllImport("linxinfa_so")] public static extern void HelloCppToCs(string msg);}

6、发布APP运行效果

发布apkAndroid模拟器上运行效果

在这里插入图片描述

六、java调用C/C++

在这里插入图片描述

1、关于JNI

java调用C/C++,需要通过JNI

JNI,全称为Java Native Interface,即Java本地接口,JNIJava调用Native语言的一种特性。通过JNI可以使得JavaC/C++交互。
由于JNIJVM规范的一部分,因此可以将我们写的JNI的程序在任何实现了JNI规范的Java虚拟机中运行。同时,这个特性使我们可以复用以前用C/C++写的大量代码。

2、本文实现JNI的步骤

(1)、在Java中先声明一个native方法,

public native int TestJNIAddInt(int a, int b);

通过java调用该native方法。

(2)、用C/C++实现Javanative方法,命名规范如下。

jint Java_com_linxinfa_game_MyActivity_TestJNIAddInt(JNIEnv* env, 												jobject thiz, jint a, jint b)

jint表示此方法的返回值为整形,其他数据类型还有jlong 、jfloat、jdouble、 jobject、jboolean、jbyte、jchar、jshort

函数名固定以Java开头,com_linxinfa_game是包名,MyActivityJava类名,TestJNIAddInt就是Java中声明的native方法名。参数里面,前面两个参数固定,后面的参数自定义。
JNIEnv是一个指针,指向一个线程相关的结构,线程相关结构,线程相关结构指向JNI函数指针数组,这个数组中存放了大量的JNI函数指针,这些指针指向了详细的JNI函数。

3、JNIEnv常用的函数

3.1、创建Java中的对象
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);

第一个参数jclass clazz代表的你要创建的类的对象,第二个参数jmethodID methodID代表你要使用那个构造方法ID来创建这个对象。只要有jclassjmethodID,我们就可以在本地方法创建这个Java类的对象。

//获取Java类jclass  clazz = env->FindClass("com/linxinfa/game/MyJavaClass");//拿到构造方法的methodID,构造函数固定是
jmethodID method_init_id = env->GetMethodID(clazz,"
", "()V");//普通方法的methodIDjmethodID method_set_id = env->GetMethodID(clazz,"setAge", "(I)V");jmethodID method_get_id = env->GetMethodID(clazz,"getAge", "()I");//创建了MyJavaClass对象jobject obj = env->NewObject(clazz, method_init_id);//调用setAge方法env->CallVoidMethod(obj, method_set_id, 21);//调用getAge方法int age = env->CallIntMethod(obj, method_get_id);

GetMethodID第三个参数是函数签名sig,文章前面讲C#通过JNI调用Java的时候已经说过了,此处不再赘述。(GetFieldIDsig同理)。

还需要注意,后面调用函数的时候,一个是CallVoidMethod,一个是CallIntMethod,还有其他的Call接口,根据返回值调用对应的方法

方法名 本地返回类型
CallVoidMethod、CallVoidMethodA、CallVoidMethodV void
CallObjectMethod、CallObjectMethodA、CallObjectMethodV jobject
CallBooleanMethod、CallBooleanMethodA、CallBooleanMethodV jboolean
CallByteMethod、CallByteMethodA、CallByteMethodV jbyte
CallCharMethod、CallCharMethodA、CallCharMethodV jchar
CallShortMethod、CallShortMethodA、CallShortMethodV jshort
CallIntMethod、CallIntMethodA、CallIntMethodV jint
CallLongMethod、CallLongMethodA、CallLongMethodV jlong
CallFloatMethod、CallFloatMethodA、CallFloatMethodV jfloat
CallDoubleMethod、CallDoubleMethodA、CallDoubleMethodV jdouble
3.2、创建Java类中的String对象
jstring NewStringUTF(JNIEnv *env, const jchar *unicodeChars);

jstring s = env->NewString("Hello world");
3.3、其他常用方法
//加载Java定义的类,类名需要是全路径,如"com/linxinfa/game/MyJavaClass"jclass FindClass(JNIEnv *env, const char *name); //返回Java字符串的长度(Unicode字符数)jsize GetStringLength(JNIEnv *env, jstring string); //以字节为单位返回字符串的 UTF-8 长度。jsize GetStringUTFLength(JNIEnv *env, jstring string);   //测试对象是否为某个类的实例jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz); //返回类或接口实例(非静态)方法 IDjmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);//返回类的实例(非静态)属性 IDjfieldID GetFieldID(JNIEnv *env, jclass clazz,   const char *name, const char *sig);//返回对象的实例(非静态)属性的值。要访问的属性由通过调用GetFieldID() 而得到的属性ID指定NativeType GetField(JNIEnv*env, jobject obj, jfieldID fieldID); //设置对象的实例(非静态)属性的值。要访问的属性由通过调用GetFieldID() 而得到的属性ID指定void SetField(JNIEnv *env, jobject obj, jfieldID fieldID,   NativeType value);//返回数组中的元素数jsize GetArrayLength(JNIEnv *env, jarray array);  //返回 Object 数组的元素jobject GetObjectArrayElement(JNIEnv *env,  jobjectArray array, jsize index);  //设置 Object 数组的元素void SetObjectArrayElement(JNIEnv *env, jobjectArray array,   jsize index, jobject value);

好了,开始写Demo

4、编写java接口

package com.linxinfa.game; import android.os.Bundle;import com.unity3d.player.UnityPlayer;import com.unity3d.player.UnityPlayerActivity; import android.widget.Toast; public class MyActivity extends UnityPlayerActivity{
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); } public void JavaCallCpp() {
//调用c++接口 int result = TestJNIAddInt(4, 5); //展示运算结果 Toast.makeText(this, "java调用c++, 4f + 5f = " + result, Toast.LENGTH_LONG).show(); } //声明.cpp中的TestJNIAddInt方法 public native int TestJNIAddInt(int a, int b); static {
//加载.so文件 System.loadLibrary("linxinfa_so"); } }

5、编写c++接口

#include 
//声明一个类class MyMathClass{
public: static int AddInt(int a, int b) {
return a + b; }};extern "C"{
jint Java_com_linxinfa_game_MyActivity_TestJNIAddInt(JNIEnv* env, jobject thiz, jint a, jint b) {
return MyMathClass::AddInt(a,b); }}

6、编译java为jar,编译C++为so

编译javac++过程见上文中的步骤,此处略过

在这里插入图片描述
在这里插入图片描述

7、将jar和so拷贝到Unity工程中

在这里插入图片描述

8、编写C#代码

using UnityEngine;using UnityEngine.UI;public class JavaCallCPP_JNI : MonoBehaviour{
public Button btn; void Start() {
btn.onClick.AddListener(() => {
var jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); var jo = jc.GetStatic
("currentActivity"); //调用java接口,java中会通过JNI去调用C++接口 jo.Call("JavaCallCpp"); }); }}

9、发布APP运行效果

发布apkAndroid模拟器上运行效果

在这里插入图片描述

七、C/C++调用java

在这里插入图片描述

C/C++调用java也是通过JNI

1、编写java接口

package com.linxinfa.game; import android.os.Bundle;import com.unity3d.player.UnityPlayer;import com.unity3d.player.UnityPlayerActivity; import android.widget.Toast; public class MyActivity extends UnityPlayerActivity{
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); } public void CsCallJavaThenJavaCallCppThenCppCallJava() {
JNICppCallJava(); } public void CppCallJavaMethod(String msg) {
Toast.makeText(this, "c++调用java:" + msg, Toast.LENGTH_LONG).show(); } //声明.cpp中的TestJNIAddInt方法 public native void JNICppCallJava(); static {
//加载.so文件 System.loadLibrary("linxinfa_so"); } }

2、编写c++接口

#include 
extern "C"{
void Java_com_linxinfa_game_MyActivity_JNICppCallJava(JNIEnv* env, jobject thiz) {
jclass clz = env->FindClass("com/unity3d/player/UnityPlayer"); jfieldID fid = env->GetStaticFieldID(clz, "currentActivity", "Landroid/app/Activity;"); jobject obj = env->GetStaticObjectField(clz, fid); jclass clzMyActivity = env->FindClass("com/linxinfa/game/MyActivity"); jmethodID methodId = env->GetMethodID(clzMyActivity, "CppCallJavaMethod","(Ljava/lang/String;)V"); jstring msg = env->NewStringUTF("I am c++"); env->CallVoidMethod(obj, methodId, msg); }}

3、编译java为jar,编译C++为so

编译javac++过程见上文中的步骤,此处略过

4、将jar和so拷贝到Unity工程中

在这里插入图片描述

5、编写C#代码

using UnityEngine;using UnityEngine.UI;public class CPPCallJava_JNI : MonoBehaviour{
public Button btn; void Start() {
btn.onClick.AddListener(() => {
var jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); var jo = jc.GetStatic
("currentActivity"); //C#调用java接口,然后java中会通过JNI去调用C++接口,再然后C++接口中会通过JNI调用java接口 jo.Call("CsCallJavaThenJavaCallCppThenCppCallJava"); }); }}

6、发布APP运行效果

发布apkAndroid模拟器上运行效果在这里插入图片描述

转载地址:https://linxinfa.blog.csdn.net/article/details/108642977 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:OpenHarmony鸿蒙操作系统源码
下一篇:分享一个超酷的Unity的Shader案例工程(GitHub)

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月12日 19时04分52秒