HarmonyOS分布式協(xié)同演奏技術(shù)實(shí)現(xiàn)路線(Java)
??想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:??
一、寫在前面的話
分布式能力是鴻蒙的一大亮點(diǎn),不管是分布式數(shù)據(jù)庫(kù),還是分布式文件管理,抑或是分布式任務(wù)流轉(zhuǎn),都給我們?nèi)粘5氖褂昧?xí)慣帶來很大的變革。很多時(shí)候我就在想,我們還能怎樣應(yīng)用分布式呢?百思不得其解之時(shí),愛因斯坦的小提琴就莫名其妙地浮現(xiàn)在了腦海里。音樂!一個(gè)好的樂隊(duì)、好的樂團(tuán),不正是由一個(gè)個(gè)小的部分組成的嗎?于是,自由樂隊(duì)就蘊(yùn)育而出了。
本文將重點(diǎn)講述下自由樂隊(duì)分布式協(xié)同演奏的技術(shù)實(shí)現(xiàn)路線,重點(diǎn)講的是思路,講的不好的地方還請(qǐng)大家多多包涵。
二、路線一
采用監(jiān)聽數(shù)據(jù)庫(kù)的方法實(shí)現(xiàn)組網(wǎng)間設(shè)備的協(xié)同演奏。
主要流程如上圖,核心就是分布式狀態(tài)數(shù)據(jù)庫(kù)和分布式音樂演奏數(shù)據(jù)庫(kù),通過監(jiān)聽這兩個(gè)數(shù)據(jù)庫(kù)實(shí)現(xiàn)組網(wǎng)間設(shè)備的組隊(duì)和協(xié)同演奏功能。分布式狀態(tài)數(shù)據(jù)庫(kù)用于組隊(duì)信息的傳輸,分布式協(xié)同演奏數(shù)據(jù)庫(kù)用于樂器模擬按鍵信息的傳輸。
1、分布式音樂演奏數(shù)據(jù)庫(kù)的初始化
如下圖,我們軟件初始化的時(shí)候就會(huì)創(chuàng)建屬于該設(shè)備的DeviceKvStore,并對(duì)其進(jìn)行監(jiān)聽。
同時(shí),我們?cè)贒ataAbility里有個(gè)deviceKvStores用來保存deviceId和DeviceKvStore的對(duì)應(yīng)關(guān)系。
2、判斷是否為本地端
如下圖,我們采用通過intent攜帶的關(guān)鍵字來判斷是否為本地端。
本地端調(diào)起協(xié)同端時(shí),寫入該關(guān)鍵字:
3、點(diǎn)擊事件的處理
如下圖,當(dāng)監(jiān)聽到點(diǎn)擊事件發(fā)生的時(shí)候,統(tǒng)一調(diào)用DataAbility.clickSound(),為防止阻塞,我們使用異步調(diào)用的方式。
而DataAbility.clickSound()則會(huì)通過DeviceId獲取到DeviceKvStore,同時(shí)將按鍵點(diǎn)擊事件的mSrc(即相應(yīng)按鍵的ID)寫入。
?4、DeviceKvStore數(shù)據(jù)庫(kù)的監(jiān)聽
如下圖,首先會(huì)判斷是否為本地端,若為本地端則處理點(diǎn)擊事件。處理點(diǎn)擊事件的核心就是獲取到點(diǎn)擊按鍵的mSrc,首先通過notification.getDeviceId()獲取到DeviceId,然后通過DeviceId去獲取響應(yīng)的DeviceKvStore,然后通過key來拿到mSrc,進(jìn)而調(diào)用DataAbility.playClip()。DataAbility.playClip()就是模擬樂器演奏的相關(guān)函數(shù)。
?5、分布式狀態(tài)數(shù)據(jù)庫(kù)的初始化
6、分布式狀態(tài)數(shù)據(jù)庫(kù)的監(jiān)聽
核心就是獲取此時(shí)退出隊(duì)伍設(shè)備的DeviceId,若當(dāng)前設(shè)備為本地端,則停止對(duì)該設(shè)備進(jìn)行監(jiān)聽,同時(shí)彈出該設(shè)備退出隊(duì)伍的提示;若當(dāng)前設(shè)備為協(xié)同端,同時(shí)發(fā)起退出設(shè)備的DeviceId為當(dāng)前隊(duì)伍的本地端,則表示該隊(duì)伍已解散,當(dāng)前設(shè)備變?yōu)楸镜囟耍瑫r(shí)彈出隊(duì)伍已解散的提示。
值得一提的是,分布式狀態(tài)數(shù)據(jù)庫(kù)是所有設(shè)備初始化的時(shí)候都通過DataAbility.STATE_KEY來監(jiān)聽同一個(gè)分布式狀態(tài)數(shù)據(jù)庫(kù)DeviceKvStore,而分布式音樂演奏數(shù)據(jù)庫(kù)則是每個(gè)設(shè)備都會(huì)根據(jù)自己唯一的DataAbility.storeId來創(chuàng)建分布式音樂演奏數(shù)據(jù)庫(kù)DeviceKvStore,也就是說會(huì)有多個(gè)分布式音樂演奏數(shù)據(jù)庫(kù)。
因此,我們是在DataAbility里通過deviceKvStores來儲(chǔ)存DeviceId和DeviceKvStore的對(duì)應(yīng)關(guān)系。組隊(duì)的實(shí)質(zhì)就是實(shí)現(xiàn)對(duì)相應(yīng)分布式音樂演奏數(shù)據(jù)庫(kù)的監(jiān)聽。多個(gè)DeviceKvStore就能夠?qū)嵗鄠€(gè)KvStoreObserver來對(duì)多個(gè)數(shù)據(jù)庫(kù)進(jìn)行監(jiān)聽,并行進(jìn)行處理,減少并發(fā)帶來的影響,降低協(xié)同演奏的延時(shí)。
7、協(xié)同演奏模擬樂器
協(xié)同端和本地端一樣,當(dāng)監(jiān)聽到點(diǎn)擊事件的時(shí)候,也是調(diào)用DataAbility.clickSound()。同樣在DataAbility.clickSound()里通過DeviceId獲取DeviceKvStore,然后將按鍵點(diǎn)擊事件的mSrc寫入相應(yīng)的DeviceKvStore分布式數(shù)據(jù)庫(kù)。
不同的是協(xié)同端的KvStoreObserver雖然監(jiān)聽到了數(shù)據(jù)變化,但是判斷當(dāng)前設(shè)備為協(xié)同端,則不會(huì)去進(jìn)行模擬樂器演奏的播放。而此時(shí),組隊(duì)本地端也同時(shí)監(jiān)聽到了數(shù)據(jù)變化,進(jìn)而去進(jìn)行模擬樂器演奏的播放。
三、路線二
我們想到的第二種實(shí)現(xiàn)分布式協(xié)同演奏的方法就是類似于Codelabs里面的分布式游戲手柄的寫法(鏈接在文末),本地端調(diào)起協(xié)同端的時(shí)候通過IAbilityConnection進(jìn)行連接。同時(shí),協(xié)同端通過proxy.senDataToRemote()將按鍵信息發(fā)送給本地端,本地端通過onRemoteRequest()來處理協(xié)同端發(fā)來的信息。詳細(xì)講解可以參考Codelabs里面的代碼,同時(shí)有一些包也在Demo里面封裝好了,可以不用重復(fù)造輪子。
具體代碼實(shí)現(xiàn)我之前測(cè)試的時(shí)候都寫好了。BUT,因?yàn)樘?,我們就放棄了這條路線,代碼也給回退了。(也可能是我們的水平有限,代碼優(yōu)化的不是很好)我們當(dāng)時(shí)是先寫的通過訂閱分布式數(shù)據(jù)庫(kù)來實(shí)現(xiàn)協(xié)同演奏的,但是我最開始是只通過訂閱一個(gè)單板本分布式數(shù)據(jù)庫(kù)實(shí)現(xiàn)的協(xié)同演奏,感覺效果不太理想,就嘗試了路線二。嘗試了之后發(fā)現(xiàn),路線二還沒有之前的延遲小呢,就最終確定了采用路線一,同時(shí)后面又對(duì)路線一進(jìn)行了相關(guān)的優(yōu)化,比如采用訂閱分布式狀態(tài)數(shù)據(jù)庫(kù)和多個(gè)分布式音樂演奏數(shù)據(jù)庫(kù)、異步處理點(diǎn)擊事件、線程阻塞等等技術(shù)來降低延遲,最終實(shí)現(xiàn)了至少三個(gè)設(shè)備(當(dāng)時(shí)手上只有三個(gè)設(shè)備)可以協(xié)同演奏一首曲子的程度。
四、總結(jié)
協(xié)同演奏的實(shí)現(xiàn)路線我們研究了兩條,分別是監(jiān)聽數(shù)據(jù)庫(kù)和直接建立連接。兩者都可以實(shí)現(xiàn)功能,但是協(xié)同演奏很重要的一點(diǎn)就是延遲,延遲太大就真的只能聽個(gè)響了。個(gè)人覺得,就目前來看,鴻蒙在分布式減少延遲這塊還是有很長(zhǎng)的路要走的。
Codelabs:??分布式游戲手柄(Java)??。