本篇文档旨在指导 Android 开发者如何使用
AtomicXCore 框架中的 BarrageStore 模块,为您的直播应用快速集成功能丰富、性能卓越的弹幕系统。
核心功能
BarrageStore 为您的直播应用提供了一套完整的弹幕解决方案,核心功能包括:接收并展示直播间弹幕消息。
发送文本弹幕与观众互动。
发送自定义业务弹幕,以支持礼物、点赞等复杂场景。
在本地消息列表中插入系统提示(例如,"欢迎 XX 进入直播间")。
核心概念解析
在开始集成之前,我们先通过下表了解一下 BarrageStore 相关的几个核心概念:
核心概念 | 类型 | 核心职责与描述 |
data class | 代表一条弹幕消息的数据模型。它包含了发送者信息 ( sender)、消息内容 (textContent 或 data)、消息类型 (messageType) 等所有关键信息。 | |
data class | 代表弹幕模块的当前状态。其核心属性 messageList 是一个StateFlow,按时间顺序存储了当前直播间的所有弹幕消息,是UI渲染的数据源。 | |
abstract class | 这是与弹幕功能交互的核心管理类。通过它,您可以发送消息 ( sendTextMessage, sendCustomMessage),并通过订阅其 state 属性来接收和监听所有弹幕消息的更新。 |
实现步骤
步骤1:组件集成
步骤2:初始化并监听弹幕
获取一个与当前直播间
liveId 绑定的 BarrageStore 实例,并设置一个订阅者来实时接收最新的全量弹幕消息列表。import kotlinx.coroutines.*import kotlinx.coroutines.flow.*import io.trtc.tuikit.atomicxcore.api.barrage.Barrageimport io.trtc.tuikit.atomicxcore.api.barrage.BarrageStoreclass BarrageManager(private val liveId: String) {private val barrageStore: BarrageStore = BarrageStore.create(liveId)private val scope = CoroutineScope(Dispatchers.Main)// 对外暴露【全量】消息列表的Flow,方便UI层订阅private val _messagesFlow = MutableStateFlow<List<Barrage>>(emptyList())val messagesFlow: StateFlow<List<Barrage>> = _messagesFlow.asStateFlow()init {// 初始化后立即开始监听弹幕消息subscribeToBarrageUpdates()}private fun subscribeToBarrageUpdates() {scope.launch {barrageStore.barrageState.messageList.collect { messageList ->// 当 messageList 更新时,通过 Flow 将新列表传递给UI层// 关键点:这里接收到的是包含所有历史消息的【完整列表】_messagesFlow.value = messageList}}}}
步骤3:发送文本弹幕
调用
sendTextMessage 方法向直播间内的所有用户广播一条纯文本消息。import io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.barrage.BarrageStoreclass BarrageManager(private val liveId: String) {private val barrageStore: BarrageStore = BarrageStore.create(liveId)// 发送一条文本弹幕fun sendTextMessage(text: String) {// 建议:增加非空校验,避免发送无效消息if (text.isEmpty()) {return}// 调用核心API发送消息barrageStore.sendTextMessage(text,mapOf("custom key" to "custom value",),object : CompletionHandler {override fun onSuccess() {println("文本弹幕 '$text' 发送成功")}override fun onFailure(code: Int, desc: String) {println("文本弹幕发送失败: $desc")}})}}
接口参数
参数 | 类型 | 描述 |
text | String? | 要发送的文本内容。 |
extensionInfo | Map<String, String>? | 附加的扩展信息,可用于业务自定义。 |
completion | CompletionHandler? | 发送完成后的回调,包含成功或失败的结果。 |
步骤4:发送自定义弹幕
发送一条包含自定义业务逻辑的消息,例如礼物、点赞或游戏化互动指令。这条消息对其他客户端来说是透明的,需要业务层自行解析。
class BarrageManager(private val liveId: String) {// 发送一条自定义弹幕,例如用于发送礼物fun sendGiftMessage(giftId: String, giftCount: Int) {// 1. 定义一个能识别业务的IDval businessId = "live_gift"// 2. 将业务数据编码为 JSON 字符串val giftData = mapOf("gift_id" to giftId,"gift_count" to giftCount)val jsonString = try {// 使用 Gson 或其他 JSON 库进行序列化// 这里假设使用 GsonGson().toJson(giftData)} catch (e: Exception) {return}// 3. 调用核心API发送自定义消息barrageStore.sendCustomMessage(businessId,jsonString,object : CompletionHandler {override fun onSuccess() {println("礼物消息(自定义弹幕)发送成功")}override fun onFailure(code: Int, desc: String) {println("礼物消息发送失败: $desc")}})}}
接口参数
参数 | 类型 | 描述 |
businessId | String | 业务唯一标识符,例如 "live_gift",用于接收端区分不同的自定义消息。 |
data | String | 业务数据,通常为 JSON 格式的字符串。 |
completion | CompletionHandler? | 发送完成后的回调。 |
步骤5:在本地插入提示消息
在当前用户的消息列表中插入一条本地消息,这条消息不会被发送到直播间的其他用户。这非常适合用来显示系统欢迎、警告或操作提示等信息。
class BarrageManager(private val liveId: String) {// 在本地消息列表中插入一条欢迎提示fun showWelcomeMessage(user: LiveUserInfo) {// 1. 创建一条 Barrage 消息val welcomeTip = Barrage(liveID = liveId,messageType = BarrageType.TEXT, // 可以复用 text 类型来显示textContent = "欢迎 ${user.userName} 进入直播间!",sender = LiveUserInfo() // sender 可以留空或设置为一个系统用户的标识)// 2. 调用API将其插入本地列表barrageStore.appendLocalTip(welcomeTip)}}
接口参数
参数 | 类型 | 描述 |
message | Barrage | 要在本地插入的消息对象。SDK 会将此消息对象追加到 BarrageState 的 messageList 中。 |
步骤6:管理用户发言(禁言与解禁)
作为主播或管理员,您可以对直播间内的用户发言权限进行管理,维护健康的社区氛围。
禁止/解禁单个用户发言
通过
LiveAudienceStore 中的 disableSendMessage 接口来实现对指定用户的禁言或解禁。此状态会被持久化,即使用户重新进入直播间,禁言状态依然有效。import io.trtc.tuikit.atomicxcore.api.*import io.trtc.tuikit.atomicxcore.api.live.LiveAudienceStore// 1. 获取与当前直播间绑定的 LiveAudienceStore 实例val audienceStore = LiveAudienceStore.create(liveId)// 2. 定义要操作的用户ID和禁言状态val userIdToMute = "user_id_to_be_muted"val shouldDisable = true // true为禁言, false为解禁// 3. 调用接口执行操作audienceStore.disableSendMessage(userIdToMute,shouldDisable,object : CompletionHandler {override fun onSuccess() {println("${if (shouldDisable) "禁言" else "解禁"}用户 $userIdToMute 成功")}override fun onFailure(code: Int, desc: String) {println("操作失败: $desc")}})
开启/关闭全体禁言
要对直播间内所有用户(通常不包括主播自己)进行禁言,您需要通过 LiveListStore 更新直播间信息来实现。
import io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.live.LiveInfoimport io.trtc.tuikit.atomicxcore.api.live.LiveListStore// 1. 获取 LiveListStore 单例val liveListStore = LiveListStore.shared()// 2. 获取当前直播间信息,并修改全体禁言状态val currentLiveInfo = liveListStore.liveState.currentLive.value.copy(isMessageDisable = true // true为全体禁言, false为关闭)// 3. 调用更新接口,并指定修改的标志位liveListStore.updateLiveInfo(currentLiveInfo,listOf(LiveInfo.ModifyFlag.IS_MESSAGE_DISABLE),object : CompletionHandler {override fun onSuccess() {println("全体禁言状态更新成功")}override fun onFailure(code: Int, desc: String) {println("操作失败: $desc")}})
功能进阶:高并发场景下的性能优化
当您使用
BarrageStore 构建弹幕功能后,本章将指导您如何处理更复杂的业务场景,确保它能在真实、复杂的高并发直播场景下,依然为用户提供流畅、稳定的体验。本章将围绕三个核心业务场景,为您提供明确的优化方案和代码示例。场景一:应对热门直播间的"弹幕风暴"
场景描述
在一场热门活动中,直播间涌入大量观众,弹幕以每秒数十条的频率刷新。
技术挑战
SDK会以极高频率返回完整的弹幕列表。如果每次都调用
adapter.notifyDataSetChanged(),主线程会被密集的 UI 布局和渲染操作阻塞,导致界面卡顿。优化方案:批处理与流量削峰 (Batch Processing & Debouncing)
不必响应每一次的数据更新,而是设定一个时间阈值(例如300毫秒)。只在距离上次UI刷新超过这个阈值后,才执行下一次刷新操作。这能将每秒数十次的
notifyDataSetChanged() 调用,降低到每秒3-4次,极大提升流畅度。代码示例
创建一个
BarrageUIManager 类,它内置一个缓冲区和一个定时器,专门负责将数据批量更新到 RecyclerView。import android.os.Handlerimport androidx.recyclerview.widget.RecyclerViewimport io.trtc.tuikit.atomicxcore.api.barrage.Barrageprivate const val UPDATE_DURATION_MS = 300Lclass BarrageUIManager() {private var timestampOnLastUpdate = 0Lprivate var dataSource: ArrayList<Barrage> = ArrayList()private val updateViewTask = Runnable { notifyDataSetChanged() }private val handler = Handler()private val adapter = BarrageAdapter(dataSource)// 外部高频调用此方法,传入最新的全量列表fun update(newList: List<Barrage>) {handler.removeCallbacks(updateViewTask)dataSource.clear()dataSource.addAll(newList)// 刷新频率小于 0.3 秒,不做刷新if (System.currentTimeMillis() - timestampOnLastUpdate < UPDATE_DURATION_MS) {handler.postDelayed(updateViewTask, UPDATE_DURATION_MS)return}}private fun notifyDataSetChanged() {adapter.notifyDataSetChanged()timestampOnLastUpdate = System.currentTimeMillis()}}class BarrageAdapter(dataSource: ArrayList<Barrage>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {//这里实现adapter能力}
场景二:保障长时间直播的内存稳定性
场景描述
您的应用需要支持数小时乃至全天候的"不间断直播",例如游戏直播或慢直播。在此期间,App 必须保持稳定运行,不能因为长时间运行而意外退出。
技术挑战
SDK 返回的全量
messageList 会在长时间直播中无限增长,即使 UI 层做了节流,数据层持有的这个巨大数组也会持续侵占内存,最终导致应用闪退。优化方案:固定容量的循环数组 (Circular Buffer)
只让您自己的数据源(DataSource)持有有限数量的消息。无论 SDK 返回的全量列表有多大,您的应用只截取其中最新的部分用于显示。
代码示例
在接收到 SDK 的全量列表后,只取最新的500条(或您定义的其他数量)来更新 UI。
class BarrageUIManager(private val recyclerView: RecyclerView) {private val capacity: Int = 500 // 客户端只保留最新的500条消息// ...(其他代码同上)...private fun refreshUIIfNeeded() {val fullList = this.latestMessageList ?: returnval adapter = recyclerView.adapter ?: returnthis.latestMessageList = null// 关键点:截取最新的N条消息val cappedList = fullList.takeLast(capacity)dataSource.clear()dataSource.addAll(cappedList)adapter.notifyDataSetChanged()}}
场景三:渲染包含用户等级、徽章的复杂弹幕样式
场景描述
为了增强直播氛围和付费用户荣誉感,弹幕需要展示丰富的视觉元素,例如混排用户名、用户等级图标、粉丝徽章和消息内容。
技术挑战
渲染包含多图文、自定义字体或复杂布局的视图,比渲染纯文本耗时更长。在列表中高频渲染这些复杂视图,会加重主线程的计算压力,导致列表滚动时出现卡顿。
优化方案:异步绘制 (Asynchronous Drawing)
将视图内容的绘制过程从主线程剥离,放到后台线程去完成。主线程只负责最终的"贴图"操作,即显示已经绘制好的位图。这极大地减轻了主线程的计算压力。
代码示例
在您的自定义
RecyclerView.ViewHolder 中,通过使用 AsyncLayoutInflater 或预加载机制,来指示系统尽可能地在后台线程完成绘制任务。import android.view.LayoutInflaterimport androidx.asynclayoutinflater.view.AsyncLayoutInflaterimport androidx.recyclerview.widget.RecyclerView// 在你的自定义弹幕ViewHolder中class BarrageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {// ... (TextView, ImageView等子视图的声明)companion object {fun create(parent: ViewGroup): BarrageViewHolder {// 使用异步布局加载器来减少主线程阻塞val inflater = AsyncLayoutInflater(parent.context)val view = LayoutInflater.from(parent.context).inflate(R.layout.item_barrage, parent, false)return BarrageViewHolder(view)}}fun bind(viewModel: BarrageViewModel) {// 在这里设置你的TextView文本、ImageView图片等// 由于使用了异步布局加载,这些内容的最终渲染会尽量在后台完成}// 追求极致性能的进阶方案:完全手动绘制// 如果需要更精细的控制,可以使用Canvas手动绘制文本和图片// 结合后台线程生成Bitmap,然后在主线程的onDraw方法中绘制它,可以实现完全的异步渲染}
API 文档
常见问题
除了基础的文本弹幕,我们还希望实现"彩色弹幕"、"礼物弹幕"等更丰富的样式,该如何实现?
这是通过自定义消息
sendCustomMessage 来实现的。BarrageStore 不会限制您的业务想象力。实现思路
1. 定义数据结构: 与您的客户端和服务器团队共同定义好自定义消息的 JSON 结构。例如,一条彩色弹幕可以这样定义:
{ "type": "colored_text", "text": "这是一条彩色弹幕!", "color": "#FF5733" }
2. 发送端: 在发送时,将这个 JSON 结构转换为字符串,并通过
sendCustomMessage 的 data 参数发送出去。businessID 可以设置为一个能代表您业务的唯一标识,例如 "barrage_style_v1"。3. 接收端: 在接收到弹幕消息后,检查其
messageType 是否为 BarrageType.CUSTOM 以及 businessID 是否匹配。如果匹配,则解析 data 字符串(通常是解析 JSON),根据解析出的数据(例如 color、text)来渲染您的自定义UI样式。我在不同的类、不同的文件中都调用了 BarrageStore.create(liveID = "some_id"),这会创建出多个实例导致混乱吗?
完全不会。
AtomicXCore 内部机制会确保只要您传入的 liveID 相同,获取到的永远是同一个与该直播间绑定的 BarrageStore 实例。您可以在需要的地方随用随取,无需手动管理单例。为什么我调用了 sendTextMessage,但是在消息列表中看不到我发送的消息?
请按以下步骤排查:
1. 检查 completion 回调:sendTextMessage 方法有一个完成回调。请检查回调返回的结果是成功还是失败。如果失败,错误信息会明确指出问题所在(例如"您已被禁言"、"网络错误"等)。
2. 确认订阅时机:确保您对 barrageStore.barrageState.messageList 的订阅发生在该 liveID 对应的直播开始之后。如果在加入直播房间之前就开始监听,可能会错过部分消息。
3. 检查 liveID:确认您在创建 BarrageStore 实例、加入直播房间、以及发送消息时使用的 liveID 完全一致,包括大小写。
4. 网络问题:检查设备当前的网络连接是否正常。消息发送依赖于网络。
新观众进入直播间时,如何让他们看到加入前的历史弹幕消息?
AtomicXCore 支持拉取历史弹幕消息,但这需要您在服务端控制台进行一项简单的配置。配置完成后,SDK会自动处理后续的一切,您无需编写额外的代码。步骤1:在 IM 控制台进行配置
1. 登录您的 即时通讯 IM 控制台。
2. 在左侧导航栏,按照路径消息服务 Chat > 功能配置 > 群组配置 > 群功能配置 > 直播群新成员查看入群前消息量配置进行导航。

3. 修改"新成员可查看最近消息数",最大支持 50 条。
步骤2:客户端无感获取
完成上述配置后,您的客户端代码无需做任何改动。
当新用户加入直播间时,
AtomicXCore 的底层会自动拉取您配置的历史消息数量。这些历史消息会和实时消息一样,通过您已实现的 BarrageStore.barrageState.messageList 订阅通道推送给您的 UI 层。您的应用会像接收实时弹幕一样,自然地接收并展示这些历史弹幕。