在项目开发中,不可避免的会使用到第三方库。这里以自己用到的so、jar、aar格式的第三方库为例子,说明如何导入并使用。
前提:假设你的项目中已经增加了一个plugin,现在是要在这个plugin中使用第三方库,另外我们的项目假设在Android环境下运行。
导入so库并调用其中方法
- 在plugin的目录下创建include、libs目录,将第三方库的头文件和so文件放入相应位置
- 在模块的Build.cs文件中指定头文件和库文件的位置
if (Target.Platform == UnrealTargetPlatform.Android){PrivateDependencyModuleNames.Add("Launch");AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(ModuleDirectory, "fngameAndroidPlugin_UPL.xml"));// 指定头文件和库文件的路径PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "include/"));PublicAdditionalLibraries.Add(Path.GetFullPath(Path.Combine(ModuleDirectory, "libs/")) + "libtest.so");}
- 在UPL的xml文件中,拷贝库文件到打包输出的目录下
<!--复制文件$S(PluginDir)=插件的build.cs所在目录. $S(BuildDir)打包输出的AndroidManifest.xml所在目录--> <resourceCopies><copyFile src="$S(PluginDir)/lib/libtest.so" dst="$S(BuildDir)/jni/arm64-v8a/libtest.so"/></resourceCopies>
这个地方自己注意一下,在打包成Android后,检查一下Intermediate\Android\arm64\gradle\app\src\main\jniLibs\arm64-v8a目录下有没有相应的库,和设定的加载目录对比一下。实在不行的话,就在打包出来后,手动把so文件拷贝过去。
导入jar或者aar并调用其中方法
- 在UPL的xml文件中,拷贝库文件到打包输出的目录下
<!--复制文件$S(PluginDir)=插件的build.cs所在目录. $S(BuildDir)打包输出的AndroidManifest.xml所在目录--> <resourceCopies><copyFile src="$S(PluginDir)/lib/gamelib.jar" dst="$S(BuildDir)/libs/gamelib.jar"/><copyFile src="$S(PluginDir)/lib/test.aar" dst="$S(BuildDir)/libs/test.aar"/></resourceCopies>
这里注意,如果是aar文件,需要额外添加一下内容,才能让打包出的Android项目正确导入aar包
<buildGradleAdditions><insert>android {repositories{flatDir{dirs'src/main/libs'}}}dependencies {implementation name:'test',ext:'aar'}</insert></buildGradleAdditions>
- 在UPL的xml文件中,引入用到的第三方库。这里用到了哪些就import哪些,如果有遗漏,在后面build的时候也会提示
<gameActivityImportAdditions><insert>import com.example.test;</insert></gameActivityImportAdditions>
- 在UPL的xml文件中,添加对第三方库函数的调用。根据具体的调用方式,采用静态调用或者对象调用。这里相当于给JNI调用生成Java实例,在c++中我们调用的就是这里声明的init方法
<gameActivityClassAdditions><insert>private static Example mInstance;private Context mContext;public int init() {mInstance= Example.getInstance();return mInstance.init(mContext);}</insert> </gameActivityClassAdditions>
-
接下来,我们就可以在c++端进行调用了。假设我们定义了一个TestJNI类,其中有个Init()方法,在方法中首先通过FAndroidApplication::GetJavaEnv()获取JNIEnv,使用FJavaWrapper::FindMethod()找到对应的init函数,然后调用FJavaWrapper::CallIntMethod()方法执行函数,获取它的返回值。关于这里更多的语法请看另外一篇文章
int32 TestJNI::Init() {UE_LOG(LogTemp, Warning, TEXT("TestJNI Init"));int32 initResult = -1;if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) {jmethodID InitID = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "init", "()I", false);if (InitID != nullptr){UE_LOG(LogTemp, Warning, TEXT("Success to find method init"));initResult = FJavaWrapper::CallIntMethod(Env, FJavaWrapper::GameActivityThis, InitID);}}else {UE_LOG(LogTemp, Error, TEXT("Failed to get env"));}UE_LOG(LogTemp, Warning, TEXT("TestJNI Init result: %d"), initResult);return initResult; }
结语
到此,通过plugin导入第三方库并调用函数就说明完成了。从UE打包出的Android工程中,我们可以看到Intermediate\Android\arm64\gradle\app\src\main\java\com\epicgames\unreal\GameActivity.java中,已经有了我们在xml文件中加入的import、init()函数,在libs目录下也有对应的库文件。如果需要,可以用Android Studio打开工程进行进一步的修改。