BarrageStore module from the AtomicXCore framework.
BarrageStore delivers a complete barrage solution for your live streaming app. Core features include:Core Concept | Type | Core Responsibility & Description |
data class | Represents a single barrage message. Contains sender info (sender), message content (textContent or data), and message type (messageType). | |
data class | Represents the current state of the barrage module. The main property, messageList, is a StateFlow containing all barrage messages for the current live room, ordered chronologically. This serves as the UI data source. | |
abstract class | The main manager for barrage features. Use it to send messages (sendTextMessage, sendCustomMessage) and subscribe to barrage message updates via its state property. |
BarrageStore instance bound to the current live room's liveId, and subscribe to receive the latest barrage message list in real time.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)// Expose the [full] message list as a Flow for the UI layer to subscribe toprivate val _messagesFlow = MutableStateFlow<List<Barrage>>(emptyList())val messagesFlow: StateFlow<List<Barrage>> = _messagesFlow.asStateFlow()init {// Start listening for barrage messages immediately after initializationsubscribeToBarrageUpdates()}private fun subscribeToBarrageUpdates() {scope.launch {barrageStore.barrageState.messageList.collect { messageList ->// When messageList updates, pass the new list to the UI layer via Flow// Note: This is the [complete list], including all historical messages_messagesFlow.value = messageList}}}}
sendTextMessage to broadcast a plain text message to all users in the live room.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)// Send a text barragefun sendTextMessage(text: String) {// We recommend adding a non-empty check to avoid sending invalid messagesif (text.isEmpty()) {return}// Call the core API to send the messagebarrageStore.sendTextMessage(text,mapOf("custom key" to "custom value",),object : CompletionHandler {override fun onSuccess() {println("Text barrage '$text' sent successfully")}override fun onFailure(code: Int, desc: String) {println("Failed to send text barrage: $desc")}})}}
Parameter | Type | Description |
text | String? | The text content to send. |
extensionInfo | Map<String, String>? | Additional data for business customization. |
completion | CompletionHandler? | Callback invoked after sending, indicating success or failure. |
businessId and data.class BarrageManager(private val liveId: String) {// Send a custom barrage, for example, to send a giftfun sendGiftMessage(giftId: String, giftCount: Int) {// 1. Define a business-recognizable IDval businessId = "live_gift"// 2. Encode business data as a JSON stringval giftData = mapOf("gift_id" to giftId,"gift_count" to giftCount)val jsonString = try {// Use Gson or another JSON library for serialization// Here we assume Gson is usedGson().toJson(giftData)} catch (e: Exception) {return}// 3. Call the core API to send the custom messagebarrageStore.sendCustomMessage(businessId,jsonString,object : CompletionHandler {override fun onSuccess() {println("Gift message (custom barrage) sent successfully")}override fun onFailure(code: Int, desc: String) {println("Failed to send gift message: $desc")}})}}
Parameter | Type | Description |
businessId | String | Unique business identifier (e.g., "live_gift"). Used by receivers to distinguish custom messages. |
data | String | Business data, typically a JSON string. |
completion | CompletionHandler? | Callback after sending. |
class BarrageManager(private val liveId: String) {// Insert a welcome tip into the local message listfun showWelcomeMessage(user: LiveUserInfo) {// 1. Create a Barrage messageval welcomeTip = Barrage(liveID = liveId,messageType = BarrageType.TEXT, // You can reuse the text type for displaytextContent = "Welcome ${user.userName} to the live room!",sender = LiveUserInfo() // sender can be left empty or set to a system user identifier)// 2. Call the API to append it to the local listbarrageStore.appendLocalTip(welcomeTip)}}
Parameter | Type | Description |
message | Barrage | The message object to insert locally. The SDK appends this to messageList in BarrageState. |
disableSendMessage method in LiveAudienceStore to mute or unmute a user. This state persists even if the user rejoins the live room.import io.trtc.tuikit.atomicxcore.api.*import io.trtc.tuikit.atomicxcore.api.live.LiveAudienceStore// 1. Get the LiveAudienceStore instance bound to the current live roomval audienceStore = LiveAudienceStore.create(liveId)// 2. Define the user ID to operate on and the mute statusval userIdToMute = "user_id_to_be_muted"val shouldDisable = true // true to mute, false to unmute// 3. Call the interface to perform the operationaudienceStore.disableSendMessage(userIdToMute,shouldDisable,object : CompletionHandler {override fun onSuccess() {println("${if (shouldDisable) "Muted" else "Unmuted"} user $userIdToMute successfully")}override fun onFailure(code: Int, desc: String) {println("Operation failed: $desc")}})
LiveListStore.import io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.live.LiveInfoimport io.trtc.tuikit.atomicxcore.api.live.LiveListStore// 1. Get the LiveListStore singletonval liveListStore = LiveListStore.shared()// 2. Get the current live room info and modify the global mute statusval currentLiveInfo = liveListStore.liveState.currentLive.value.copy(isMessageDisable = true // true to enable global mute, false to disable)// 3. Call the update interface and specify the modification flagliveListStore.updateLiveInfo(currentLiveInfo,listOf(LiveInfo.ModifyFlag.IS_MESSAGE_DISABLE),object : CompletionHandler {override fun onSuccess() {println("Global mute status updated successfully")}override fun onFailure(code: Int, desc: String) {println("Operation failed: $desc")}})
BarrageStore, use the following strategies to ensure smooth and stable user experiences in high-concurrency live streaming scenarios. This section provides optimization strategies and code samples for three core business scenarios.adapter.notifyDataSetChanged() on every update, the main thread may be blocked by intensive UI layout and rendering, causing UI lag.notifyDataSetChanged()calls per second to just 3–4, greatly improving smoothness.BarrageUIManager class with a buffer and timer for batch updating data to the 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)// This method is called frequently from outside, passing in the latest full listfun update(newList: List<Barrage>) {handler.removeCallbacks(updateViewTask)dataSource.clear()dataSource.addAll(newList)// If the refresh interval is less than 0.3 seconds, do not refreshif (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>() {// Implement adapter capabilities here}
messageList that grows indefinitely during long live streams. Even if the UI layer throttles updates, a large array in the data layer will continue consuming memory and may eventually cause the app to crash.class BarrageUIManager(private val recyclerView: RecyclerView) {private val capacity: Int = 500 // Only retain the latest 500 messages// ... (other code as above) ...private fun refreshUIIfNeeded() {val fullList = this.latestMessageList ?: returnval adapter = recyclerView.adapter ?: returnthis.latestMessageList = null// Only take the latest N messagesval cappedList = fullList.takeLast(capacity)dataSource.clear()dataSource.addAll(cappedList)adapter.notifyDataSetChanged()}}
RecyclerView.ViewHolder, use AsyncLayoutInflater or a preloading mechanism to perform rendering tasks on a background thread whenever possible.import android.view.LayoutInflaterimport androidx.asynclayoutinflater.view.AsyncLayoutInflaterimport androidx.recyclerview.widget.RecyclerView// In your custom Barrage ViewHolderclass BarrageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {// ... (Declarations for TextView, ImageView, etc.)companion object {fun create(parent: ViewGroup): BarrageViewHolder {// Use async layout inflater to reduce main thread blockingval inflater = AsyncLayoutInflater(parent.context)val view = LayoutInflater.from(parent.context).inflate(R.layout.item_barrage, parent, false)return BarrageViewHolder(view)}}fun bind(viewModel: BarrageViewModel) {// Set your TextView text, ImageView images, etc. here// With async layout inflation, final rendering is done on a background thread whenever possible}// Advanced performance optimization: fully manual drawing// For finer control, use Canvas to manually draw text and images// Generate the Bitmap on a background thread, then draw it in the main thread's onDraw method for fully asynchronous rendering}
sendCustomMessage. { "type": "colored_text", "text": "This is a colored bullet comment!", "color": "#FF5733" }
data parameter of sendCustomMessage. The businessID can be set to a unique identifier for your use case, such as barrage_style_v1.BarrageType.CUSTOM and whether the businessID matches. If so, parse the data string (usually as JSON), and render your custom UI style based on the parsed data (such as color, text).liveID, you always get the same BarrageStore instance for that live room. You do not need to manage singletons manually.sendTextMessage method provides a completion callback. Check whether the callback indicates success or failure. If it fails, the error message will indicate the problem (such as "You have been muted," "Network error," etc.).barrageStore.barrageState.messageList after the live session for the corresponding liveID has started. If you start listening before joining the live room, you may miss some messages.Feedback