文章目錄
- 零、GitHub工程
- 一、前言
- 二、C#呼叫java
- 1、撰寫java介面
- 2、將java代碼編譯為jar包
- 3、拷貝jar包和AndroidManifest.xml
- 4、通過AndroidJavaClass呼叫java介面(簡單明了)
- 4.1、撰寫C#呼叫介面
- 4.2、發布APP運行效果
- 5、通過AndroidJNI呼叫java介面(繁瑣)
- 5.1、撰寫C#呼叫介面
- 5.2、發布APP運行效果
- 三、java呼叫C#
- 1、撰寫java介面
- 2、Unity創建物體和C#腳本回應java訊息
- 3、發布APP運行效果
- 四、C#呼叫C/C++
- 1、NDK
- 2、撰寫C/C++介面
- 3、Android.mk與Application.mk
- 4、通過ndk-build.cmd編譯C/C++
- 5、生成so檔案
- 6、拷貝so檔案到Unity工程中
- 7、撰寫C#呼叫介面
- 8、發布APP運行效果
- 五、C/C++呼叫C#
- 1、撰寫C++介面
- 2、Android.mk檔案和Application.mk檔案
- 3、通過ndk-build編譯成so
- 4、拷貝so檔案到Unity工程
- 5、撰寫C#呼叫介面
- 6、發布APP運行效果
- 六、java呼叫C/C++
- 1、本文實作JNI的步驟
- 2、JNIEnv常用的函式
- 2.1、創建Java中的物件
- 2.2、創建Java類中的String物件
- 2.3、其他常用方法
- 3、撰寫java介面
- 4、撰寫c++介面
- 5、編譯java為jar,編譯C++為so
- 6、將jar和so拷貝到Unity工程中
- 7、撰寫C#代碼
- 8、發布APP運行效果
- 七、C/C++呼叫java
- 1、撰寫java介面
- 2、撰寫c++介面
- 3、編譯java為jar,編譯C++為so
- 4、將jar和so拷貝到Unity工程中
- 5、撰寫C#代碼
- 6、發布APP運行效果
零、GitHub工程
本文中的Unity工程,包括Java、C/C++以及編譯腳本,我都上傳到GitHub中了,感興趣的同學可以下載下來學習,
GitHub地址:https://github.com/linxinfa/Unity-JNI-NDK-Demo

一、前言
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,必須繼承UnityPlayerActivity
public 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,相關的教程,可參見我之前的博客:https://linxinfa.blog.csdn.net/article/details/72852155
這里呢,我就直接用jdk命令列來編譯,用到的命令為javac和jar,相關教程可參見我這篇博客:https://linxinfa.blog.csdn.net/article/details/107174242
這里需要注意的是,我們依賴了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 objs
rem 講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
<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.linxinfa.game"
xmlns:tools="http://schemas.android.com/tools">
<application>
<activity android:name="com.linxinfa.game.MyActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>

接下來,就是寫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<AndroidJavaObject>("currentActivity");
txt.text = jo.Call<string>("HelloMethod", "I am Unity") + "\n";
});
btn2.onClick.AddListener(() =>
{
//通過類呼叫靜態介面
var jc = new AndroidJavaClass("com.linxinfa.game.MyActivity");
txt.text = jc.CallStatic<string>("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: {}V
public void demo1() {}
//sig: ()I
public int demo2() {}
//sig: (I)V
public void demo3(int a) {}
//sig: (II)V
public void demo4(int a, int b) {}
//sig: (Ljava/lang/String;)V
public void demo5(String a) {}
//sig: (Ljava/lang/String;Ljava/lang/String;)V
public void demo6(String a, String b) {}
//sig: ([Ljava/lang/String;)V
public 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;)V
public void demo8(String[] a, String[] b) {}
//sig: ([Ljava/lang/String;I)V
public void demo8(String[] a,int b) {}
//sig: ()Z
public 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\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_so
LOCAL_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++_static
APP_CPPFLAGS := -frtti -std=c++11
APP_PLATFORM := android-19
APP_CFLAGS += -Wno-error=format-security
APP_BUILD_SCRIPT := Android.mk
APP_ABI := armeabi-v7a x86
4、通過ndk-build.cmd編譯C/C++
現在就可以通過ndk-build.cmd來編譯C/C++代碼了

為了方便,我們也寫個makeSo.bat腳本來執行

makeSo.bat腳本
cd jni
ndk-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<string.h>
#include<iostream>
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_so
LOCAL_SRC_FILES := \
cpp_call_cs.h \
cpp_call_cs.cpp
include $(BUILD_SHARED_LIBRARY)
Application.mk檔案
APP_STL := c++_static
APP_CPPFLAGS := -frtti -std=c++11
APP_PLATFORM := android-19
APP_CFLAGS += -Wno-error=format-security
APP_BUILD_SCRIPT := Android.mk
APP_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++

java呼叫C/C++,需要通過JNI,
JNI,全稱為Java Native Interface,即Java本地介面,JNI是Java呼叫Native語言的一種特性,通過JNI可以使得Java與C/C++互動,即可以在Java代碼中呼叫C/C++等語言的代碼,也可以在C/C++代碼中呼叫Java代碼,
由于JNI是JVM規范的一部分,因此可以將我們寫的JNI的程式在任何實作了JNI規范的Java虛擬機中運行,同時,這個特性使我們可以復用以前用C/C++寫的大量代碼,
1、本文實作JNI的步驟
(1)、在Java中先宣告一個native方法,
public native int TestJNIAddInt(int a, int b);
通過java呼叫該native方法,
(2)、用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函式,
2、JNIEnv常用的函式
2.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,建構式固定是<init>
jmethodID method_init_id = env->GetMethodID(clazz,"<init>", "()V");
//普通方法的methodID
jmethodID 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 |
2.2、創建Java類中的String物件
jstring NewStringUTF(JNIEnv *env, const jchar *unicodeChars);
例
jstring s = env->NewString("Hello world");
2.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);
//回傳類或介面實體(非靜態)方法 ID
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
//回傳類的實體(非靜態)屬性 ID
jfieldID 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,
3、撰寫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");
}
}
4、撰寫c++介面
#include <jni.h>
//宣告一個類
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);
}
}
5、編譯java為jar,編譯C++為so
編譯java和c++程序見上文中的步驟,此處略過


6、將jar和so拷貝到Unity工程中

7、撰寫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<AndroidJavaObject>("currentActivity");
//呼叫java介面,java中會通過JNI去呼叫C++介面
jo.Call("JavaCallCpp");
});
}
}
8、發布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++介面
#include <jni.h>
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
編譯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<AndroidJavaObject>("currentActivity");
//C#呼叫java介面,然后java中會通過JNI去呼叫C++介面,再然后C++介面中會通過JNI呼叫java介面
jo.Call("CsCallJavaThenJavaCallCppThenCppCallJava");
});
}
}
6、發布APP運行效果
發布apk到Android模擬器上運行效果
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/99511.html
標籤:其他
上一篇:求助關于代碼執行的效率問題,在jupyter和vscode中的差異?
下一篇:python小白求解
