概述
本文实现的功能:
- UE4接入JAVA库
- 由UE4调用安卓端java代码,再回调UE4C++代码
涉及知识点:
- AndroidStudio基础使用
- JNI基础
新建一个Jar或者arr库
File->New->NewProject->EmptyActivity
切换至Android视窗,在app上右键->New->Module->AndroidLibrary
库的目录结构:
在TestClass编写代码:
package com.yours.testlib;
import android.util.Log;
public class TestClass {
public static void TestFunc(){
Log.v("GZJ","[GZJ] Call TestFunc()");
}
}
在 testlib的build.gradle编写
task deleteJar(type: Delete) {
delete 'libs/TestLib.jar'
}
task exportjar(type: Copy) {
from('build/intermediates/aar_main_jar/release/')
into('libs/')
include('classes.jar')
rename('classes.jar', 'TestLib.jar')
}
exportjar.dependsOn(deleteJar, build)
然后Build->MakeProject
然后切换至Terminal窗口:输入gradlew exportjar
稍等一会,在testlib的libs目录下会生成一个TestLib.jar,这就是我们要的文件
需要arr文件的话,在buiild/outputs/arr/TestLib-release.arr
UE4导入Java库并调用
新建一个UE4 C++项目,Edit->Plugin->NewPlugin
选择BlueprintLibrary->CreatePlugin
切换至VS
以下是插件目录结构,没有文件的就新建文件,我的最终目的是导入安卓的一个midi库,所以命名是MidiDriver开头
首先记得在.Build.cs包含对应模块和APL
需要使用JNI则必须添加Launch模块依赖
MidiDriverPlugin.Build.cs部分
if(Target.Platform == UnrealTargetPlatform.Android)
{
PrivateDependencyModuleNames.AddRange(new string[] { "Launch" });
AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", ModuleDirectory + "/MidiDriverPlugin_APL.xml"));
}
MidiDriverPlugin_APL.xml
解释:基于XML的一种语法,编写规则参考 Engine/Source/Programs/UnrealBuildTool/System/UnrealPluginLanguage.cs
感兴趣可以搜索unreal UPL以及安卓开发了解更多,现在讲几个必须点
init:初始化时调用
resourceCopies:在NDK之后复制文件,$S(PluginDir)为xml所在目录,$S(BuildDir)为Intermediate/Android/APK
gameActivityImportAdditions:填写java导入包的代码
gameActivityClassAdditions:编写需要给C++调用的java代码
buildGradleAdditions: 编写需要在BuildGradle中增加的代码
导入本地Jar的方法
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:android="http://schemas.android.com/apk/res/android">
<init>
<log text="[GZJ] MidiDriverPlugin_APL INIT"/>
</init>
<proguardAdditions>
<insert>
</insert>
</proguardAdditions>
<resourceCopies>
<copyFile src = "$S(PluginDir)/../../Libs/Android/TestLib.jar" dst = "$S(BuildDir)/libs/TestLib.jar" />
</resourceCopies>
<gameActivityImportAdditions>
<insert>
import com.yours.testlib.*;
</insert>
</gameActivityImportAdditions>
<gameActivityClassAdditions>
<insert>
public void TestFunc() {
TestClass.TesFunc();
nativeTestFunc(3);
}
public native void nativeTestFunc(int value);
</insert>
</gameActivityClassAdditions>
</root>
导入本地arr的方法
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:android="http://schemas.android.com/apk/res/android">
<init>
<log text="[GZJ] MidiDriverPlugin_APL INIT"/>
</init>
<proguardAdditions>
<insert>
</insert>
</proguardAdditions>
<resourceCopies>
<copyFile src = "$S(PluginDir)/../../Libs/Android/TestLib.arr" dst = "$S(BuildDir)/libs/TestLib.arr" />
</resourceCopies>
<gameActivityImportAdditions>
<insert>
import com.yours.testlib.*;
</insert>
</gameActivityImportAdditions>
<gameActivityClassAdditions>
<insert>
public void TestFunc() {
TestClass.TesFunc();
nativeTestFunc(3);
}
public native void nativeTestFunc(int value);
</insert>
</gameActivityClassAdditions>
<buildGradleAdditions>
<insert>
repositories {
flatDir{ dirs '/src/main/libs/' }
}
dependencies { implementation (name: 'TestLib',ext:'aar') }
</insert>
</buildGradleAdditions>
</root>
然后是JNI调用的编写,没有什么东西,照着模式写就行,FindMethod如何使用在我另一篇博客有详细说明,这里就不列举了
c++调用JAVA代码参考CallTest()
JAVA调用C++代码参考Java_com_epicgames_ue4_GameActivity_nativeTestFunc(JNIEnv * LocalJNIEnv, jobject LocalThiz)
MidiDriverJNI.h
#pragma once
#include "CoreMinimal.h"
#if PLATFORM_ANDROID
#include "Runtime/Launch/Public/Android/AndroidJNI.h"
#include "Runtime/ApplicationCore/Public/Android/AndroidApplication.h"
class MIDIDRIVERPLUGIN_API MidiDriverJNI
{
public:
MidiDriverJNI();
~MidiDriverJNI();
static void CallTest();
};
#endif
MidiDriverJNI.cpp
#include "MidiDriverJNI.h"
#if PLATFORM_ANDROID
MidiDriverJNI::MidiDriverJNI() {
}
MidiDriverJNI::~MidiDriverJNI() {
}
//C++调用java示例
void MidiDriverJNI::CallTest() {
if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
{
bool bIsOptional = false;
static jmethodID MethonId_Test = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "TestFunc", "()V", bIsOptional);
FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, MethonId_Test);
}
}
//Java调用C++代码示例,JNIEnv * LocalJNIEnv, jobject LocalThiz 必须要有,jint value为java函数形参,jint对应c++的int
extern "C" void Java_com_epicgames_ue4_GameActivity_nativeTestFunc(JNIEnv * LocalJNIEnv, jobject LocalThiz,jint value)
{
int32 CPPValue = value;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::FromInt(CPPValue));
}
#endif
然后暴露给蓝图调用
MidiDriverPluginBPLibrary.cpp部分
#include "MidiDriverPluginBPLibrary.h"
#include "MidiDriverPlugin.h"
#include "MidiDriverJNI.h"
float UMidiDriverPluginBPLibrary::MidiDriverPluginSampleFunction(float Param)
{
#if PLATFORM_ANDROID
UE_LOG(LogTemp, Warning, TEXT("[GZJ][UMidiDriverPluginBPLibrary]CallTest Start"));
MidiDriverJNI::CallTest();
UE_LOG(LogTemp, Warning, TEXT("[GZJ][UMidiDriverPluginBPLibrary]CallTest End"));
#endif
return -1;
}
OK,导入成功,验证的话就在UI上建一个按钮,点击就调用MidiDriverPluginSampleFunction
如果屏幕有打印3 说明成功
手机通过数据线连接电脑,开启开发者模式,打开AndroidStudio,查看LogCat就可以