本文共 20049 字,大约阅读时间需要 66 分钟。
文章目录
零、GitHub工程
本文中的Unity
工程,包括Java
、C/C++
以及编译脚本,我都上传到GitHub
中了,感兴趣的同学可以下载下来学习。
GitHub
地址: 一、前言
Unity
中,我们一般使用C#
语言来开发,如果要发布Android
平台,则可能会涉及到java
和C/C++
。
C#
、java
、C/C++
三者是可以相互调用的,为此,我画了如下这个图,下文中我将详细介绍如何实现。 下文演示中,工程包名BundleID
为com.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
命令行来编译,用到的命令为javac
和jar
,相关教程可参见我这篇博客:
这里需要注意的是,我们依赖了Android SDK
的android.jar
和Unity
的classes.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所在目录 Unity2020
的UnityPlayerActivity.java
没有在Unity
的classes.jar
中,而是单独以代码的方式提供给我们。 UnityPlayerActivity.java
所在目录: Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Source\com\unity3d\player
2.4、写个bat来执行编译命令 如下,为了方便,我把上文提到的依赖的jar
和java
文件放在同一个目录中,然后再创建一个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
,生成了objs
和output
两个文件夹 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
改为上文中写的java
的MyActivity
接下来,就是写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运行效果
发布apk
到Android模拟器
上运行效果
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运行效果
发布apk
到Android模拟器
上运行效果
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运行效果
发布apk
到Android模拟器
上运行效果
四、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.mk
和Application.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
文件和生成so
的CPU
架构,如下会生成armeabi-v7a
和x86
两个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文件
生成了libs
和obj
两个文件夹
libs
文件夹中,就是我们生成的armeabi-v7a
和x86
两个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运行效果
发布apk
到Android模拟器
上运行效果
五、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运行效果
发布apk
到Android模拟器
上运行效果
六、java调用C/C++
1、关于JNI
java
调用C/C++
,需要通过JNI
。
JNI
,全称为Java Native Interface
,即Java
本地接口,JNI
是Java
调用Native
语言的一种特性。通过JNI
可以使得Java
与C/C++
交互。 由于JNI
是JVM
规范的一部分,因此可以将我们写的JNI
的程序在任何实现了JNI
规范的Java
虚拟机中运行。同时,这个特性使我们可以复用以前用C/C++
写的大量代码。 2、本文实现JNI的步骤
(1)、在Java
中先声明一个native
方法,
public native int TestJNIAddInt(int a, int b);
通过java
调用该native
方法。
C/C++
实现Java
的native
方法,命名规范如下。 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
是包名,MyActivity
是Java
类名,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
来创建这个对象。只要有jclass
和jmethodID
,我们就可以在本地方法创建这个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
的时候已经说过了,此处不再赘述。(GetFieldID
的sig
同理)。
还需要注意,后面调用函数的时候,一个是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
编译java
和c++
过程见上文中的步骤,此处略过
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运行效果
发布apk
到Android模拟器
上运行效果
七、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++接口
#includeextern "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
编译java
和c++
过程见上文中的步骤,此处略过
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运行效果
发布apk
到Android模拟器
上运行效果
转载地址:https://linxinfa.blog.csdn.net/article/details/108642977 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!