Standalone RTC channels
Standalone channels are for meetings, voice rooms and similar scenarios that don't depend on an IM group: users join with permission from your backend, with no ringing or incoming-call UI. Join and leave with DRClient.instance.channels; audio, video and the video view come from the im-call module. For channel rules, see Channels overview in the server docs; for installing modules, see the Android overview.
| You want to | See |
|---|---|
| Add the modules, request permissions | Dependencies and permissions |
| Join, leave, restore after an app restart | Join, cancel and restore |
| Show members and video | State and video |
| Behavior on sign-out and in the background | Lifecycle and limits |
Dependencies and permissions
- Add
imandim-callat the same version.im-callprovides media for channels automatically; you don't need the system incoming-call options. - Without
im-call,channels.supportedisfalseandjoinfails right away withunsupported, without taking a slot on the server. - Your app requests the microphone permission
RECORD_AUDIOand camera permissionCAMERAthrough Android's permission APIs. The SDK never shows permission prompts itself; without the permission it returnspermission_required. - Turning on the microphone or camera must be triggered by a user action. Viewer-only participants (
subscriber) can't publish, and audio-only channels (audio) can't turn on the camera.
Join, cancel and restore
Use this in a coroutine of a signed-in Activity / Fragment; ticketFromBusinessServer comes from your backend:
import com.deeprespond.im.ChannelJoinOptions
import com.deeprespond.im.DRClient
val channels = DRClient.instance.channels
check(channels.supported)
val handle = channels.join(
"meeting-demo",
ChannelJoinOptions(ticket = ticketFromBusinessServer),
)
// 默认不采集;完成媒体连接和服务端 joined 确认后才返回。
handle.setMicrophoneEnabled(true) // 用户操作且 RECORD_AUDIO 已授权。
handle.setCameraEnabled(true) // video publisher,且 CAMERA 已授权。
handle.leave()- Open channels don't need a
ticket. ChannelJoinOptionsalso hasclientSessionId,microphoneEnabledandcameraEnabled; the last two default tofalse, so joining doesn't turn on the microphone or camera.- When the SDK retries the same join, it reuses the same join identifier, so you never join twice.
- Cancelling the coroutine running
joinreleases the slot it took; even if theleaverequest fails, the local microphone and camera stop.
val existing = channels.currentSession()
if (existing != null && existing.status in setOf("joining", "joined")) {
val restored = channels.resume(existing.channelId, existing.sessionId)
// 恢复只使用原会话,不新占位,也不自动采集。
}currentis this device's current channel handle (aStateFlow);currentSession()asks the server for this sign-in's channel session, for restoring after the app restarts.- Only this sign-in's session can be restored; a different sign-in can't. A session in
leavingmust finish cleaning up first, and one inleftcan't be restored. - IDs are strings; don't convert them to
Intor truncate them.
State and video
The UI observes two StateFlows:
handle.snapshot(ChannelSnapshot):channel,session,members,mediaState,microphoneEnabled,cameraEnabled,error.handle.tracks(List<ChannelTrack>): the audio and video tracks. Remote tracks only appear here once they're confirmed to belong to members allowed to join.
val stateJob = lifecycleScope.launch {
handle.snapshot.collect { snapshot -> renderChannelState(snapshot) }
}
val tracksJob = lifecycleScope.launch {
handle.tracks.collect { tracks ->
val video = tracks.firstOrNull { it.kind == "video" && !it.local }
channelVideoView.bind(handle, video)
}
}
// 页面结束时停止观察,解绑画面,并在可完成清理的协程中 leave。
stateJob.cancel()
tracksJob.cancel()
channelVideoView.unbind()
handle.leave()renderChannelState is your app's render function; channelVideoView is a com.deeprespond.im.call.DRChannelVideoView, supporting FIT / FILL, mirror and bind / unbind. Only show tracks given in tracks; don't assemble video from a username or media of unknown origin.
Lifecycle and limits
- The SDK sends heartbeats and renews the join permission automatically. On a sign-in switch, sign-out or media failure, it stops the local connection.
- The server still checks permission for every join; the media connection's own token refresh doesn't replace that check.
- Join permissions, media tokens and handles are never saved locally.
- Channels don't use the system incoming-call UI (
DRCallUI, Core-Telecom), and there's no ringing or incoming-call notification. - Channels currently only support foreground use. Capturing audio or video continuously in the background needs a foreground service, an ongoing notification and extra platform work, tested on its own.
- Call
leave()when leaving the screen; don't just unbind the video view.
The channel code and automated tests are done, but no APK / AAR containing it has been released yet, and multi-party calls haven't been tested on real devices. For the Java API, see Java; for full signatures, see the API reference; for types, see Data types.
