> ## 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 使用者入門

> PlayFab Party SDK 的 Android 專屬設定，包括 NDK 需求、JNI 初始化，以及資訊清單中的音訊和麥克風權限。

本文件列出將 PlayFab Party 整合到 Android 應用程式所需的基本先決條件和需求。依照本文件設定好系統後，請參閱 [PlayFab Party 快速入門](/zh-TW/services/playfab/multiplayer/networking/quickstart)，以設定 PlayFab Party 的各項建置組塊。

## 先決條件

開始本教學課程之前，請確定已符合下列先決條件：

1. 您已建立 [PlayFab 開發人員帳戶](https://developer.playfab.com)。

2. 您已建立 PlayFab 遊戲，且您的遊戲已加入 PlayFab Party 的允許清單。

3. 您已安裝 Android Studio 3.2 版或更新版本。

4. 您的應用程式以 Android 4.4 (Kitkat) 或更新版本為目標。

5. 您已安裝 Android NDK 18.1.5063045 或更新版本。

6. 您可以存取 [PlayFab Party 平台存放庫](https://github.com/PlayFab/PlayFabParty/releases)

7. 您已建立 Android 簽署憑證，並使用該憑證設定簽署您的應用程式以進行部署。

8. 在 Android 平台上建置的應用程式必須確保應用程式資訊清單中包含 BLUETOOTH\_CONNECT，Party 才能監視並連線到可用的藍牙裝置。

## 包含必要的程式庫和標頭檔案

您需要包含來自 [PlayFab Party 發佈存放庫](https://github.com/PlayFab/PlayFabParty)的下列標頭檔案。

LIB 檔案

1. libParty.a
2. libcrypto.a
3. libssl.a

<Note>
  SSL 程式庫是從 [Open SSL 版本 3.5.7](https://github.com/openssl/openssl/releases#release-openssl-3.5.7) 建置而成。請使用 3.5.7 或更新版本的 openSSL。
</Note>

標頭包含

[Party 標頭](https://github.com/PlayFab/PlayFabParty/tree/master/include)

<Note>
  除了上述 lib 檔案和標頭之外，您還需要 PlayFab SDK 的程式庫和標頭，以及應用程式所需的任何其他平台專屬相依性。如需詳細資訊，請參閱 Android 範例的專案檔案組織。
</Note>

## 讓 PlayFab Party 在 Android 上運作的步驟

由於核心 Party 程式庫是以 C++ 撰寫，我們需要建立一個簡單的 JNI 包裝函式類別來存取 Party 程式庫的功能。概括來說，您需要一個能存取 Party API 方法的類別，以建立網路、連線到網路，以及在網路上傳送訊息。我們在示範應用程式中透過 [NetworkManager Java 類別](https://github.com/PlayFab/PlayFabParty/blob/docs/android/PartySample/demo/src/main/java/com/microsoft/playfab/party/sdk/NetworkManager.java)實現了這一點。

```java theme={null}

import android.util.Log;

public class NetworkManager {
    static {
        System.loadLibrary("partysample");
    }

    private MessageManager messageManager;

    private static NetworkManager networkManager;

    private NetworkManager() {
    }

    public static NetworkManager getInstance() {
        if (networkManager == null) {
            networkManager = new NetworkManager();
        }
        return networkManager;
    }

    public native boolean initialize(String name);

    public native boolean createAndConnectToNetwork(String type, String languageCode);

    public native boolean joinNetwork(String networkId);

    public native void leaveNetwork();

    public native void sendTextMessage(String message, boolean isTTS);

    public native void doWork();

    public native void getPlayerState();

    public native void setLanguage(int idx);

    public native void setPlayFabTitleID(String titleID);

    public void onNetworkCreated(String network) {
        Log.d(getClass().getSimpleName(), "onNetworkCreated: " + network);
        getMessageManager().sendNetworkCreatedMessage(network);
        getMessageManager().sendErrorMessage("Connected to network: " + network);
    }

    public void onMessageReceived(String sender, String message) {
        Log.d(getClass().getSimpleName(), "onMessageReceived: " + sender + ": " + message);
        getMessageManager().sendTextMsgReceivedMessage(sender, message, false);
    }

    public void onTranscriptMessageReceived(String sender, String message) {
        Log.d(getClass().getSimpleName(), "onTranscriptMessageReceived: " + sender + ": " + message);
        getMessageManager().sendTextMsgReceivedMessage(sender, message, true);
    }

    public void onPlayerJoined(String playerId, String name) {
        Log.d(getClass().getSimpleName(), "onPlayerJoined: " + playerId + ": " + name);
        getMessageManager().sendPlayerJoinMessage(playerId, name);
    }

    public void onPlayerLeft(String playerId) {
        Log.d(getClass().getSimpleName(), "onPlayerLeft: " + playerId);
        getMessageManager().sendPlayerLeftMessage(playerId);
    }

    public void toastMessage(String message) {
        getMessageManager().toastMessage(message);
    }

    public void resetChat(String error) {
        getMessageManager().sendResetMessage(error);
    }
    public void resetMessage() {
        getMessageManager().sendResetMessage("Left");
    }

    public void addErrorMessage(String message) {
        getMessageManager().sendErrorMessage(message);
    }

   public void updatePlayerState(String playerId, String state) {
        Log.d(getClass().getSimpleName(), "updatePlayerState: " + playerId + ": " + state);
        getMessageManager().sendPlayerStatusMessage(playerId, state);
    }

    public MessageManager getMessageManager() {
        return MessageManager.getInstance();
    }

}
```

上述 JNI 橋接器背後是一個純 C++ 實作檔案，它會呼叫 [NetworkManager.cpp](https://github.com/PlayFab/PlayFabParty/blob/docs/android/PartySampleNetworkCommon/lib/NetworkManager.cpp)，而後者再呼叫 Party API。

以下是顯示各個層級的範例程式碼片段：

NetworkManager Java 介面公開了一個加入 Party 網路的方法。

```java theme={null}
public native boolean joinNetwork(String networkId);
```

`joinNetwork` 的實作位於 C++ 層的 [PartyDemo.cpp](https://github.com/PlayFab/PlayFabParty/blob/docs/android/PartySample/demo/src/main/cpp/PartyDemo.cpp) 中，如下所示。

```cpp theme={null}
JNIEXPORT jboolean JNICALL
    Java_com_microsoft_playfab_party_sdk_NetworkManager_joinNetwork(
        JNIEnv* env,
        jobject thiz,
        jstring networkId
        )
    {
        if (g_isRunning && g_initializeCompleted)
        {
            Managers::Get<NetworkManager>()->Initialize(g_playfabTitleId.c_str());
            const char* networkNameCStr = env->GetStringUTFChars(networkId, NULL);
            g_networkName = networkNameCStr;
            env->ReleaseStringUTFChars(networkId, networkNameCStr);
            g_isSpinDone = false;
            Managers::Get<PlayFabManager>()->GetDescriptor(
                    g_networkName,
                    [](std::string networkDescriptor)
                    {
                        SendSysLogToUI("OnGetDescriptorForConnectTo : %s", networkDescriptor.c_str());
                        g_networkDescriptor = networkDescriptor;
                        ReleaseSpin();
                    }
            );

            HoldSpin();
            // When network connection is not stable, waiting for ConnectToNetwork callback will cost longer time.
            // To avoid App UI busy waiting, return to UI after ConnectToNetwork returns.
            Managers::Get<NetworkManager>()->ConnectToNetwork(
                g_networkName.c_str(),
                g_networkDescriptor.c_str(),
                []()
                {
                    OnNetworkConnected(g_networkName);
                    SendSysLogToUI("OnConnectToNetwork succeeded");
                },
                [](PartyError error)
                {
                    SendSysLogToUI("OnConnectToNetworkFailed %s", GetErrorMessage(error));
                    ResetChat(GetErrorMessage(error));
                });

            return true;
        }
        else
        {
            SendSysLogToUI("Please waiting for initialization done.");
            return false;
        }
    }
```

在上述程式碼片段中，`joinNetwork` 會呼叫 `NetworkManager::CreateAndConnectToNetwork()`，而後者再呼叫 [Party.h](https://github.com/PlayFab/PlayFabParty/blob/docs/include/Party.h) 中公開的原始 Party API

```cpp theme={null}
void 
NetworkManager::CreateAndConnectToNetwork(
    const char *networkId, 
    std::function<void(std::string)> callback, 
    std::function<void(PartyError)> errorCallback
    )
{
    DEBUGLOG("NetworkManager::CreateAndConnectToNetwork()\n");

    // Set the maximum number of devices allowed in a network to 16 devices
    constexpr uint8_t c_maxSampleNetworkDeviceCount = 16;
    static_assert(c_maxSampleNetworkDeviceCount <= c_maxNetworkConfigurationMaxDeviceCount, "Must be less than or equal to c_maxNetworkConfigurationMaxDeviceCount.");

    // Initialize network configuration for Party Network.
    PartyNetworkConfiguration cfg = {};
    cfg.maxDeviceCount = c_maxSampleNetworkDeviceCount;
    cfg.maxDevicesPerUserCount = 1;
    cfg.maxEndpointsPerDeviceCount = 1;
    cfg.maxUserCount = c_maxSampleNetworkDeviceCount;
    cfg.maxUsersPerDeviceCount = 1;

    //Get the uid from the local chat control
    PartyString uid = nullptr;
    PartyError err = m_localUser->GetEntityId(&uid);

    if (PARTY_FAILED(err))
    {
        DEBUGLOG("GetUserIdentifier failed: %s\n", GetErrorMessage(err));
        errorCallback(err);
        return;
    }

    // Setup the network invitation configuration to use the network id as an invitation id and allow anyone to join.
    PartyInvitationConfiguration invitationConfiguration{
        networkId,                                  // invitation identifier
        PartyInvitationRevocability::Anyone,        // revokability
        0,                                          // authorized user count
        nullptr                                     // authorized user list
    };

    // Initialize an empty network descriptor to hold the result of the following call.
    PartyNetworkDescriptor networkDescriptor = {};

    // Create a new network descriptor
    err = PartyManager::GetSingleton().CreateNewNetwork(
        m_localUser,                                // Local User
        &cfg,                                       // Network Config
        0,                                          // Region List Count
        nullptr,                                    // Region List
        &invitationConfiguration,                   // Invitation configuration
        nullptr,                                    // Async Identifier
        &networkDescriptor,                         // OUT network descriptor
        nullptr                                     // applied initialinvitationidentifier.
    );

    if (PARTY_FAILED(err))
    {
        DEBUGLOG("CreateNewNetwork failed: %s\n", GetErrorMessage(err));
        errorCallback(err);
        return;
    }

    // Connect to the new network
    if (InternalConnectToNetwork(networkDescriptor, networkId, errorCallback))
    {
        m_state = NetworkManagerState::WaitingForNetwork;
        m_onnetworkcreated = callback;
        m_onnetworkcreatederror = errorCallback;
        m_onnetworkconnectedError = errorCallback;
    }
}
```

同樣地，NetworkManager JNI 介面中的每個方法都會透過 PartyDemo 和 NetworkManager 對應到 Party API。

## 後續步驟

在本文中，我們了解了如何開始將 Party 程式庫整合到您的 Android 應用程式中。請參閱 [PlayFab Party 快速入門](/zh-TW/services/playfab/multiplayer/networking/quickstart)，以設定 PlayFab Party 的其餘建置組塊。


## Related topics

- [PlayFab Multiplayer C++ SDK 版本資訊](/zh-TW/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/lobby-and-matchmaking-release-notes.md)
- [服務 C API 概觀 - PFPushNotifications.h](/zh-TW/services/playfab/api-references/c/pfpushnotifications/pfpushnotifications_members.md)
- [PFPushNotificationsServerSendPushNotificationAsync](/zh-TW/services/playfab/api-references/c/pfpushnotifications/functions/pfpushnotificationsserversendpushnotificationasync.md)
- [PFPushNotificationsServerSendPushNotificationFromTemplateAsync](/zh-TW/services/playfab/api-references/c/pfpushnotifications/functions/pfpushnotificationsserversendpushnotificationfromtemplateasync.md)
- [タッチ入門](/ja-jp/build/core-features/common/game-streaming/game-streaming-getting-started-with-touch.md)
