注意:此文為配合 JPVideoPlayer
version 2.0 版本釋出而寫,如果你想了解 2.0 版本的更新內容和所有實現細節,請點選前往 GitHub。
導言:我幾個月前寫了一個在
UITableView
中滑動UITableViewCell
播放視訊的框架,類似於“新浪微博” 和 “Facebook” 首頁視訊播放。我也為第一版本的JPVideoPlayer
實現寫了兩篇文章:
01、[iOS]仿微博視訊邊下邊播之封裝播放器 講述如何封裝一個實現了邊下邊播並且快取的視訊播放器。
02、[iOS]仿微博視訊邊下邊播之滑動TableView自動播放 講述如何實現在
UITableView
中滑動播放視訊,並且是流暢,不阻塞執行緒,沒有任何卡頓的實現滑動播放視訊。同時也將講述當UITableView
滾動時,以什麼樣的策略,來確定究竟哪一個cell應該播放視訊。
當時匆忙實現功能,沒有仔細斟酌架構的問題,也沒有徹底的實現單個功能的元件化。而且由於架構設計不足帶來一些不可避免的問題。這些問題,1.0 版本的使用者應該有所體會。前段時間獨自回了一趟老家,車程比較長,而且可以專注而不被打擾,在車上寫了一個 2.0 版本。2.0 版本有部分內容模仿了
SDWebImage
的設計。
2.x 版本效果如下:
01.JPVideoPlayer Version 2.0 如何使用?
考慮到 API 和架構都重新設計了,尤其是 API 全部重新設計,1.0 版本的 API 已經不能沿用了,機智的我已經做好了被罵得很慘的心理準備了。
如果你是新的使用者,就不會受之前版本的影響。最終不管你是新使用者還是老使用者,你一定會喜歡這種新的 API 設計的,因為這是目前 iOS 最受歡迎的 API 設計。
Objective-C:
#import <UIView+WebVideoCache.h>
...
NSURL *url = [NSURL URLWithString:@"http://lavaweb-10015286.video.myqcloud.com/%E5%B0%BD%E6%83%85LAVA.mp4"];
[aview jp_playVideoWithURL:url];
複製程式碼
02.JPVideoPlayer Version 2.0 內部執行細節?
很多使用者可能不會有時間去讀原始碼,但是也許會關心 2.0 版本內部實現的一些大的方式,所以這裡我總結了一張 2.0 版本的實現大致結構圖表,如下:
下面我用文字來表述一下框架內部的運作順序:
-
01.提供給外部呼叫的 API 非常簡潔,採用為
UIView
新增分類方法的形式為外界呼叫,只要匯入了標頭檔案,所有UIView
的子類都擁有播放視訊的方法。框架把UIView
的分類方法作為和框架內部互動的橋樑。 -
02.
JPVideoPlayerManager
負責甄別使用者傳過來的 URL,根據不同的 URL 作出不同的反應進行視訊播放。具體細節如下: -
02.1、是否是本地檔案路徑,如果是本地路徑,直接把路徑給負責視訊播放的工具類
JPVideoPlayerPlayVideoTool
進行視訊播放; -
02.2、如果不是本地路徑,再根據 URL 生成快取的 key 給
JPVideoPlayerCache
工具類查詢是否有本地快取檔案,如果有快取就把快取路徑返還給JPVideoPlayerManager
,JPVideoPlayerManager
會把路徑給負責視訊播放的工具類JPVideoPlayerPlayVideoTool
進行視訊播放; -
02.3、如果沒有本地快取,就把 URL 給
JPVideoPlayerDownloader
下載工具類,這個工具類就會去網路上下載視訊資料,每下載完一段資料,都會返回給JPVideoPlayerManager
,JPVideoPlayerManager
會先把這段資料給JPVideoPlayerCache
,JPVideoPlayerCache
先把資料快取到磁碟,然後再把快取的路徑返還給JPVideoPlayerManager
,JPVideoPlayerManager
會把路徑給負責視訊播放的工具類JPVideoPlayerPlayVideoTool
進行視訊播放。
03. JPVideoPlayer Version 2.0 更新了哪些內容?
類名 | 功能點 |
---|---|
JPVideoPlayerDownloaderOperation |
下載單個視訊檔案工具 |
JPVideoPlayerDownloader |
下載工具類,管理下載操作佇列 |
JPVideoPlayerCachePathTool |
管理臨時和完整視訊儲存路徑 |
JPVideoPlayerCacheConfig |
快取配置檔案,包括快取週期,最大磁碟快取等 |
JPVideoPlayerCache |
快取工具類,負責視訊資料的存、取、刪、更新 |
JPVideoPlayerResourceLoader |
視訊播放器的資料代理,負責將網路視訊資料填充給播放器 |
JPVideoPlayerPlayVideoTool |
視訊播放工具類 |
JPVideoPlayerManager |
管理者,協調各個模組相互配合工作 |
接下來我將大致描述一下每個類的實現:
-
01.
JPVideoPlayerDownloaderOperation
:它繼承自NSOperation
,它內部持有一個NSURLSession
例項物件,由這個例項物件去負責下載視訊資料,JPVideoPlayerDownloaderOperation
成為這個例項物件的代理,監聽獲取下載到的資料,並將獲得的資料回傳給操作的所有者JPVideoPlayerDownloader
。 -
02.
JPVideoPlayerDownloader
:它持有一個下載佇列,下載佇列裡存放的是JPVideoPlayerDownloaderOperation
例項物件。考慮到播放視訊資料量較大而且是持續的,為了將所有的網路資源用於載入當前使用者播放的視訊,提升使用體驗,所以這個佇列在任何時候都只允許一個下載操作執行。它接收到JPVideoPlayerDownloaderOperation
回撥的資料以後會繼續把資料回傳出去。 -
03.
JPVideoPlayerCachePathTool
:它負責管理快取檔案的路徑,包括臨時和完整視訊儲存路徑兩個部分。 -
04.
JPVideoPlayerCacheConfig
:這個類存放著快取儲存週期,最大磁碟快取等快取配置資料。 -
05.
JPVideoPlayerCache
:它負責資料的存、取、刪、更新等功能。當呼叫存資料的功能時,會在臨時檔案存放的資料夾內新建一個 mp4 檔案,並開始將資料寫入到這個檔案內,存完一段資料以後,會將儲存資料的路徑回撥出去。每次播放視訊的時候,都會去查詢快取中有沒有這個 URL 的完整資料快取,如果有就會把路徑回撥出去。它還對外提供獲取快取大小的功能,如果你需要在設定裡獲取當前視訊快取,可以呼叫這個介面。它還有刪除快取視訊資料的功能。 -
06.
JPVideoPlayerResourceLoader
這個類沿用自 1.0 版本,負責從已經下載的資料中找出播放器需要的資料,並將資料填充到播放器的資料請求中進行視訊播放。 -
07.
JPVideoPlayerPlayVideoTool
這個類持有一個視訊播放器,負責串聯從視訊資料到影像和影音的整個播放功能。 -
08.
JPVideoPlayerManager
它是框架的管理者、大管家,它的具體功能上面“內部細節”已經陳述過了,這裡不重複了。 -
09.其他還有一個資料載入進度條和一個緩衝狀態指示器,這些都很簡單。還有就是 2.0 版本的 Demo 將
UITableViewController
視訊播放抽到一個分類中集中管理,也方便後期維護,如果你的需求和 Demo 類似,可以考慮直接把這個分類拽進專案,需要改動的程式碼很少,就能實現。
04.為什麼要這麼改?
這個問題我想從使用者和我自己,還有它本來應該是什麼樣子這些維度來分析。
04.1、使用者角度
-
01.首先,受使用者歡迎的框架應該要具備兩個基本的素質。第一,呼叫起來方便,能一行程式碼解決的,絕不搞兩行。第二,不侵入使用者的專案,萬一哪天框架不維護了,也不要成為使用者的累贅。
-
02.其次,我說一個我今天看一個框架的感受,是一個類似微信選擇多張照片的框架,框架對外提供的介面不夠我用,所以我只能去框架內部改,但是當我找一個功能的時候,框架文件沒有說明,我在那些程式碼應該放的位置也沒有找到,需求就是改字型和顏色,我找了一上午,終於在一個不可能猜到的位置找到了那幾行程式碼。這裡就有幾個問題點:
- 我們對外提供的介面儘量讓使用者夠用,如果沒有考慮到,那使用者就可能會來框架裡改。
- 我們的框架類的命名,方法的命名都應該遵守蘋果的那套標準,因為大家天天在用的都是蘋果 API,如果完全不遵守蘋果那套,那使用者來到框架裡就是一頭霧水,這裡有一個溝通成本的問題,看懂程式碼之前還要先熟悉我們特有的標準。就像去美國,要和美國人說話,要先學英語。
- 文件一定要詳實,每個人水平都不一樣,程式碼可能看不懂,但是文字誰都認識,這是作者和使用者溝通的基礎。
04.2、作者角度
維護框架其實也是很花時間的一件事情,1.0 版本的時候,就經常有使用者給我留言,發QQ訊息和我溝通實現的細節,還要他們希望下個版本希望加進去的一些功能。
當時很多功能的程式碼都混在一個類裡,第一就是這個類上千行程式碼,我自己要改一個東西都需要用搜尋功能才能找到,各個方法之間相互呼叫的時候跳來跳去,頭暈眼花。
現在每個模組劃分完功能以後,每個功能的核心程式碼都高度集中在對應的類裡,隱藏實現的細節,遮蔽了外部的干擾,只對外提供必要的介面。現在除錯問題的時候,分析到出現問題可能的模組以後,能快速定位到對應類的對應方法裡,只需要在當前類裡專注當前的問題就可以了,不需要考慮外部的影響。這個效率的提升還是蠻明顯的。
所以從作者的角度,這個架構的好處就是,第一,方便我後期的維護,提高效率;第二,方便和使用者的溝通,減少溝通成本;第三,當有新功能的時候,我能快速的把程式碼寫到對應的類別裡。
04.3、它本來的樣子
前段時間看 BBC 的 “Planet Earth” 紀錄片,裡面說螞蟻巢穴裡有幾千萬只螞蟻,但是卻分工明確,秩序盡然。原來,螞蟻分為四類:
- 蟻后,也叫蟻皇,是一族之主,專管產卵繁殖,一般一群只有一個,體型特大,行動不便,由工蟻侍候。
- 雄蟻,專與蟻后交配,交配後即死亡,一群中有數十隻或數百隻,要看蟻群的大小。
- 工蟻,是蟻群中的主要成員,專司覓食、飼養幼蟻、侍候蟻后、搬家清掃等等雜勤工作。
- 兵蟻,個頭較大,雙顎發達,是蟻群中的保衛者,擔負著本蟻群的安全,如有外蟻入侵,或爭奪食物時,必誓死決鬥。
我們的程式碼或許也可以仿照大自然,先劃分功能,再列出幾個類,將功能點挨個集中到類裡,武裝出類,就是所謂的物件。這就是我理解的框架應該有的美。
05.Update.
2017.04.04 更新.
2017.05.31 更新:
2017.05.02 更新.
有些朋友反應有些視訊無法邊下邊播, 具體解決思路請參考 這篇博文 。