> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Android 快速入门

> 在 Android Studio 中设置 PlayFab Services C SDK，添加原生库，并在 Android 项目中进行第一次 PlayFab API 调用。

# 快速入门：Android

开始使用适用于 Android 的 PlayFab Services SDK。按以下步骤将库包含到你的项目中，并试用基本 PlayFab 功能的示例代码。

本快速入门帮助你使用 Android SDK 进行第一次 PlayFab API 调用。在继续之前，请确保已完成 [快速入门：Game Manager](/services/playfab/live-service-management/gamemanager/quickstart) 中的步骤，以确保你拥有 PlayFab 账户并熟悉 PlayFab Game Manager。

## 要求

* 一个 [PlayFab 开发者账户](https://developer.playfab.com)。
* 已安装 [Android Studio](https://developer.android.com/studio)。

## 项目设置

从 [PlayFab SDK 发布页面](https://github.com/PlayFab/PlayFabCSdk/releases/latest) 将 PlayFab Android SDK 下载到你的项目中。

### 将 PlayFab C SDK 集成到你自己的项目中

以下步骤假定你已使用 Android Studio 创建了一个新项目。

#### 将二进制文件添加到你的游戏

有两部分二进制文件需要集成到你的项目中：共享对象文件（.so）和 Android 归档文件（.aar）。你可以自己构建这些二进制文件，或从发布页面下载。

#### 添加 .so 文件

这些文件通过 CMake 集成到你的项目中。

1. 解压 PlayFab SDK for Android 发布包并将其内容放到你想要的目录。

2. 使用 **target\_include\_directories** 或其他等效函数，添加 PlayFab SDK 发布包中 "Include" 下的头文件：

```cmake theme={null}
TARGET_INCLUDE_DIRECTORIES(
    ${PROJECT_NAME}
    "Include"
)
```

3. 使用 **target\_link\_libraries** 或其他等效函数，将 .so 文件的位置链接到你的项目。

   例如：

```cmake theme={null}
set(PLAYFAB_SERVICES_PATH "[LOCATION OF YOUR FILE]/libPlayFabServices.Android.so")

set(PLAYFAB_CORE_PATH "[LOCATION OF YOUR FILE]/libPlayFabCore.Android.so")

set(LIBHTTPCLIENT_PATH "[LOCATION OF YOUR FILE]/libHttpClient.Android.so")

TARGET_LINK_LIBRARIES(
    [YOUR PROJECT NAME]
    ${PLAYFAB_SERVICES_PATH}
    ${PLAYFAB_CORE_PATH}
    ${LIBHTTPCLIENT_PATH}
)
```

#### 添加 .aar 文件

这些文件通过 Gradle 集成到你的项目中。

1. 在 app 级别的 Android 项目目录中创建一个 libs 文件夹。以下是项目目录现在应有的示例：

<img src="https://mintcdn.com/microsoft-4404708b/N3T1ucKV7zIMBudj/images/playfab/sdks/c/android_1.png?fit=max&auto=format&n=N3T1ucKV7zIMBudj&q=85&s=298add66e2b7ab1a3f4b8508815f4f32" alt="项目目录" width="652" height="496" data-path="images/playfab/sdks/c/android_1.png" />

2. 将 .aar 文件复制到 libs 文件夹中。

3. 在与 libs 文件夹相同目录下的 app 级别 build.gradle 文件中，将以下几行添加到 dependencies 部分。第二行是 libHttpClient 所需的依赖项。

```gradle theme={null}
implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
```

## 初始化和登录

现在你的项目已完全设置为使用 PlayFab Services SDK for Android，按照下面的步骤让一些示例调用运行起来。

### 初始设置

首先你需要设置应用程序使其拥有一个 Android [activity](https://developer.android.com/reference/android/app/Activity) 的实例。你还需要设置一个小型的 C/C++ 应用程序以使用 NDK 与 JNI（Java Native Interface）。以下是一个小示例可供参考：[https://github.com/android/ndk-samples/tree/android-mk/hello-jni。](https://github.com/android/ndk-samples/tree/android-mk/hello-jni。)

该示例包含一个返回 jstring 的原生方法：

```cpp theme={null}
jstring Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
```

原生方法可用于获取 Java VM 和应用程序上下文，这是初始化 PFServices 所需的两样东西。你可以为初始化目的创建一个类似的方法：

```cpp theme={null}
void Java_com_example_hellojni_HelloJni_InitializeApp(JNIEnv* env, jobject appContext)
```

然后可以使用 JNIEnv 变量检索 Java VM。

```cpp theme={null}
    JavaVM* javaVM = nullptr;
    jint res = env->GetJavaVM(&javaVM);
    if (res != JNI_OK)
    {
        // error handling
    }
```

接下来，可通过 jobject 参数获得应用程序上下文。

```cpp theme={null}
    applicationContext = env->NewGlobalRef(appContext);
```

现在你已存储这两个变量，我们可以开始进行调用。

### 头文件

包含 **PFServices.h** 以访问所有内置的 PlayFab 功能：

```cpp theme={null}
#include <playfab/services/PFServices.h>
```

### 初始化

PlayFab 初始化需要两个函数调用：**PFServicesInitialize** 和 **PFServiceConfigCreateHandle**。此初始化的结果是一个 **PFServiceConfigHandle**。你将此句柄提供给后续的登录调用，将调用指向 PlayFab 后端中正确的 title。

```cpp theme={null}
    HCInitArgs initArgs;
    // Use the Java VM and application context from earlier
    initArgs.javaVM = javaVm;
    initArgs.applicationContext = applicationContext;

    HRESULT hr = PFServicesInitialize(nullptr, &initArgs); // Add your own error handling when FAILED(hr) == true

    PFServiceConfigHandle serviceConfigHandle{ nullptr };

    hr = PFServiceConfigCreateHandle(
            "https://ABCDEF.playfabapi.com",    // PlayFab API endpoint - obtained in the Game Manager
            "ABCDEF",                           // PlayFab Title id - obtained in the Game Manager
            &serviceConfigHandle);
```

### 登录

一旦你有了 **PFServiceConfigHandle**，就可以用它进行玩家登录调用。在 SDK 中，使用 **PFAuthenticationLoginWith\*Async** 方法，例如 **PFAuthenticationLoginWithCustomIDAsync**。此函数允许你使用自定义 ID 将玩家登录到 PlayFab。

进行登录调用后，可以使用 **XAsyncGetStatus** 检查调用状态。状态开始为 **E\_PENDING**，调用成功完成后变为 **S\_OK**。如果调用因某种原因失败，状态会反映该失败。所有 PlayFab Services 调用的错误处理方式都是这样。

与 **S\_OK** 结果一起，你会得到一个 **PFEntityHandle**。你使用此句柄以登录玩家的身份进行后续 PlayFab 调用。它包含以该玩家身份对 PlayFab 服务进行身份验证所需的任何材料。

```cpp theme={null}
    PFAuthenticationLoginWithCustomIDRequest request{};
    request.createAccount = true;
    request.customId = "player1";

    XAsyncBlock async{};
    HRESULT hr = PFAuthenticationLoginWithCustomIDAsync(serviceConfigHandle, &request, &async); // Add your own error handling when FAILED(hr) == true
    hr = XAsyncGetStatus(&async, true); // This is doing a blocking wait for completion, but you can use the XAsyncBlock to set a callback instead for async style usage

    std::vector<char> loginResultBuffer;
    PFAuthenticationLoginResult const* loginResult;
    size_t bufferSize;
    hr = PFAuthenticationLoginWithCustomIDGetResultSize(&async, &bufferSize);
    loginResultBuffer.resize(bufferSize);

    PFEntityHandle entityHandle{ nullptr };
    hr = PFAuthenticationLoginWithCustomIDGetResult(&async, &entityHandle, loginResultBuffer.size(), loginResultBuffer.data(), &loginResult, nullptr);
```

## 服务调用

玩家登录后，你现在可以对 PlayFab 后端进行调用。以下是一个获取存储在 PlayFab 中当前玩家文件的调用示例。

### 获取 EntityKey

对于某些 PlayFab 调用来说，知道玩家的 **PFEntityKey** 会很有用。一旦你有了 **PFEntityToken**，就可以使用 **PFEntityGetEntityKey** 检索 **PFEntityKey**。

```cpp theme={null}
    PFEntityKey const* pEntityKey{};
    std::vector<char> entityKeyBuffer;
    size_t size{};
    HRESULT hr = PFEntityGetEntityKeySize(entityHandle, &size); // Add your own error handling when FAILED(hr) == true

    entityKeyBuffer.resize(size);
    hr = PFEntityGetEntityKey(entityHandle, entityKeyBuffer.size(), entityKeyBuffer.data(), &pEntityKey, nullptr);
```

### 调用 GetFiles

所有 PlayFab 调用都遵循类似的模式：准备请求对象、进行调用（使用登录得到的 **PFEntityHandle**）、创建接收响应的对象，然后调用 **GetResult** 函数以填充新创建的容器。

```cpp theme={null}
    XAsyncBlock async{};
    PFDataGetFilesRequest requestFiles{};
    requestFiles.entity = pEntityKey;

    HRESULT hr = PFDataGetFilesAsync(entityHandle, &requestFiles, &async); // Add your own error handling when FAILED(hr) == true
    hr = XAsyncGetStatus(&async, true); // This is doing a blocking wait for completion, but you can use the XAsyncBlock to set a callback instead for async style usage

    size_t resultSize;
    hr = PFDataGetFilesGetResultSize(&async, &resultSize);

    std::vector<char> getFilesResultBuffer(resultSize);
    PFDataGetFilesResponse* getFilesResponseResult{ nullptr };
    hr = PFDataGetFilesGetResult(&async, getFilesResultBuffer.size(), getFilesResultBuffer.data(), &getFilesResponseResult, nullptr);
```

## 清理

当你的游戏准备关闭或你出于其他原因需要清理 PlayFab 时，请确保关闭所有已打开的句柄并调用 **PFServicesUninitializeAsync**。

```cpp theme={null}
    PFEntityCloseHandle(entityHandle);
    entityHandle = nullptr;

    PFServiceConfigCloseHandle(serviceConfigHandle);
    serviceConfigHandle = nullptr;

    XAsyncBlock async{};
    HRESULT hr = PFServicesUninitializeAsync(&async); // Add your own error handling when FAILED(hr) == true
    hr = XAsyncGetStatus(&async, true); // This is doing a blocking wait for completion, but you can use the XAsyncBlock to set a callback instead for async style usage
```

## 异步 API 模式

PlayFab Services SDK 遵循 GDK 中实现的[异步编程模型](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/system/overviews/async-programming-model)。此编程模型涉及使用由 [XAsync 库](/build/core-features/common/async/async-libraries/async-library-xasync)提供的任务和任务队列。此模型与其他 GDK 函数和扩展（如 XBOX Services API）保持一致。虽然它引入了一些复杂性，但也带来了对异步操作的高度控制。

以下示例展示了如何对 **PFDataGetFilesAsync** 进行异步调用。

```cpp theme={null}
    auto async = std::make_unique<XAsyncBlock>();
    async->callback = [](XAsyncBlock* async)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async }; // take ownership of XAsyncBlock

        size_t resultSize;
        HRESULT hr = PFDataGetFilesGetResultSize(async, &resultSize);
        if (SUCCEEDED(hr))
        {
            std::vector<char> getFilesResultBuffer(resultSize);
            PFDataGetFilesResponse* getFilesResponseResult{ nullptr };
            PFDataGetFilesGetResult(async, getFilesResultBuffer.size(), getFilesResultBuffer.data(), &getFilesResponseResult, nullptr);
        }
    };

    PFDataGetFilesRequest requestFiles{};
    requestFiles.entity = m_pEntityKey;
    HRESULT hr = PFDataGetFilesAsync(m_entityHandle, &requestFiles, async.get());
    if (SUCCEEDED(hr))
    {
        async.release(); // at this point, the callback will be called so release the unique ptr
    }

```

## 错误处理

已完成的 **XAsync** 操作返回 HTTP 状态代码。错误状态代码在调用 **XAsyncGetStatus()** 或某个 **PF\*Get()** API 时表现为失败的 **HRESULT**，例如 **HTTP\_E\_STATUS\_NOT\_FOUND**。

要查看服务返回的详细错误消息，请参阅下一节关于调试的内容。这些详细的错误消息在开发期间可用于更好地理解 PlayFab 服务对客户端请求的反应。

## 调试

查看结果并调试 PlayFab Services SDK 中任何调用的最简单方法是启用 [调试跟踪](/services/playfab/sdks/c/tracing)。启用调试跟踪后，你既可以在调试器输出窗口中看到结果，也可以将结果挂接到自己游戏的日志中。

## 参考

[API 参考文档](/services/playfab/api-references/c/pfauthentication/pfauthentication_members)


## Related topics

- [Android 快速入门](/zh-CN/services/playfab/multiplayer/networking/android-specific-requirements.md)
- [使用 Economy v2、Unity IAP 和 Android 快速入门](/zh-CN/services/playfab/economy-monetization/economy-v2/tutorials/getting-started-with-unity-and-android.md)
- [PlayFab 支持的语言](/zh-CN/services/playfab/sdks/languages/index.md)
- [PlayFab Party SDK](/zh-CN/services/playfab/multiplayer/networking/party-sdks/overview.md)
- [快速入门](/zh-CN/services/playfab/economy-monetization/economy-v2/quickstart.md)
