由于所有的 Player 都有這個邏輯因此可以將這部分再抽象成一個 AbsPlayer:
abstract class AbsPlayerIDataSource, CB : ICallback>
: IPlayer
最后整個 Player 的類圖如下所示:
image.png
這里我們不關(guān)注 Player 的功能具體是如何實現(xiàn)的,比如如何推流,如何拉流,如何進行 RTC 等。畢竟每個項目底層所用的服務(wù)商 sdk 各不相同,技術(shù)實現(xiàn)也不同,因此這里我們只從架構(gòu)的層面去探討。
2、Player 的切換
Player 的切換針對的就是部分場景 RTC,這里我們引入 SwitchablePlayer 的概念專門用于此種場景,而其本身也繼承自 AbsPlayer, 具備 Player 的所有功能。只不過這些功能是通過裝飾者模式由其內(nèi)部真正的 Player 來實現(xiàn),同時增加了 Switch 的能力。再講到 Switch 能力之前先來思考幾個問題。
- 何時觸發(fā) Switch?
- 如何進行 Switch?
- Switch 的目標對象 Player 從何而來?
第一個問題何時觸發(fā) Switch :我們知道只要觸發(fā) Switch 就意味著需要啟動另外的 Player,而啟動 Player 又需要上面提到的 IDataSource,因此我們只需要判斷啟動 Player 所傳入的 IDataSource 類型和當(dāng)前 Player 的 IDataSource 類型是否相同,如果不同便可觸發(fā)。判斷的具體邏輯是對比當(dāng)前 Player 泛型參數(shù)的 IDataSource 類型( AbsPlayer第一個范型參數(shù) )和傳入的 IDataSource 類型來實現(xiàn)。
private fun isSourceMatch(
player: AbsPlayer<IDataSource, ICallback>?,
ds: IDataSource
): Boolean {
if (player == null) {
return false
} else {
val clazz = player::class.java
var type = getGenericSuperclass(clazz) ?: return false
while (Types.getRawType(type) != AbsPlayer::class.java) {
type = getGenericSuperclass(type) ?: return false
}
return if (type is ParameterizedType) {
val args = type.actualTypeArguments
if (args.isNullOrEmpty()) {
false
} else {
Types.getRawType(args[0]).isInstance(ds) && isSameSource(player, ds)
}
} else {
false
}
}
}
第二個問題如何進行 Switch :這個就比較簡單了只需要停止掉當(dāng)前的 Player 再啟動目標 Player 即可。
第三個問題 Switch 的目標對象 Player 從何而來 :SwitchablePlayer 并不清楚業(yè)務(wù)需要哪些 Player ,只是對 Player 功能的一層包裝以及維護 Switch 功能,因此具體的 Player 創(chuàng)建需要由業(yè)務(wù)層來實現(xiàn), SwitchablePlayer 只提供一個獲取 Player 的抽象方法例如:
abstract fun getPlayer(ds: IDataSource): AbsPlayer<out IDataSource, out ICallback>?
另外由于進行 Switch 的時候會停止掉當(dāng)前的 Player,而被停止的 Player 是否能復(fù)用,如果能復(fù)用則可以將其緩存起來,下次使用優(yōu)先從緩存中獲得。整個SwitchablePlayer對應(yīng)的流程如圖所示:
image.png
在使用時調(diào)用者可以根據(jù)自己的業(yè)務(wù)定義相關(guān) Player,例如在直播-> PK 的業(yè)務(wù)中,涉及到兩個 Player 的切換即:LivePlayer 和 PKPlayer
class LivePKSwitchablePlayer : SwitchablePlayer(false) {
override fun getPlayer(ds: IDataSource): AbsPlayer<out IDataSource, out ICallback> {
return when (ds) {
is LiveDataSource -> {
LivePlayer()
}
is PKDataSource -> {
PKPlayer()
}
else -> LivePlayer()
}
}
}
3、流程封裝
對于整個 RTC 流程的封裝需要搞清楚兩件事情:
- RTC 的主體流程是怎樣的
- 業(yè)務(wù)調(diào)用方需要的是什么,關(guān)注的又是什么
由于 RTC 的主體流程和日常打電話相似,所以筆者以此類比,這樣大家更容易理解。下圖所示即為整個通話過程。
搞清楚整個流程后,接下來就是搞清楚第二件事情,業(yè)務(wù)調(diào)用方需要的是什么,關(guān)注的又是什么。結(jié)合上圖來看關(guān)注的大概有三點:
- 第一就是需要具備撥打和掛斷的入口;( Player 的 Start 和 Stop )
- 第二就是要能知道當(dāng)前的通話狀態(tài)比如是否正在連通,是否已經(jīng)接通,是否通話結(jié)束;( Player 的 狀態(tài)維護 )
- 第三就是一些反饋比如對方未接通,對方不在服務(wù)區(qū),手機號是空號等。( Player 的 核心事件回調(diào)即之前提到的 ICallback )
而至于它是如何連通的,底層做了哪些操作,撥打電話的人對此毫不關(guān)心。基于上述我們的整體功能設(shè)計所要關(guān)注的點就有了。
1、通過設(shè)計一個 manager 來管理 Player 并對外暴露 Start 和 Stop 方法。
2、對 Player 進行狀態(tài)維護,并讓其狀態(tài)可被上層監(jiān)聽。
3、Player 的一些核心事件回調(diào)也可被上層監(jiān)聽。
其中第一點和第三點比較簡單,這里就不做過多的贅述。第二點狀態(tài)維護,筆者使用了 StateMachine 狀態(tài)機來實現(xiàn),在不同的狀態(tài)執(zhí)行不同的操作,同時每一種狀態(tài)都對應(yīng)一個狀態(tài)碼,上層可以通過監(jiān)聽狀態(tài)碼來感知狀態(tài)變化。
image.png
狀態(tài)碼和核心事件的設(shè)置這里使用了 LiveData 去處理
class RtcHolder : IRtcHolder {
private val _rtcState = MutableLiveData(RtcStatus.IDLE)
private val _rtcEvent = MutableLiveData(RtcEvent.IDLE)
val rtcState = _rtcState.distinctUntilChanged()
val rtcEvent = _rtcEvent.distinctUntilChanged()
private val callBack = object : IRtcCallBack {
override fun onCurrentStateChange(stateCode: Int) {
_rtcState.value = stateCode
}
override fun onEvent(eventCode: Int) {
_rtcEvent.value = eventCode
}
//......省略其他代碼
}
init {
//上層狀態(tài)監(jiān)聽
rtcState.observeForever {
when (it) {
RtcStatus.CONNECT_END -> {
ToastHelper.showToast("通話結(jié)束")
}
}
}
}
//......省略其他代碼
}
到這里整個腳手架的方案設(shè)計就結(jié)束了,其中服務(wù)商 SDK 封裝部分以及監(jiān)控部分,筆者準備放到下期再來講解。
總結(jié)
本文介紹了 RTC 腳手架產(chǎn)生的背景,并以通俗易懂的方式一步步闡述設(shè)計過程以及最終實現(xiàn)。在此期間發(fā)現(xiàn)問題,解決問題,引出思考。由于受限于篇幅,不能將每一個點都進行詳盡的介紹,有興趣的同學(xué)如有疑問,可以留言,一起探討學(xué)習(xí)。
-
RTC
+關(guān)注
關(guān)注
2文章
619瀏覽量
68685 -
騰訊云
+關(guān)注
關(guān)注
0文章
221瀏覽量
17079
發(fā)布評論請先 登錄
利用愛星物聯(lián)服務(wù)腳手架實現(xiàn)定制服務(wù)

2010年迪拜五大行業(yè)展|迪拜建材展|緊固件|腳手架|建筑五金|衛(wèi)浴|陶瓷|大理石
2011年沙特建材展|吉達建材展|五大行業(yè)展|緊固件|腳手架|玻璃|門窗|
2011年沙特建材展|吉達建材展|五大行業(yè)展|緊固件|腳手架|玻璃|門窗|
懸挑式腳手架監(jiān)理控制要點
鋼腳手架的避雷方法
物聯(lián)網(wǎng)腳手架系統(tǒng)能帶來什么益處
科學(xué)家研發(fā)可溶解的植入骨折的腳手架材料——特殊繃帶
腳手架掛牌方案需要符合哪些層面的規(guī)定
腳手架掛牌一般在哪些情況下使用比較好
關(guān)于針對腳手架掛牌的歸納分析
為何需要腳手架掛牌,它的作用是怎樣的
腳手架掛牌的具體流程是怎樣的
腳手架掛牌是什么,有什么作用
RTC腳手架的設(shè)計與實現(xiàn)(上)

評論