加入白板房间

  • Android

更新时间不可用

了解如何快速接入互动白板 SDK,完成环境准备、参数配置、示例运行和核心流程验证。

声网互动白板房间提供丰富的编辑工具,支持展示静态图片、动态 PPT、H5 课件等多种格式的文件,不同终端用户能在白板上实时展示想法,轻松实现多人互动协作。本文介绍如何建立一个简单的项目,使用声网互动白板 SDK 加入房间并实现基础的白板功能。

技术原理

下图展示了加入声网互动白板房间的基本流程。

当你的 App 客户端请求加入互动白板房间时:

  1. 你的 App 服务端使用 SDK Token 向声网互动白板服务端发起创建房间的请求。
  2. 成功创建房间后,声网互动白板服务端向你的 App 服务端返回新建房间的房间 UUID。
  3. 你的 App 服务端使用房间 UUID 生成 room Token,并将房间 UUID 和 room Token 发送给 App 客户端。
  4. App 客户端使用从声网控制台获取的 AppIdentifier 初始化互动白板 SDK 实例。
  5. App 客户端调用 SDK 的方法并传入房间 UUID 和 room Token 以加入互动白板房间。

前提条件

创建 Android 项目

使用 Android Studio 创建一个 Android 项目。

  • 项目名设为 WhiteBoard。
  • Package 名设为 com.example.whiteboard。
  • Activity 类型选择 Empty Activity。
  • Minimum SDK 选择 API 21。

添加权限

在 AndroidManifest.xml 文件中添加访问网络权限:

XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.whiteboard">
 
    <uses-permission android:name="android.permission.INTERNET" />
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.WhiteBoard">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

获取 SDK

打开根目录下的 build.gradle 进行以下配置:

groovy
allprojects {
    repositories {
        google()
        mavenCentral()
        // 添加以下内容
        maven { url 'https://jitpack.io' }
        // 对于国内用户,如果 Gradle sync 失败,可以选择国内镜像,例如阿里云 Maven 镜像
        maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/'}
    }
}

然后打开 app 目录下的 build.gradle 进行如下配置:

groovy
dependencies {
    // 当前正式版
    implementation 'com.github.netless-io:whiteboard-android:2.16.134'
}

关于声网互动白板 SDK 的最新版本,详见发版说明。

防止代码混淆

在 proguard-rules.pro 文件中添加以下语句,防止代码混淆:

proguard
# SDK model
-keep class com.herewhite.** { *; }

实现步骤

现在,我们已经将声网互动白板 SDK 集成到项目中了。接下来我们要调用声网互动白板 SDK 提供的核心 API 实现基础的白板功能。

1. 创建房间

在 App 客户端加入互动白板房间前,你需要在 App 服务端调用互动白板服务端 RESTful API 创建一个房间。详见创建房间(POST)。

请求示例

你可以使用以下 Node.js 脚本发送请求:

信息

使用 Node.js 发送 HTTP 请求前安装 request 模块。你可以运行 npm install request 安装。

JavaScript
var request = require("request");
var options = {
  method: "POST",
  url: "https://api.netless.link/v5/rooms",
  headers: {
    "token": "你的 SDK Token",
    "Content-Type": "application/json",
    "region": "cn-hz",
  },
};
request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log(response.body);
});

如果方法调用成功,声网互动白板服务端将返回新建房间的信息,其中的 uuid 是房间的唯一标识,App 客户端加入房间时需要传入该参数。

响应示例

JSON
{
    "uuid": "4a50xxxxxx796b", // 房间的 UUID
    "teamUUID": "RMmLxxxxxx15aw",
    "appUUID": "i54xxxxxx1AQ",
    "isRecord": true,
    "isBan": false,
    "createdAt": "2021-01-18T06:56:29.432Z",
    "limit": 0
}

2. 生成 Room Token

创建房间并获取新建房间的 uuid 后,你需要在 App 服务端生成 Room Token 并下发给 App 客户端。当 App 客户端加入房间时,声网互动白板服务端会使用该 Token 对其鉴权。

你可以通过以下方式在 App 服务端生成 Room Token:

下面以调用 RESTful API 的方式为例介绍如何生成 Room Token。

请求示例

你可以使用以下 Node.js 脚本发送请求:

信息

使用 Node.js 发送 HTTP 请求前安装 request 模块。你可以运行 npm install request 安装。

JavaScript
var request = require("request");
var options = {
  method: "POST",
  // 将 <房间的 UUID> 替换成你的房间 UUID
  url: "https://api.netless.link/v5/tokens/rooms/<房间的 UUID>",
  headers: {
    "token": "你的 SDK Token",
    "Content-Type": "application/json",
    "region": "cn-hz",
  },
  body: JSON.stringify({ lifespan: 3600000, role: "admin" }),
};
request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log(response.body);
});

如果方法调用成功,声网互动白板服务端将返回生成的 Room Token。

响应示例

JavaScript
"NETLESSROOM_YWs9XXXXXXXXXXXZWNhNjk" // Room Token

3. 创建 UI

修改 activity_main.xml 为如下内容,可以看到整个视图由一个白板页面填充,页面 com.herewhite.sdk.WhiteboardView 由互动白板 SDK 实现。

XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <com.herewhite.sdk.WhiteboardView
        android:id="@+id/white"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible" />
 
</LinearLayout>

4. 初始化 SDK 并加入房间

编辑 MainActivity.java,实现从初始化 SDK 到加入房间的基本操作。你需要传入以下参数:

  • appId:互动白板项目的 AppIdentifier。详见获取 AppIdentifier。
  • uuid:房间的 UUID。详见创建房间。
  • uid:用户的唯一标识符,字符串格式,长度不能超过 1024 字节。如果你使用 2.15.0 或之后版本的 SDK,必须传入该参数;如果你使用 2.15.0 之前版本的 SDK,则不需要传入该参数。
  • roomToken:用于鉴权的 Room Token。生成该 Room Token 使用的房间 UUID 必须和上面的房间 UUID 一致。详见生成 Room Token。
  • region:数据中心,必须和创建房间时设置的数据中心一致。
Java
package com.example.whiteboard;

import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.herewhite.sdk.Room;
import com.herewhite.sdk.RoomListener;
import com.herewhite.sdk.RoomParams;
import com.herewhite.sdk.WhiteSdk;
import com.herewhite.sdk.WhiteSdkConfiguration;
import com.herewhite.sdk.WhiteboardView;
import com.herewhite.sdk.domain.MemberState;
import com.herewhite.sdk.domain.Promise;
import com.herewhite.sdk.domain.Region;
import com.herewhite.sdk.domain.RoomPhase;
import com.herewhite.sdk.domain.RoomState;
import com.herewhite.sdk.domain.SDKError;
import com.herewhite.sdk.domain.WindowParams;

public class MainActivity extends AppCompatActivity {
    private WhiteboardView whiteboardView;
    private WhiteSdk whiteSdk;
    private Room room;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        whiteboardView = findViewById(R.id.white);
        WhiteSdkConfiguration config = new WhiteSdkConfiguration("APP_IDENTIFIER", true);
        config.setRegion(Region.cn);
        // 同一房间各端一致使用高性能绘制插件。
        config.setUseMultiViews(true);
        config.setEnableAppliancePlugin(true);
        whiteSdk = new WhiteSdk(whiteboardView, this, config);
        RoomParams params = new RoomParams("ROOM_UUID", "ROOM_TOKEN", "CLIENT_UID");
        params.setRegion(Region.cn);
        params.setWritable(true);
        params.setWindowParams(new WindowParams());
        RoomListener listener = new RoomListener() {
            @Override public void onPhaseChanged(RoomPhase phase) { }
            @Override public void onDisconnectWithError(Exception error) { }
            @Override public void onKickedWithReason(String reason) { }
            @Override public void onRoomStateChanged(RoomState modifyState) { }
            @Override public void onCanUndoStepsUpdate(long canUndoSteps) { }
            @Override public void onCanRedoStepsUpdate(long canRedoSteps) { }
            @Override public void onCatchErrorWhenAppendFrame(long userId, Exception error) { }
        };
        whiteSdk.joinRoom(params, listener, new Promise<Room>() {
            @Override public void then(Room joinedRoom) {
                room = joinedRoom;
                MemberState state = new MemberState();
                state.setCurrentApplianceName("pencil");
                state.setStrokeColor(new int[]{255, 0, 0});
                room.setMemberState(state);
            }
            @Override public void catchEx(SDKError error) {
                Log.e("Whiteboard", error.getMessage());
            }
        });
    }

    private boolean leaving;

    @Override
    public void onBackPressed() {
        if (leaving) return;
        if (whiteSdk == null) { finish(); return; }
        leaving = true;
        whiteSdk.leaveRoom(new Promise<Object>() {
            @Override public void then(Object completion) {
                // 仅通知退房完成;不是房间状态快照。
                room = null;
                whiteSdk.releaseRoom();
                finish(); // onDestroy 再销毁 View。
            }
            @Override public void catchEx(SDKError error) {
                leaving = false;
                Log.e("Whiteboard", "退出失败,请重试", error);
            }
        });
    }
  
    @Override
    protected void onDestroy() {
        // 正常退出已在 onBackPressed 等待;系统强制销毁不提供异步等待窗口。
        if (whiteboardView != null) whiteboardView.destroy();
        super.onDestroy();
    }
}

5. 接入多窗口

SDK 已通过内嵌 Bridge 集成 Window Manager。创建 WhiteSdk 前启用多 View,入房前将窗口配置传给 RoomParams;步骤 4 的完整示例已包含这些配置:

Java
import com.herewhite.sdk.domain.WindowParams;

// 创建 WhiteSdk 前设置。
config.setUseMultiViews(true);
// joinRoom 前设置;WindowParams 可进一步配置窗口显示。
params.setWindowParams(new WindowParams());

入房成功后,SDK 自动在 WhiteboardView 内挂载窗口管理器。窗口配置和课件控制见统一页面控制。

6. 接入动态 PPT

内嵌 Bridge 已注册 app-slide。业务服务端完成动态 PPT 转换后,在入房成功回调中,或其后的业务按钮中,用 WindowAppParam.createSlideApp 创建参数,再调用 room.addApp:

Java
import com.herewhite.sdk.domain.WindowAppParam;
import com.herewhite.sdk.domain.Promise;
import com.herewhite.sdk.domain.SDKError;

WindowAppParam slide = WindowAppParam.createSlideApp(
    "CONVERSION_TASK_UUID",
    "https://convertcdn.netless.link/dynamicConvert",
    "教学课件.pptx"
);
room.addApp(slide, new Promise<String>() {
    @Override public void then(String appId) {
        android.util.Log.i("Whiteboard", "动态 PPT 窗口 ID:" + appId);
    }
    @Override public void catchEx(SDKError error) {
        android.util.Log.e("Whiteboard", error.getMessage());
    }
});

第一个参数是转换任务 UUID,第二个参数是转换结果的资源地址前缀,末尾不带 /,不能填写 PPT 源文件地址。此工厂方法为窗口生成独立场景目录。创建 App 需要可写权限;房间中已存在的课件会同步显示,不要在每次入房时重复插入。返回窗口 ID 不表示课件资源和首帧全部加载完成。更多说明见展示文件。

7. 开启新教具

创建 WhiteSdk 前同时开启多 View 与新教具;步骤 4 的示例已开启:

Java
config.setUseMultiViews(true);
config.setEnableAppliancePlugin(true);

SDK 已内嵌 Appliance Plugin 与 Worker 资源,业务继续使用 room.setMemberState 等原生 API 切换 Pencil、Selector、Eraser、Shape、Text 等教具,无需额外安装或注册 Web 插件。同一房间各端应一致启用。

入房前可通过 RoomParams.appliancePluginOptions 设置教具插件参数。自定义教具、背景图和 Auto Shape 接入见教具插件介绍与启用。

WhiteSdkConfiguration 的初始化开关需在 SDK 创建前设置,入房后不能通过修改此对象启用新教具。SDK 初始化错误通过 CommonCallback.sdkSetupFail 处理。

编译并运行项目

在 Android Studio 中编译并在模拟器或真机上运行项目。入房成功后可在白板上书写,业务按钮可通过步骤 6 的原生 API 打开动态 PPT。

配置远端参数

需要在入房前读取远端参数时,参见动态配置。地址在创建 SDK 前设置,本地显式参数优先;读取失败回退本地参数。