最近公司做新需求, 原來(lái)用的老彈幕庫(kù), 已經(jīng)無(wú)法滿足需要. 迫不得已自己寫了一套彈幕庫(kù)OCBarrage. 這套彈幕庫(kù)輕量, 可拓展, 高度自定義, 超高性能, 簡(jiǎn)單易上手.
無(wú)論哪家公司軟件的性能絕對(duì)是衡量APP好壞的重要指標(biāo). 之前有一次開(kāi)會(huì), 我們領(lǐng)導(dǎo)說(shuō):"我們寫的東西, 有哪些是可以拿的出手阻问,讓我們引以為豪的?". 之前還真就得想一會(huì)兒, 現(xiàn)在可以毫不猶豫的說(shuō)我們的彈幕庫(kù)絕對(duì)是一個(gè)好家伙.
做直播類軟件核心功能一個(gè)是播放器另一個(gè)就是彈幕了. 現(xiàn)在iOS開(kāi)源的彈幕庫(kù)中性能好的不多, 彈幕量稍微大一點(diǎn), 或者彈幕稍微復(fù)雜一點(diǎn), 就會(huì)出現(xiàn)卡頓雅潭,這與它們的底層實(shí)現(xiàn), 設(shè)計(jì)策略以及你的使用方法都有關(guān)系. 關(guān)鍵是動(dòng)畫單一避诽,無(wú)法定制,滿足不了動(dòng)畫的多樣化需求虚倒!OCBarrage正是為解決這些問(wèn)題而生的!
OCBarrage底層使用Core Animation驅(qū)動(dòng), Core Graphics繪圖, GPU渲染, 性能極高, 哪怕是同時(shí)渲染5000條彈幕也不會(huì)感覺(jué)到卡頓. 開(kāi)源地址:https://github.com/w1531724247/OCBarrage
(以下測(cè)試基于iPhone7真機(jī))
對(duì)于全民直播這樣的平臺(tái)來(lái)說(shuō),在大主播高峰時(shí)期的彈幕量是很大的钱磅,特別是當(dāng)主播說(shuō)一句:“我們現(xiàn)在開(kāi)始彈幕抽獎(jiǎng)”。彈幕量瞬間就會(huì)漲的很高似枕!所以對(duì)彈幕這一塊的要求還是蠻高的.
性能優(yōu)化原理
彈幕渲染時(shí)比較耗性能的點(diǎn):
-
彈幕陰影
主播在戶外直播時(shí)偶爾會(huì)有白色的背景, 而彈幕文字的顏色也是白色的, 這個(gè)時(shí)候彈幕飄到直播畫面的白色區(qū)域會(huì)導(dǎo)致看不到文字內(nèi)容. 為了解決這個(gè)問(wèn)題我們通常會(huì)給彈幕文字添加一個(gè)隱影.以防止這種情況的發(fā)生. 然而別小看這幾個(gè)像素陰影, 它可是性能消耗的大戶. 哪怕是用GPU渲染因?yàn)槭莿?dòng)態(tài)的實(shí)時(shí)的所以也相當(dāng)吃性能. 在實(shí)驗(yàn)的過(guò)程中發(fā)現(xiàn)如果有文字陰影幾十條彈幕就會(huì)出現(xiàn)彈幕卡頓, 結(jié)果就是彈幕抖動(dòng)一跳一跳的.
解決辦法就是用NSAttributeString
的NSStrokeColorAttributeName
屬性設(shè)置文字的輪廓顏色替換文字陰影.效果對(duì)比如下:
都能解決我們的問(wèn)題, 但是性能差的可不是一丁半點(diǎn).
-
用CALayer替代UIView展示
與UIView相比CALayer更輕量. 性能更好.系統(tǒng)提供的組件為了保證其通用性, 難免有些冗余.這就是我們優(yōu)化的空間.
-
彈幕文字下面的漸變色背景
彩色彈幕下面的漸變色背景如果用CAGradientLayer實(shí)現(xiàn)也是比較耗性能的, 但是如果是用圖片呈現(xiàn)的話效果就會(huì)好的多, 但是不夠靈活, 沒(méi)關(guān)系, 我們都一并解決了.
-
將內(nèi)容合成一張圖片展現(xiàn)
將所有的內(nèi)容呈現(xiàn)在layer上并布局好位置以后將所有的內(nèi)容合成一張圖片展現(xiàn)在barrageCell的layer上, 并刪除所有的子subview及sublayer, 以提高性能.
效果演示
使用用法
- 第一步:
為新的彈幕類型新建一個(gè)數(shù)據(jù)模型 例如:OCBarrageWalkBannerDescriptor
. 這個(gè)類必須繼承自OCBarrageDescriptor
類.
這樣就創(chuàng)建新的彈幕類型的數(shù)據(jù)模型類, 我們可以在這個(gè)類里面添加新的彈幕屬性例如:
bannerLeftImageSrc
, bannerMiddleColor
, bannerRightImageSrc
等等.
- 第二步:
為新的彈幕類型創(chuàng)建建一個(gè)數(shù)據(jù)展示視圖例如:OCBarrageWalkBannerCell
. 這個(gè)新的彈幕類型的展示視圖必須繼承自OCBarrageTextCell
類.
在這個(gè)新的展示視圖里我們可以添加展示相應(yīng)數(shù)據(jù)的子視圖,例如:leftImageView
, middleImageView
, rightImageView
.
并為這個(gè)新的視圖類添加一個(gè)相應(yīng)的數(shù)據(jù)模型類的屬性OCBarrageWalkBannerDescriptor *walkBannerDescriptor
來(lái)傳遞數(shù)據(jù).
- 第三步:
重寫新視圖OCBarrageWalkBannerCell
的- (void)setBarrageDescriptor:(OCBarrageDescriptor *)barrageDescriptor
方法. 并只能在這個(gè)方法里為walkBannerDescriptor
屬性賦值, 在這個(gè)方法里必須要調(diào)用[super setBarrageDescriptor:barrageDescriptor]
方法, 不然barrageDescriptor
屬性將沒(méi)有值, 并且部分屬性設(shè)置將不生效.OCBarrageCell
本身有一個(gè)barrageDescriptor
屬性引用數(shù)據(jù)模型. 但是為了方便拓展我們選擇在第二步里為OCBarrageWalkBannerCell
添加一個(gè)新的數(shù)據(jù)屬性walkBannerDescriptor
. 實(shí)質(zhì)上OCBarrageWalkBannerCell
的barrageDescriptor
屬性和walkBannerDescriptor
指向的是同一個(gè)walkBannerDescriptor
對(duì)象.
- 第四步:
重寫新視圖OCBarrageWalkBannerCell
的- (void)updateSubviewsData
方法. 渲染引擎在渲染彈幕視圖之前會(huì)自動(dòng)調(diào)用這個(gè)方法. 我們可以在這個(gè)方法里為子視圖設(shè)置數(shù)據(jù)
.
- 第五步:
在第四步設(shè)置好子視圖的數(shù)據(jù)之后就可以計(jì)算并設(shè)置子視圖的大小和位置.重寫- (void)layoutContentSubviews
方法, 并在這個(gè)方法里布局子視圖的位置.渲染引擎會(huì)在調(diào)用- (void)updateSubviewsData
方法之后自動(dòng)調(diào)用- (void)layoutContentSubviews
方法, 這個(gè)方法必須在主線程執(zhí)行.
- 第六步:
在布局好子視圖的位置之后, 如果想要提高性能可以調(diào)用- (void)convertContentToImage
方法, 將可以圖像化的視圖合成一張圖片展示在cell的layer上, 渲染引擎會(huì)在調(diào)用- (void)layoutContentViews
方法之后自動(dòng)調(diào)用- (void)convertContentToImage
方法, 這個(gè)方法必須在主線程執(zhí)行.
如果不想將子視圖的內(nèi)容轉(zhuǎn)化成圖片只需重寫- (void)convertContentToImage
并留空即可:
- 第七步:
如果想要進(jìn)一步優(yōu)化內(nèi)存和性能, 可以重寫- (void)removeSubViewsAndSublayers
方法, 刪除之前添加的的subView和sublayer, 并將子視圖置為nil
.
如果既想提高性能, 又有一些無(wú)法圖片化的內(nèi)容(例如:gif)需要展示, 可以重寫- (void)removeSubViewsAndSublayers
方法但不調(diào)用[super removeSubViewsAndSublayers]
方法, 并選擇性的刪除一些子視圖, 保留一些子視圖.
如果不想刪除子視圖, 只需重寫- (void)removeSubViewsAndSublayers
方法并留空即可:
當(dāng)然寫到這里依然還有優(yōu)化的空間, 后續(xù)會(huì)繼續(xù)優(yōu)化, 歡迎各位仁人志士共同探討指點(diǎn).
開(kāi)源地址:https://github.com/w1531724247/OCBarrage
補(bǔ)充說(shuō)明:
cell會(huì)在動(dòng)畫執(zhí)行完之后調(diào)用- (void)prepareForReuse
, 在數(shù)據(jù)設(shè)置并布局完準(zhǔn)備展示的時(shí)候調(diào)用- (void)removeSubViewsAndSublayers
, 如果在- (void)removeSubViewsAndSublayers
里將子視圖刪除了, 下次重用的時(shí)候要在- (void)prepareForReuse
里重新加一下子視圖
想知道實(shí)際效果如何嗎?趕快掃碼下載體驗(yàn)吧!