暫存和分發(fā):LNDanmakuDispatcher

這個(gè)文章的前置文章:LNDanmakuMaster

Dispatcher工作方式

Dispatcher的工作方式非常像配貨站,通常有閑置卡車的司機(jī)會將自己的卡車信息登記在配貨站,需要運(yùn)送屋子的雇主把貨物、目的地等信息登記在配貨站,然后由專人將一些順路的貨物分配到一輛卡車上,這輛卡車裝滿了就發(fā)車;這種配貨方式可以使運(yùn)輸資源得到最大限度利用。

Dispatcher的工作方式與配貨站十分類似,一個(gè)Dispatcher通常會管理多條軌道,內(nèi)置一個(gè)隊(duì)列存儲彈幕,彈幕在這里就代表了貨物,軌道代表了卡車;Dispatcher會在時(shí)鐘的驅(qū)動下不斷check那些空閑的軌道,當(dāng)某個(gè)軌道同時(shí)符合空閑度要求和Dispatcher策略時(shí),隊(duì)首的彈幕會被添加到這條軌道上。

Dispatcher同時(shí)兼顧了自身的分配策略和軌道的空閑度,優(yōu)先級:空閑度 > 分配策略,例如:在寬松策略下,同時(shí)有多個(gè)軌道都可以播放隊(duì)首的彈幕時(shí),Dispatcher會選擇當(dāng)前最空閑的那個(gè)軌道來放置這條彈幕,這個(gè)空閑度數(shù)值是由TrackController根據(jù)當(dāng)前自身的狀態(tài)決定,如:條形軌道的最后一條彈幕的剩余存活時(shí)間就代表了這條軌道的空閑度。

Queue

LNDanmakuQueue是Dispatcher使用到的存儲結(jié)構(gòu),為了保證按照一定順序添加的彈幕也會按照一定順序播放,同時(shí)也提供一定的容錯(cuò)能力,讓那些因?yàn)檐壍罁砣荒懿シ诺膹椖灰膊粫涣⒓磥G棄掉。
以下是LNDanmakuQueue對外提供的方法列表,包含了一個(gè)Queue結(jié)構(gòu)常規(guī)的push、pop、top、空判斷、最大容量等方外,也包括了額外的清空、包含判斷和移除判斷,提供一個(gè)代理來向外界傳達(dá)自己的操作時(shí)機(jī)。

@interface LNDanmakuDispatchQueue : NSObject
@property (nonatomic, weak) id <LNDanmakuDispatchQueueDelegate> delegate;
@property (nonatomic, assign) NSInteger maxCapacity;
- (void)push:(LNDanmakuAbstractAttributes *)attributes;
- (void)push:(LNDanmakuAbstractAttributes *)attributes priority:(LNDanmakuDispatchQueuePriority)priority;
- (LNDanmakuAbstractAttributes *)top;
- (LNDanmakuAbstractAttributes *)pop;
- (NSArray<LNDanmakuAbstractAttributes *> *)clearQueue;
- (BOOL)contains:(LNDanmakuAbstractAttributes *)attributes;
- (void)remove:(LNDanmakuAbstractAttributes *)attributes;
- (BOOL)isEmpty;
@end

與常規(guī)隊(duì)列提供的push方法稍有不同,這個(gè)隊(duì)列提供了額外的一種按優(yōu)先級的push,使彈幕可以以不同的優(yōu)先級push進(jìn)這個(gè)隊(duì)列,高優(yōu)的彈幕會先于默認(rèn)優(yōu)先級彈幕播放。內(nèi)部依靠兩個(gè)子隊(duì)列實(shí)現(xiàn)了這種優(yōu)先級:

@interface LNDanmakuDispatchQueue ()
@property (nonatomic, strong) LNDanmakuDispatchSubQueue *highQueue;
@property (nonatomic, strong) LNDanmakuDispatchSubQueue *defaultQueue;
@end

在pop的時(shí)候,我們會優(yōu)先檢查高優(yōu)子隊(duì)列的空狀態(tài),如果高優(yōu)隊(duì)列不為空則pop高優(yōu)隊(duì)列,否則pop默認(rèn)優(yōu)先級隊(duì)列,這里截取了pop方法:

- (LNDanmakuAbstractAttributes *)pop
{
    if ([self.highQueue top]) {
        return [self.highQueue pop];
    }
    return [self.defaultQueue pop];
}

這種優(yōu)先級隊(duì)列的用途:我們考慮展示用戶自己發(fā)送的彈幕在本地展示的情景,用戶希望自己發(fā)出的彈幕在第一時(shí)間得到展示,如果將這條彈幕和普通彈幕一樣插入到隊(duì)列中,它可能會在當(dāng)前隊(duì)列中已存在的彈幕都被播完之后才會顯示出來;反之插在隊(duì)首,如果有兩條或以上的高優(yōu)彈幕被插入隊(duì)首,那么對這些高優(yōu)彈幕而言,這個(gè)結(jié)構(gòu)就變成了棧。因此,我們提供了額外的高優(yōu)隊(duì)列來解決這個(gè)問題。
每個(gè)子隊(duì)列內(nèi)部實(shí)現(xiàn)在此不過多贅述,本質(zhì)上就是封裝了NSMutableOrderedSet,然后對外暴露方法。

Dispatcher

LNDanmakuAbstrackDispatcher抽象類中規(guī)定了一個(gè)Dispatcher需要支持的所有能力,以下是抽象類的定義:

@interface LNDanmakuAbstractDispatcher (Override)
//private use
- (void)dispatchNewAttributesToFreeTracks:(NSArray<LNDanmakuAbstractTrackController *> *)trackControllerArray;
//public use
- (void)insertNewAttributes:(NSArray <LNDanmakuAbstractAttributes *> *)newAttributesArray;
- (void)insertHighPriorityAttributes:(NSArray <LNDanmakuAbstractAttributes *> *)highPriorityAttributesArray;
- (void)clear;
- (BOOL)containsAttributes:(LNDanmakuAbstractAttributes *)attributes;
- (void)removeAttributes:(LNDanmakuAbstractAttributes *)attributes;
@end
  • -(void)dispatchNewAttributesToFreeTracks 方法被標(biāo)記了private,意思是,這個(gè)方法只能被LNDanmakuMaster框架內(nèi)部的類調(diào)用,這個(gè)方法翻譯成中文:“我這里有一些軌道,如果你有需要播放的彈幕,請從這些軌道中挑選出一個(gè)放置你的彈幕”。因此,這個(gè)方法只會被Player和TrackGroup調(diào)用,Player和TrackGroup是軌道列表的持有者。
  • 其他的方法和隊(duì)列的方法是大體對應(yīng)的,它們都被標(biāo)記為public,也就是使用者可調(diào)用的方法,當(dāng)然這個(gè)界限并不是絕對的,因此總是有我們預(yù)料不到的場景需要特殊處理。

QA:為什么為dispatcher提供抽象類,雖然大部分場景下的彈幕都是按照順序分發(fā)的,但產(chǎn)品經(jīng)理的意識總會超出常人預(yù)料,我們假設(shè)他們提出一種從給定的彈幕池子里隨機(jī)抽取彈幕進(jìn)行分發(fā)的需求,我們重新實(shí)現(xiàn)一種新的Dispatcher接入后仍然可以讓這個(gè)框架的其他部分正常工作。

梳理一下Dispatcher的工作原理:

  • 使用者通過insertNewAttributes方法將彈幕插入Dispatcher的隊(duì)列。
  • Player受Clock驅(qū)動,調(diào)用dispatchNewAttributesToFreeTracks方法讓Dispatcher做出選擇并放置彈幕。
  • Dispatcher從給定的TrackController列表中挑選出空閑且符合分發(fā)策略的軌道,把隊(duì)首的彈幕放上去。
  • 彈幕被添加到TrackController后,走TrackController的播放流程。

后續(xù)會單獨(dú)有一個(gè)文章介紹三種分發(fā)策略:

typedef NS_ENUM(NSInteger, LNDanmakuDispatchStrategy)
{
    LNDanmakuDispatchStrategyDefault = 0, //Find the first track to insert.
    LNDanmakuDispatchStrategyLowDensity, //Find the most free track to insert.
    LNDanmakuDispatchStrategyMostFastDisplay //Find the track with shortest waiting time.
};

typedef NS_ENUM(NSInteger, LNDanmakuRecoverDispatchStrategy)
{
    LNDanmakuRecoverDispatchStrategyDefault = 0,
    LNDanmakuRecoverDispatchStrategyLowDensity,
    LNDanmakuRecoverDispatchStrategyMostFastDisplay,
};
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容