這個文章的前置文章:LNDanmakuMaster
Dispatcher工作方式
Dispatcher的工作方式非常像配貨站携悯,通常有閑置卡車的司機會將自己的卡車信息登記在配貨站赃蛛,需要運送屋子的雇主把貨物道媚、目的地等信息登記在配貨站环形,然后由專人將一些順路的貨物分配到一輛卡車上,這輛卡車裝滿了就發(fā)車磺浙;這種配貨方式可以使運輸資源得到最大限度利用护盈。
Dispatcher的工作方式與配貨站十分類似,一個Dispatcher通常會管理多條軌道闲先,內(nèi)置一個隊列存儲彈幕状土,彈幕在這里就代表了貨物,軌道代表了卡車伺糠;Dispatcher會在時鐘的驅(qū)動下不斷check那些空閑的軌道蒙谓,當某個軌道同時符合空閑度要求和Dispatcher策略時,隊首的彈幕會被添加到這條軌道上训桶。
Dispatcher同時兼顧了自身的分配策略和軌道的空閑度累驮,優(yōu)先級:空閑度 > 分配策略酣倾,例如:在寬松策略下,同時有多個軌道都可以播放隊首的彈幕時谤专,Dispatcher會選擇當前最空閑的那個軌道來放置這條彈幕躁锡,這個空閑度數(shù)值是由TrackController根據(jù)當前自身的狀態(tài)決定,如:條形軌道的最后一條彈幕的剩余存活時間就代表了這條軌道的空閑度置侍。
Queue
LNDanmakuQueue是Dispatcher使用到的存儲結(jié)構(gòu)映之,為了保證按照一定順序添加的彈幕也會按照一定順序播放,同時也提供一定的容錯能力蜡坊,讓那些因為軌道擁塞而不能播放的彈幕也不會被立即丟棄掉杠输。
以下是LNDanmakuQueue對外提供的方法列表,包含了一個Queue結(jié)構(gòu)常規(guī)的push秕衙、pop蠢甲、top、空判斷灾梦、最大容量等方外峡钓,也包括了額外的清空、包含判斷和移除判斷若河,提供一個代理來向外界傳達自己的操作時機能岩。
@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ī)隊列提供的push方法稍有不同,這個隊列提供了額外的一種按優(yōu)先級的push萧福,使彈幕可以以不同的優(yōu)先級push進這個隊列拉鹃,高優(yōu)的彈幕會先于默認優(yōu)先級彈幕播放。內(nèi)部依靠兩個子隊列實現(xiàn)了這種優(yōu)先級:
@interface LNDanmakuDispatchQueue ()
@property (nonatomic, strong) LNDanmakuDispatchSubQueue *highQueue;
@property (nonatomic, strong) LNDanmakuDispatchSubQueue *defaultQueue;
@end
在pop的時候鲫忍,我們會優(yōu)先檢查高優(yōu)子隊列的空狀態(tài)膏燕,如果高優(yōu)隊列不為空則pop高優(yōu)隊列,否則pop默認優(yōu)先級隊列悟民,這里截取了pop方法:
- (LNDanmakuAbstractAttributes *)pop
{
if ([self.highQueue top]) {
return [self.highQueue pop];
}
return [self.defaultQueue pop];
}
這種優(yōu)先級隊列的用途:我們考慮展示用戶自己發(fā)送的彈幕在本地展示的情景坝辫,用戶希望自己發(fā)出的彈幕在第一時間得到展示,如果將這條彈幕和普通彈幕一樣插入到隊列中射亏,它可能會在當前隊列中已存在的彈幕都被播完之后才會顯示出來近忙;反之插在隊首,如果有兩條或以上的高優(yōu)彈幕被插入隊首智润,那么對這些高優(yōu)彈幕而言及舍,這個結(jié)構(gòu)就變成了棧。因此窟绷,我們提供了額外的高優(yōu)隊列來解決這個問題锯玛。
每個子隊列內(nèi)部實現(xiàn)在此不過多贅述,本質(zhì)上就是封裝了NSMutableOrderedSet遭商,然后對外暴露方法犁罩。
Dispatcher
LNDanmakuAbstrackDispatcher抽象類中規(guī)定了一個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 方法被標記了private背苦,意思是肯腕,這個方法只能被LNDanmakuMaster框架內(nèi)部的類調(diào)用献宫,這個方法翻譯成中文:“我這里有一些軌道,如果你有需要播放的彈幕实撒,請從這些軌道中挑選出一個放置你的彈幕”姊途。因此,這個方法只會被Player和TrackGroup調(diào)用知态,Player和TrackGroup是軌道列表的持有者捷兰。
- 其他的方法和隊列的方法是大體對應(yīng)的,它們都被標記為public负敏,也就是使用者可調(diào)用的方法贡茅,當然這個界限并不是絕對的,因此總是有我們預(yù)料不到的場景需要特殊處理其做。
QA:為什么為dispatcher提供抽象類顶考,雖然大部分場景下的彈幕都是按照順序分發(fā)的,但產(chǎn)品經(jīng)理的意識總會超出常人預(yù)料妖泄,我們假設(shè)他們提出一種從給定的彈幕池子里隨機抽取彈幕進行分發(fā)的需求驹沿,我們重新實現(xiàn)一種新的Dispatcher接入后仍然可以讓這個框架的其他部分正常工作。
梳理一下Dispatcher的工作原理:
- 使用者通過insertNewAttributes方法將彈幕插入Dispatcher的隊列蹈胡。
- Player受Clock驅(qū)動渊季,調(diào)用dispatchNewAttributesToFreeTracks方法讓Dispatcher做出選擇并放置彈幕。
- Dispatcher從給定的TrackController列表中挑選出空閑且符合分發(fā)策略的軌道罚渐,把隊首的彈幕放上去却汉。
- 彈幕被添加到TrackController后,走TrackController的播放流程荷并。
后續(xù)會單獨有一個文章介紹三種分發(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,
};