Using EOS SDK on Android

如何借助Android Studio在Android项目中接入EOS SDK

14 分钟可读完

本指南介绍了如何为Android Studio项目添加 Epic Online Services (EOS) SDK,如何确保构建系统能够识别出SDK,以及如何处理Android相关的SDK初始化工作。

在为安卓平台接入SDK前,请务必阅读常用指南及参考一文,了解平台接入的注意事项。

1. 必要设置

本指南假设你已经创建了一个Android Studio项目,并且可以用Android NDK构建C/C++代码。如果你还没有设置项目,请参见https://developer.android.com/ndk/guides,了解如何设置。本指南还假设你使用Gradle来构建项目。你也可以使用其他构建工具,但官方对它们不提供支持。

EOS SDK要求 libc++_shared 与你的应用程序捆绑。如果它是你需要的标准模板库(STL),你可以参阅, 了解如何将它捆绑到你的 .apk 或 .aab中。

如果你打算使用其他的STL,你需要为你的NDK发布版本导入相关的 libc++_shared 库。你可以在 ndk 目录下的 sources\cxx-stl\llvm-libc++\libs\{arch}\ 位置找到 .so 文件。你需要用构建工具将它们包含在最终的构建版本中。

2. 将EOS引入Android Studio项目

你从 开发者门户(Developer Portal) 下载的SDK包含了一个压缩文件,其中包含了以下构件:

  • include 文件夹,包含EOS SDK中公开的所有头文件。
  • lib 文件夹,包含EOS SDK的 .so 文件;这些文件是为 arm64-v8aarmeabi-v7a 构建的,可以在构建应用程序时使用。
  • EOSSDK.aar 文件,它会自动将正确的库与你的应用程序捆绑,并将所需的权限和附带的java代码添加到你的项目中。

无论采用何种构建系统,你都需要将 EOSSDK.aar 作为模块引入到你的项目中。为此,你需要完成下列操作:

  1. 点击 文件(File) > 新建(New) > 新建模块(New Module..)

    在文件菜单中选择新建模块
    1. 在选项列表中选择 导入.JAR/.AAR包(Import .JAR/.AAR Package),然后点击 下一步(Next)
    选择导入.JAR/.AAR包
  2. 在解压的文件中选中 .aar 文件。子项目名称(Subproject Name) 应该会自动填充为“EOSSDK”。如果没有,则手动设置或填写另一个名称。EOSSDK 现在应该会作为导入的模块出现在项目中。

    1. 打开应用程序的 build.gradle 文件,它位于EOSSDK目录中。
    EOSSDK出现在项目中
  3. 在依赖项代码中,添加 androidx.security:security-crypto:1.0.0-rc01 以及 implementation project(path: :EOSSDK)

dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.security:security-crypto:1.0.0-rc01'
implementation project(':EOSSDK')
}

到这一步,SDK应该已经导入到你的项目中了,并且应该会包含在你的构建版本中! 在构建过程中,.aar文件会自动处理并将正确的.so文件导入进apk/aab。

下列小节介绍了如何将C库直接与SDK集成。

3. 让构建系统包含SDK

你可以使用 CMakendk-build 将EOS SDK添加到你的项目中,官方对这两个构建系统都提供支持,可以与NDK一起使用。本小节将介绍如何使用这些构建系统将SDK导入你的项目。

搭配使用EOS和CMake

如果你已经在项目中使用了CMake,你的项目中应该会有一个 CMakeLists.txt 文件。为了将EOS SDK引入到你的NDK项目中,你需要让CMake知道库的位置。

为此,请在 CMakeLists.txt 中加入下列命令行:

CMakeLists.txt

add_library(EOSSDK SHARED IMPORTED)
set(EOSSDKDIR PUT_PATH_TO_EXPANDED_ZIP_HERE)
set_property(TARGET EOSSDK PROPERTY IMPORTED_LOCATION ${EOSSDKDIR}/lib/${ANDROID_ABI}/libEOSSDK.so)
set_property(TARGET EOSSDK PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${EOSSDKDIR}/include/)

如果你在 CMakeLists.txt 所在的相同目录下展开压缩文件,看起来会像是这样:

CMakeLists.txt

add_library(EOSSDK SHARED IMPORTED)
set(EOSSDKDIR ${CMAKE_SOURCE_DIR})
set_property(TARGET EOSSDK PROPERTY IMPORTED_LOCATION ${EOSSDKDIR}/lib/${ANDROID_ABI}/libEOSSDK.so)
set_property(TARGET EOSSDK PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${EOSSDKDIR}/include/)

最后,请确保EOS SDK与项目中正确的库相链接。假如使用Android Studio C++ 新建项目向导(New Project Wizard) 中的示例,看起来会是这样:

CMakeLists.txt

target_link_libraries( # Specifies the target library.
native-lib
# 将目标库链接到EOSSDK
EOSSDK
# 将目标库链接到日志库
# 包含在NDK中。
${log-lib} )

到这一步时,如果同步gradle,你应该就能将EOSSDK符号(Symbol)导入到你的C++项目中,然后移动到 Android特定初始化选项(Android-Specific Initialize Options) 部分。

有关更多CMake的使用信息,请参阅 https://developer.android.com/ndk/guides/cmake。

搭配使用EOS和NDK-Build

如果你打算在项目中使用ndk-build,你需要将它作为模块添加到你的 Android.mk 文件中。 一个基本的Android.mk文件看起来是这样的:

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)

这个文件定义了一个 hello-jni 模块,并使用单个 .c 文件作为其源文件。你需要在这个文件中为EOS SDK定义类似的模块,看起来像是这样:

Android.mk

include $(CLEAR_VARS)
LOCAL_MODULE := eossdk
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/$(TARGET_ARCH_ABI)/libEOSSDK.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)

在这个示例中,压缩文件被解压到与 Android.mk 相同的文件夹中。如果你习惯将文件放在其他位置,你可以将 LOCAL_PATH 替换为正确的路径。

为了通过 hello-jni 模块使用EOS SDK模块,你需要将它们链接起来。你可以通过将sdk作为 LOCAL_SHARED_LIBRARIES 条目添加来实现这一步。另外,我们要确保同时引用 c++_shared

Android.mk

include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SHARED_LIBRARIES := eossdk c++_shared
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)

到这一步,hello-jni 模块应该已经能访问从EOS SDK导出的符号(Symbol)了!

下方展示了一个完整的 Android.mk 示例文件。请根据你的具体需求进行调整。

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := eossdk
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/$(TARGET_ARCH_ABI)/libEOSSDK.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SHARED_LIBRARIES := eossdk c++_shared
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)

有关ndk-build的更多详情,请参阅https://developer.android.com/ndk/guides/ndk-build。

4. 特定于Android的初始化选项

为了让EOS SDK发挥作用,你必须使用 System.loadLibrary 加载原生库(某些引擎会在导入插件时自动完成这步):

static {
System.loadLibrary("EOSSDK");
}

然后将应用程序的上下文传递给SDK,具体如下:

EOSSDK.init(getApplicationContext());

在登录示例中,我们在 MainActivity 类的 onCreate 调用了此方法。

以下是在Android上初始化EOS SDK的例子,你可以根据具体情况进行调整:

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_eossdk_Documentation_initializeSdk( JNIEnv* env,
jobject /* this */)
{
EOS_InitializeOptions SDKOptions = { 0 };
SDKOptions.ApiVersion = EOS_INITIALIZE_API_LATEST;
SDKOptions.ProductName = "EOSDocumentationExample";
SDKOptions.ProductVersion = "0.1";
EOS_EResult InitResult = EOS_Initialize(&SDKOptions);
if(InitResult != EOS_EResult::EOS_Success){
return env->NewStringUTF("Error when initializing");
}
return env->NewStringUTF("Initialized successfully");
}

如要设置磁盘访问目录路径(可选),EOS SDK为Android平台提供了 系统初始化选项

extern "C" JNIEXPORT bool JNICALL
Java_com_epicgames_mobile_login_MainActivity_InitializeSDK(
JNIEnv* env,
jobject /* this */,
jstring internalPath, jstring externalPath)
{
EOS_InitializeOptions SDKOptions = { 0 };
SDKOptions.ApiVersion = EOS_INITIALIZE_API_LATEST;
SDKOptions.ProductName = "EOSDocumentationExample";
SDKOptions.ProductVersion = "0.1";
const char* androidInternalPath = env->GetStringUTFChars(internalPath, nullptr);
const char* androidExternalPath = env->GetStringUTFChars(externalPath, nullptr);
static EOS_Android_InitializeOptions JNIOptions = { 0 };
JNIOptions.ApiVersion = EOS_ANDROID_INITIALIZEOPTIONS_API_LATEST;
JNIOptions.Reserved = nullptr;
JNIOptions.OptionalInternalDirectory = androidInternalPath;
JNIOptions.OptionalExternalDirectory = androidExternalPath;
SDKOptions.SystemInitializeOptions = &JNIOptions;
EOS_EResult InitResult = EOS_Initialize(&SDKOptions);
if(InitResult != EOS_EResult::EOS_Success) {
return env->NewStringUTF("Error when initializing");
}
return env->NewStringUTF("Initialized successfully");
}

以下方法适用于1.10及以上版本的SDK。对于1.11及以上版本的SDK,请使用上述方法。

如果是1.10及以上版本的EOS SDK ,系统初始化选项 被定义为 EOS_Android_InitializeOptions,并要求引用 JavaVM,还需要提供磁盘访问的目录路径(可选)。

为了让EOS SDK在Java端发挥作用,你需要将应用的上下文传递给它,具体如下:

EOSSDK.init(getApplicationContext());

在登录示例中,我们在 MainActivityonCreate 方法中调用了此方法。

为了获得这些选项的正确值,你应该通过JNI传入相关的值。通过引用 JNIEnv*,你可以轻松访问JavaVM。

JavaVM* VM = nullptr;
env->GetJavaVM(&VM);

下方展示了在Android上初始化EOS SDK的完整过程。请根据具体情况进行调整。

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_eossdk_Documentation_initializeSdk( JNIEnv* env,
jobject /* this */, jstring internalPath, jstring externalPath)
{
EOS_InitializeOptions SDKOptions = { 0 };
SDKOptions.ApiVersion = EOS_INITIALIZE_API_LATEST;
SDKOptions.ProductName = "EOSDocumentationExample";
SDKOptions.ProductVersion = "0.1";
JavaVM* VM = nullptr;
env->GetJavaVM(&VM);
const char* androidInternalPath = env->GetStringUTFChars(internalPath, nullptr);
const char* androidExternalPath = env->GetStringUTFChars(externalPath, nullptr);
static EOS_Android_InitializeOptions JNIOptions = { 0 };
JNIOptions.ApiVersion = EOS_ANDROID_INITIALIZEOPTIONS_API_LATEST;
JNIOptions.VM = VM;
JNIOptions.OptionalInternalDirectory = androidInternalPath;
JNIOptions.OptionalExternalDirectory = androidExternalPath;
SDKOptions.SystemInitializeOptions = &JNIOptions;
EOS_EResult InitResult = EOS_Initialize(&SDKOptions);
if(InitResult != EOS_EResult::EOS_Success){
return env->NewStringUTF("Error when initializing");
}
return env->NewStringUTF("Initialized successfully");
}

5. 接收登录意图

为了让EOS SDK收到正确的登录意图,你需要在 Strings.xml 中提供你的客户端ID。

客户端ID必须是小写字母。Android框架中的Scheme匹配是区分大小写的;你应该始终用小写字母注明Scheme。

res/values/strings.xml 中,使用你的 clientId 设置 eos_login_protocol_scheme

<string name="eos_login_protocol_scheme">eos.yourclientidhere</string>