写点什么

anyHouse - Android 仿写 ClubHouse

发布于: 2021 年 04 月 22 日

仿写一个 ClubHouse

相信 ClubHouse 大家都有所耳闻,就是一个主打语音社交,并且是邀请制的 App。在各个领域的大佬带动下,迅速出圈,火遍全球社交媒体。不过目前只有 iOS 版本,并且目前已在中国区下架。

上周咱也试着仿写了一个,从技术角度来说,实现它的语音互动模块,真的很简单,下面是效果图。

效果图



流程图



架构



(图:Google)

代码实现

加入频道

如果是自己创建的房间,则加入频道之前将 RTC 用户角色设置为 HOST ,即为“发言者”。所谓发言者,就是房间内的所有人都能听到 Ta 的声音的人。对于 RTC SDK 而言,HOST 身份就代表一加入频道,默认就会发布自己的音频流。AUDIENCE(听众) 则只订阅 HOST 的音频流。

 //加入RTC/RTM 频道     fun joinChannel() {        RtcManager.instance.joinChannel(            getToken(), getChannelId(), getSelfId(), if (isMeHost()) {                Role.HOST            } else {                Role.AUDIENCE            }        )         RtmManager.instance.joinChannel(getChannelId())    }    
复制代码

听众举手/取消举手

实现这两个功能需要用到 RTM SDK,只需要发送一条 P2P 消息给主持人即可。这里 ClubHouse 有个有意思的地方,一开始做的时候,我以为申请举手后,主持人只需同意或者拒绝就行。但实际上是主持人收到有人举手后,不能直接拒绝或者同意,而是反向邀请这个举手的观众,最终由观众决定到底要不要上台发言。

这样做其实对于隐私保护上是非常正确的。

 //举手    fun raiseHand() {        val json = JSONObject().apply {            put("action", BroadcastCMD.RAISE_HANDS)            put("userName", getSelf()?.userName)            put("avatar", getSelf()?.userIcon)        }        RtmManager.instance.sendPeerMessage(channelInfo.value?.hostId.toString(), json.toString())        updateUserStatusFromHttp(getSelfId(), 1)    }
//取消举手 fun cancleRaiseHand() { val json = JSONObject().apply { put("action", BroadcastCMD.CANCLE_RAISE_HANDS) } RtmManager.instance.sendPeerMessage(channelInfo.value?.hostId.toString(), json.toString()) }
复制代码

邀请上台发言/同意/拒绝

同样的,基于 RTM 实时信令 SDK,像这样观众和主持人的互动实现变得格外的简单。只要双方确定好信令,简单的发送一条 P2P 消息即可实现相关功能。

//邀请上麦说话    fun inviteLine(userId: String) {        val json = JSONObject().apply {            put("action", BroadcastCMD.INVITE_SPEAK)        }        RtmManager.instance.sendPeerMessage(userId, json.toString())    }
//拒绝邀请 fun rejectLine() { val json = JSONObject().apply { put("action", BroadcastCMD.REJECT_INVITE) put("userName", getSelf()?.userName) } RtmManager.instance.sendPeerMessage(channelInfo.value?.hostId.toString(), json.toString()) }
//同意邀请 fun acceptLine() { val json = JSONObject().apply { put("action", BroadcastCMD.ACCEPT_INVITE) } RtmManager.instance.sendPeerMessage(channelInfo.value?.hostId.toString(), json.toString()) }
复制代码

改变身份

当听众收到主持人邀请想上台发言时,该怎么做呢?文章开头有提到:对于 RTC SDK 而言,HOST 身份就代表一加入频道,默认就会发布自己的音频流。AUDIENCE(听众) 则只订阅 HOST 的音频流。所以我们只需要改变听众在 RTC SDK 中的身份,由 AUDIENCE 变成 HOST,即可发布自己的音频流,自己的声音将被房间内所有听众订阅到。

//点击同意邀请并改变 RTC 用户角色channelVM.acceptLine()channelVM.changeRoleToSpeaker()
// 将角色直至为说话者(HOST)fun changeRoleToSpeaker() { RtcManager.instance.changeRoleToSpeaker()}
复制代码

关闭麦克风

因为同时可能由多个发言者,所以一般有人在发言时,用户都会主动关闭自己的麦克风。避免影响到他人发言。关闭麦克风,这里只需调用 muteLocalAudioStream 方法即可。

//本地麦克风静音fun muteLocalAudio(mute:Boolean){    rtcEngine?.let {it.muteLocalAudioStream(mute)}}
复制代码

局部更新麦克风图标

一直没注意过 RecyclerView notifyItemChanged 方法的第二个参数,之前想更新 item 的某一个 View 的时候都是通过 ViewHolder 去找,原来 notifyItemChanged 第二个参数就可以实现 🤣

speakerAdapter.notifyItemChanged(index, SpeakerPayload.AUDIO(it)) override fun convert(holder: BaseViewHolder, item: Speaker, payloads: List<Any>) {        super.convert(holder, item, payloads)        if (payloads.isNullOrEmpty()){            convert(holder,item)            return        }        payloads.forEach {            when(val payload = it as SpeakerPayload){                SpeakerPayload.AUDIO ->{                    holder.setVisible(R.id.muted,!(payload.data as Boolean))                }            }        }    }
复制代码

以上,就是实现一个 ClubHouse 核心语音模块大致的功能点。利用国内一些成熟的音视频 SDK ,实现起来非常简单。比如我所采用的 anyRTC RTC SDK,不限制房间内上麦人数,音质也很好,SDK 也比较稳定,每月还免费送一万分钟~

这个项目整体采用谷歌推荐的架构,页面与数据分离,用到了 ViewModel、LiveData,并且使用 DiffUtil 优化列表,Kotlin 协程优化异步任务等。

已实现功能

  1. 登录获取房间列表

  2. 创建公开/私密类型房间

  3. 主播发布音频/听众订阅音频

  4. 听众举手/取消举手

  5. 主播邀请听众上台

  6. 举手列表

下载体验链接

⏬ 下载体验

🐱 github地址

第三方库

用户头像

实时交互,万物互联! 2020.08.10 加入

实时交互,万物互联,全球实时互动云服务商领跑者!

评论

发布
暂无评论
anyHouse - Android 仿写 ClubHouse