04.視頻播放器通用架構實踐
目錄介紹
- 01.視頻播放器的痛點
- 02.業務需求的目標
- 03.該播放器框架特點
- 04.播放器內核封裝
- 05.播放器UI層封裝
- 06.如何簡單使用
- 07.如何自定義播放器
- 08.該案例的拓展性分享
- 09.關於視頻緩存方案
- 10.如何監控視頻埋點
- 11.待實現的需求分析
- 12.一些細節上優化
- 13.參考案例和博客記錄
00.視頻播放器通用框架
- 基礎封裝視頻播放器player,可以在ExoPlayer、MediaPlayer,聲網RTC視頻播放器內核,原生MediaPlayer可以自由切換
- 對於視圖狀態切換和後期維護拓展,避免功能和業務出現耦合。比如需要支持播放器UI高度定製,而不是該lib庫中UI代碼
- 針對視頻播放,音頻播放,播放回放,以及視頻直播的功能。使用簡單,代碼拓展性強,封裝性好,主要是和業務徹底解耦,暴露接口監聽給開發者處理業務具體邏輯
- 該播放器整體架構:播放器內核(自由切換) + 視頻播放器 + 邊播邊緩存 + 高度定製播放器UI視圖層
- 項目地址:https://github.com/yangchong211/YCVideoPlayer
- 關於視頻播放器整體功能介紹文檔:https://juejin.im/post/6883457444752654343
- 關於視頻播放器通用架構實踐:
01.視頻播放器的痛點
-
播放器內核難以切換
- 不同的視頻播放器內核,由於api不一樣,所以難以切換操作。要是想兼容內核切換,就必須自己制定一個視頻接口+實現類的播放器
-
播放器內核和UI層耦合
- 也就是說視頻player和ui操作柔和到了一起,尤其是兩者之間的交互。比如播放中需要更新UI進度條,播放異常需要顯示異常UI,都比較難處理播放器狀態變化更新UI操作
-
UI難以自定義或者修改麻煩
- 比如常見的視頻播放器,會把視頻各種視圖寫到xml中,這種方式在後期代碼會很大,而且改動一個小的佈局,則會影響大。這樣到後期往往只敢加代碼,而不敢刪除代碼……
- 有時候難以適應新的場景,比如添加一個播放廣告,老師開課,或者視頻引導業務需求,則需要到播放器中寫一堆業務代碼。迭代到後期,違背了開閉原則,視頻播放器需要做到和業務分離
-
視頻播放器結構不清晰
- 這個是指該視頻播放器能否看了文檔後快速上手,知道封裝的大概流程。方便後期他人修改和維護,因此需要將視頻播放器功能分離。比如切換內核+視頻播放器(player+controller+view)
-
播放器播放和業務耦合
- 比如多個app共用一個視頻播放器組件,一個播放業務播放器狀態發生變化,其他播放業務必須同步更新播放狀態,各個播放業務之間互相交叉,隨著播放業務的增多,開發和維護成本會急劇增加, 導致後續開發不可持續。
02.業務需求的目標
-
常見的業務需求
- 基礎封裝視頻播放器player,可以在ExoPlayer、MediaPlayer,聲網RTC視頻播放器內核,原生MediaPlayer可以自由切換
- 對於視圖狀態切換和後期維護拓展,避免功能和業務出現耦合。比如需要支持播放器UI高度定製,而不是該lib庫中UI代碼
- 針對視頻播放,音頻播放,播放回放,以及視頻直播的功能。使用簡單,代碼拓展性強,封裝性好,主要是和業務徹底解耦,暴露接口監聽給開發者處理業務具體邏輯
-
音視頻播放框架
- 視頻播放等於MediaPlayer和SurfaceView,MediaPlayer主要用於播放音頻,沒有提供圖像輸出界面,所以我們需要藉助其他的組件來顯示MediaPlayer播放的圖像輸出,我們可以使用SurfaceView來顯示
- 能否實踐開發出一套音視頻播放的通用架構,能支持音頻播放場景,也能播放視頻場景,還可以無縫切換。比如視頻切換音頻操作,增強庫的功能性
- 視頻窗口、音頻窗口、視頻浮窗、音頻浮窗、短視頻窗口、短視頻浮窗、音頻控制檯等多種場景播放,需要靈活切換,這個也是一個大的難點
03.該播放器框架特點
-
一定要解耦合
- 播放器內核與播放器解耦: 支持更多的播放場景、以及新的播放業務快速接入,並且不影響其他播放業務,比如後期添加阿里雲播放器內核,或者騰訊播放器內核
- 播放器player與視頻UI解耦:支持添加自定義視頻視圖,比如支持添加自定義廣告,新手引導,或者視頻播放異常等視圖,這個需要較強的拓展性
-
適合多種業務場景
- 比如適合播放單個視頻,多個視頻,以及列表視頻,或者類似抖音那種一個頁面一個視頻,還有小窗口播放視頻。也就是適合大多數業務場景
-
播放器的整體層級圖
-
播放器架構的介紹
- 基礎內核播放庫:提供基礎的播放功能,可以自由切換內核,也方便拓展添加其他sdk內核播放器
- 統一播放器:屏蔽底層內核播放器播放差異,根據協議為上層提供統一的播放能力接口,供上層調用
- 播放視圖層:負責播放器視圖層的UI控制和調度,徹底解除播放業務與播放器的耦合
- 播放場景業務:負責向用戶展示音視頻播放能力和交互的業務
- 播放關聯業務: 為播放器提供增值或支撐的業務,比如視頻埋點統計,後期添加投屏,後期添加下載功能
- demo:提供各種播放場景案例代碼,基本上有大多數常用播放器的使用場景,建議直接看demo拿來即用
04.播放器內核封裝
4.0 遇到的問題
-
播放器內核拓展難
- 不同的播放SDK提供的API都不一樣,如果業務層對每個合作方都進行業務開發,就會導致業務量非常龐大,並且不同合作的方的播放SDK會產生交叉,不利於播放業務的維護和拓展。
-
播放器內核難以切換
- 不同的視頻播放器內核,由於api不一樣,所以難以切換操作。要是想兼容內核切換,就必須自己制定一個視頻接口+實現類的播放器
4.1 視頻播放器內核封裝需求
-
一定要解耦合
- 播放器內核與播放器解耦: 支持更多的播放場景、以及新的播放業務快速接入,並且不影響其他播放業務,比如後期添加阿里雲播放器內核,或者騰訊播放器內核
-
傳入不同類型方便創建不同內核
- 隱藏內核播放器創建具體細節,開發者只需要關心所需產品對應的工廠,無須關心創建細節,甚至無須知道具體播放器類的類名。需要符合開閉原則
-
具體設計方案
- 設計統一播放協議,對於上層播放業務,只調用按照統一協議設計接口,不必關心底層播放器的設計邏輯。保證上層播放業務不隨新的接入播放SDK發生變化。
4.2 播放器內核架構圖
-
播放器內核架構圖
-
播放器內核代碼說明
4.3 如何兼容不同內核播放器
-
提問:針對不同內核播放器,比如谷歌的ExoPlayer,B站的IjkPlayer,還有原生的MediaPlayer,有些api不一樣,那使用的時候如何統一api呢?
- 比如說,ijk和exo的視頻播放listener監聽api就完全不同,這個時候需要做兼容處理
- 定義接口,然後各個不同內核播放器實現接口,重寫抽象方法。調用的時候,獲取接口對象調用api,這樣就可以統一Api
-
播放器內核
- 可以切換ExoPlayer、MediaPlayer,IjkPlayer,聲網視頻播放器,這裡使用工廠模式Factory + AbstractVideoPlayer + 各個實現AbstractVideoPlayer抽象類的播放器類
- 定義抽象的播放器,主要包含視頻初始化,設置,狀態設置,以及播放監聽。由於每個內核播放器api可能不一樣,所以這裡需要實現AbstractVideoPlayer抽象類的播放器類,方便後期統一調用
- 為了方便創建不同內核player,所以需要創建一個PlayerFactory,定義一個createPlayer創建播放器的抽象方法,然後各個內核都實現它,各自創建自己的播放器
-
關於AbstractVideoPlayer接口詳細說明。這個接口定義通用視頻播放器方法,比如常見的有:視頻初始化,設置url,加載,以及播放狀態,簡單來說可以分為三個部分。
- 第一部分:視頻初始化實例對象方法,主要包括:initPlayer初始化視頻,setDataSource設置視頻播放器地址,setSurface設置視頻播放器渲染view,prepareAsync開始準備播放操作
- 第二部分:視頻播放器狀態方法,主要包括:播放,暫停,恢復,重製,設置進度,釋放資源,獲取進度,設置速度,設置音量
- 第三部分:player綁定view後,需要監聽播放狀態,比如播放異常,播放完成,播放準備,播放size變化,還有播放準備
-
播放器的核心實現要點
- 針對上層播放器業務,該內核庫提供統一的播放暫停,設置播放狀態的接口,由於播放器內核和播放器業務解耦合,所以非常方便快速添加其他sdk播放器,具體可以看這篇文章:05.視頻播放器內核切換封裝
05.播放器UI層封裝
5.1 實際開發遇到問題
-
發展中遇到的問題
- 播放器可支持多種場景下的播放,多個產品會用到同一個播放器,這樣就會帶來一個問題,一個播放業務播放器狀態發生變化,其他播放業務必須同步更新播放狀態,各個播放業務之間互相交叉,隨著播放業務的增多,開發和維護成本會急劇增加, 導致後續開發不可持續。
-
不太好適合多種業務場景
- 比如適合播放單個視頻,多個視頻,以及列表視頻,或者類似抖音那種一個頁面一個視頻,還有小窗口播放視頻。也就是適合大多數業務場景,視頻通用性需要儘可能完善
5.2 如何分離播放和UI分離
-
VideoPlayer播放器
- 可以自由切換視頻內核,Player+Controller。player負責播放的邏輯,Controller負責視圖相關的邏輯,兩者之間用接口進行通信
- 針對Controller,需要定義一個接口,主要負責視圖UI處理邏輯,支持添加各種自定義視圖View【統一實現自定義接口Control】,每個view儘量保證功能單一性,最後通過addView形式添加進來
- 針對Player,需要定義一個接口,主要負責視頻播放處理邏輯,比如視頻播放,暫停,設置播放進度,設置視頻鏈接,切換播放模式等操作。需要注意把Controller設置到Player裡面,兩者之間通過接口交互
-
UI控制器視圖
- 定義一個BaseVideoController類,這個主要是集成各種事件的處理邏輯,比如播放器狀態改變,控制視圖隱藏和顯示,播放進度改變,鎖定狀態改變,設備方向監聽等等操作
- 定義一個view的接口InterControlView,在這裡類裡定義綁定視圖,視圖隱藏和顯示,播放狀態,播放模式,播放進度,鎖屏等操作。這個每個實現類則都可以拿到這些屬性呢
- 在BaseVideoController中使用LinkedHashMap保存每個自定義view視圖,添加則put進來後然後通過addView將視圖添加到該控制器中,這樣非常方便添加自定義視圖
- 播放器切換狀態需要改變Controller視圖,比如視頻異常則需要顯示異常視圖view,則它們之間的交互是通過ControlWrapper(同時實現Controller接口和Player接口)實現
-
具體如何實現呢
- 可以看這篇博客:06.播放器UI抽取封裝
5.3 關於優先級視圖展示
-
視頻播放器為了拓展性,需要暴露view接口供外部開發者自定義視頻播放器視圖,通過addView的形式添加到播放器的控制器中。
- 這就涉及view視圖的層級性。控制view視圖的顯示和隱藏是特別重要的,這個時候在自定義view中就需要拿到播放器的狀態
-
舉一個簡單的例子,基礎視頻播放器
- 添加了基礎播放功能的幾個播放視圖。有播放完成,播放異常,播放加載,頂部標題欄,底部控制條欄,鎖屏,以及手勢滑動欄。如何控制它們的顯示隱藏切換呢?
- 在addView這些視圖時,大多數的view都是默認GONE隱藏的。比如當視頻初始化時,先緩衝則顯示緩衝view而隱藏其他視圖,接著播放則顯示頂部/底部視圖而隱藏其他視圖
-
比如有時候需要顯示兩種不同的自定義視圖如何處理
- 舉個例子,播放的時候,點擊一下視頻,會顯示頂部title視圖和底部控制條視圖,那麼這樣會同時顯示兩個視圖。
- 點擊頂部title視圖的返回鍵可以關閉播放器,點擊底部控制條視圖的播放暫停可以控制播放條件。這個時候底部控制條視圖FrameLayout的ChildView在整個視頻的底部,頂部title視圖FrameLayout的ChildView在整個視頻的頂部,這樣可以達到上下層都可以相應事件。
-
那麼FrameLayout層層重疊,如何讓下層不響應事件
- 在最上方顯示的層加上: android:clickable="true" 可以避免點擊上層觸發底層。或者直接給控制設置一個background顏色也可以。
5.4 視頻播放器重力感應監聽
-
區別視頻幾種不同的播放模式
- 正常播放時,設置檢查系統是否開啟自動旋轉,打開監聽;全屏模式播放視頻的時候,強制監聽設備方向;在小窗口模式播放視頻的時候,取消重力感應監聽
- 注意一點。關於是否開啟自動旋轉的重力感應監聽,可以給外部開發者暴露一個方法設置的開關。讓用戶選擇是否開啟該功能
-
具體怎麼操作
- 寫一個類,然後繼承OrientationEventListener類,注意視頻播放器重力感應監聽不要那麼頻繁。表示500毫秒才檢測一次……
- mOrientationHelper.enable();表示檢查系統是否開啟自動旋轉。mOrientationHelper.disable();表示取消監聽
- 具體可以看這篇博客:06.播放器UI抽取封裝
06.如何簡單使用
6.1 播放單個視頻
-
必須需要的四步驟代碼如下所示
//創建基礎視頻播放器,一般播放器的功能 BasisVideoController controller = new BasisVideoController(this); //設置控制器 mVideoPlayer.setVideoController(controller); //設置視頻播放鏈接地址 mVideoPlayer.setUrl(url); //開始播放 mVideoPlayer.start();
-
只需要四步操作即可,非常簡單。這樣就可以滿足一個基礎的視頻播放器
- 具體邏輯可以看:BasisVideoController
-
如何添加只定義視圖,非常方便。AdControlView需要實現InterControlView接口才可以
AdControlView adControlView = new AdControlView(this); controller.addControlComponent(adControlView);
-
要是一個頁面播放多個視頻怎麼辦
- 直接創建兩個VideoPlayer,實現代碼和播放單個視頻一樣,只是需要注意:不要開啟音頻焦點監聽。
- 如果是開啟的音頻焦點改變監聽,那麼播放該視頻的時候,就會停止其他音視頻的播放操作。類似,你聽音樂,這個時候去看視頻,那麼音樂就暫停呢
6.2 列表播放視頻
-
關於列表播放視頻,該案例支持
-
列表頁面有多個item
- 第一種:點擊item播放,當item滑動到不可見時暫停播放;點擊其他可見item播放視頻,則會暫停其他正在播放的視頻,也就是說一次只能播放一個視頻
- 第二種:滑動item,用戶不用點擊,讓其自動進行播放,這種業務場景在玩手機碰到過。大概思路時,進入列表自動播放第一個,然後在RecyclerView滑動監聽的方法中,判斷如果頁面滑動停止了,則遍歷RecyclerView子控件找到第一個完全可見的item,然後拿到該item的索引即可播放該位置的視頻
-
列表頁面是一個頁面一個item
- 第一種操作使用ViewPager,是垂直方向可以滾動的VerticalViewPager + PagerAdapter,這種方式在item創建上可以設置預加載加載佈局視圖
- 第二種操作使用RecyclerView,是用ScrollPageHelper + RecyclerView,這種方式也可以實現一個頁面一個item,一次滑動一個
-
-
如何保證在列表中只播放一個視頻。兩種方案
- 第一種:每個item放一個VideoPlayer,但是要注意需要用一個單例VideoPlayerManager來保證只有一個VideoPlayer對象,這樣就可以保證一次播放一個視頻。當ViewHolder中的視圖被回收時需要銷燬視頻資源
- 第二種:只創建一個VideoPlayer,那個播放就添加到具體的item佈局中。比如播放第一個視頻就把player對象添加到視圖中,點擊播放第三個時需要把player從它的父佈局中移除後然後再添加到該item的佈局中,這樣就可以實現
-
list條目中滑動item不可見就停止視頻播放
- 在列表中播放,可以監聽RecyclerView中的item生命週期,有一個AttachedToWindow是綁定item視圖,還有一個DetachedFromWindow方法是item離開窗口時調用,在這個裡面可以做視頻銷燬的邏輯。
07.如何自定義播放器
-
BasisVideoController已經滿足基礎視頻播放器功能
- 在該控制器中,已經做了相關的初始化操作,比如設置視頻可以拖動,根據屏幕方向自動進入/退出全屏,設置滑動調節亮度,音量,進度,默認開啟等操作。
- 快速添加基礎視頻播放器的模塊,包括視頻播放完成view,播放異常view,播放top視圖view,播放底部控制藍view,手勢滑動視圖view等。同時在每一個視圖view中可以拿到視頻播放器的狀態,便於設置UI的操作。
-
比如在此播放器基礎上,添加廣告視圖view
- 現在有個業務需求,需要在視頻播放器剛開始添加一個廣告視圖,等待廣告倒計時120秒後,直接進入播放視頻邏輯。相信這個業務場景很常見,大家都碰到過,使用該播放器就特別簡單。
- 首先創建一個自定義view,需要實現InterControlView接口,重寫該接口中所有抽象方法,這裡省略了很多代碼,具體看demo。最後在調用controller.addControlComponent(adControlView);添加到基礎視頻播放器,這種方式滿足大多數的場景……
-
那要是基礎播放器不滿足UI該怎麼辦?
- 好辦,直接仿照BasisVideoController創建一個你自己的控制器,ui想怎麼定製你自己決定。比如說你要實現一個小窗口播放視頻,那這個時候肯定需要定製,照葫蘆畫瓢,具體可以看CustomFloatController類。
08.該案例的拓展性分享
-
可以配置多個內核切換
- 只需要你在配置的時候,傳入不同的類型即可創建不同的播放器內核,十分方便。如果後期你要拓展其他的內核播放器,只需要按照exo的代碼案例弄一套即可,十分方便,加入其他內核播放器不會影響到你的業務。
PlayerFactory player = PlayerFactoryUtils.getPlayer(PlayerConstant.PlayerType.TYPE_IJK);
-
可以配置統一視頻埋點監聽
- 避免在每個帶有視頻的頁面activity或者Fragment中添加埋點,而是有播放器框架內部提供一個埋點的接口,外部開發者只需要實現這個接口即可全局埋點視頻播放器,非常方便和管理維護,針對接口增加或者刪除都是不影響你其他的業務。
-
開發者可以自由添加自定義視頻視圖
- 在封裝BaseVideoController控制器的時候,考慮到後期的拓展性,把視頻各個視頻都是以addView的形式添加進來,使用LinkedHashMap存儲這樣可以保證順序。
- 需要注意的是在這個Controller中,需要把播放器的播放狀態,播放模式,播放進度,鎖屏等操作給綁定到開發者自定義實現的播放器視圖View中。
-
暴露眾多視頻操作的方法給開發者
- 比如給視頻設置封面圖片,這個時候總不能在播放器內部引入一個Glide,然後加載圖片,這樣和業務耦合呢。可以把這個設置封面view暴露給開發者,然後設置,這樣更好一些。
- 比如外部開發者想要知道視頻播放器的狀態,做一些業務上操作,這個時候完全可以通過接口的形式暴露出來,該播放器把視頻的播放模式監聽,播放狀態監聽,還有各種視頻操作都暴露了方法出來,方便開發者調用。
09.關於視頻緩存方案
-
網絡上比較好的項目:https://github.com/danikula/AndroidVideoCache
- 網絡用的HttpURLConnection,文件緩存處理,文件最大限度策略,回調監聽處理,斷點續傳,代理服務等。
-
但是存在一些問題,比如如下所示
- 文件的緩存超過限制後沒有按照lru算法刪除,
- 處理返回給播放器的http響應頭消息,響應頭消息的獲取處理改為head請求(需服務器支持)
- 替換網絡庫為okHttp(因為大部分的項目都是以okHttp為網絡請求庫的),但是這個改動性比較大
-
然後看一下怎麼使用,超級簡單。傳入視頻url鏈接,返回一個代理鏈接,然後就可以呢
HttpProxyCacheServer server = new HttpProxyCacheServer(this); String proxyVideoUrl = server.getProxyUrl(URL_AD);
-
大概的原理
- 原始的方式是直接塞播放地址給播放器,它就可以直接播放。現在我們要在中間加一層本地代理,播放器播放的時候(獲取數據)是通過我們的本地代理的地址來播放的,這樣我們就可以很好的在中間層(本地代理層)做一些處理,比如:文件緩存,預緩存(秒開處理),監控等。
-
原理詳細一點來說
- 1.採用了本地代理服務的方式,通過原始url給播放器返回一個本地代理的一個url ,代理URL類似:http://127.0.0.1:port/視頻url;(port端口為系統隨機分配的有效端口,真實url是為了真正的下載),然後播放器播放的時候請求到了你本地的代理上了。
- 2.本地代理採用ServerSocket監聽127.0.0.1的有效端口,這個時候手機就是一個服務器了,客戶端就是socket,也就是播放器。
- 3.讀取客戶端就是socket來讀取數據(http協議請求)解析http協議。
- 4.根據url檢查視頻文件是否存在,讀取文件數據給播放器,也就是往socket裡寫入數據(socket通信)。同時如果沒有下載完成會進行斷點下載,當然弱網的話數據需要生產消費同步處理。
-
如何實現預加載
- 其實預加載的思路很簡單,在進行一個播放視頻後,再返回接下來需要預加載的視頻url,啟用線程去請求下載數據
- 開啟一個線程去請求並預加載一部分的數據,可能需要預加載的數據大於>1,利用隊列先進入的先進行加載,因此可以採用LinkedHashMap保存正在預加載的task。
- 在開始預加載的時候,判斷該播放地址是否已經預加載,如果不是那麼創建一個線程task,並且把它放到map集合中。然後執行預加載邏輯,也就是執行HttpURLConnection請求
- 提供取消對應url加載的任務,因為有可能該url不需要再進行預加載了,比如參考抖音,當用戶瞬間下滑幾個視頻,那麼很多視頻就需要跳過了不需要再進行預加載。這個後期在做
10.如何監控視頻埋點
-
傳統一點的做法
- 比如用友盟或者百度統計,或者用其他的統計。之前的做法是,在每個有視頻的頁面比如說Activity,Fragment等開啟時視頻播放時埋點一次,頁面退出時埋點一次。
- 如果app中有多個activity或者fragment頁面,那麼就每個頁面都要進行埋點。比如如果你的app是付費視頻,你想知道有多少人試看了,該怎麼操作。那麼你需要在每一個有視頻的activity頁面挨個添加埋點,那還有沒有更好的辦法?
-
解決方案
- 舉個例子:例如,你需要來讓外部開發者手動去埋點,可是在類中怎麼埋點又是由其他人來設計的,你只是需要對外暴露監聽的方法。那麼該如何做呢?採用接口 + 實現類方式即可實現。
-
該案例中怎麼操作
- 定義一個接口,規定其他人設計類,必須繼承這個接口。在這個接口中,定義進入視頻播放,退出視頻播放器,記錄播放進度,視頻播放完成,播放異常,點擊廣告,點擊試看等操作的抽象方法。具體可以看BuriedPointEvent類代碼……
-
外部開發者如何使用
- 定義一個類實現該視頻埋點接口,重寫裡面方法。然後需要在初始化配置視頻播放器的時候,將這個實現類的對象傳遞進來即可。通過這個配置類傳進來的對象,播放器就可以處理監聽設置邏輯呢。
- 這種操作最大的好處就是:在這個類中統一處理視頻的埋點,修改快捷,而不用在每一個有視頻播放器的頁面埋點,方便維護。比如如何處理視頻播放完成監聽,代碼如下所示:
@Override public void onCompletion() { VideoPlayerConfig config = VideoViewManager.getConfig(); if (config!=null && config.mBuriedPointEvent!=null){ //視頻播放完成 config.mBuriedPointEvent.playerCompletion(mUrl); } }
11.待實現的需求分析
-
音視頻無縫切換
- 比如在豆神教育中,有視頻播放,也有音頻播放,這兩塊都是寫到了業務代碼中,能否將兩者糅合起來。但音頻相比視頻,多了一個可以在後臺播放的功能,一般用在service中,這一相互切換需求待完善。以滿足後期可能出現的需求功能。
-
優化播放器持續平滑播放
- 畫中畫方案:雖然Android8.0及其以上版本已提供了畫中畫方案,但是Android8.0以下版本仍然保有大量用戶,其缺點就是無法滿足Android8.0以下用戶需;
- 採用系統浮層:採用系統浮層需要系統浮層權限,Android廠商對系統浮層的授權越來越嚴格,導致用戶授權過程的體驗比較差;需要權限,可能有些手機不太好適配;
- 在每個展示頁面單獨添加播放器浮窗:優點是不受Android系統版本限制,並且用戶無需系統浮層權限授權,適合所有手機用戶,體驗較好
12.一些細節上優化
-
多使用註解限定符
- 對於一些關於類型的方法參數,可以多用註解限定符,暴露給外部開發者調用的方法,可以防止傳入正確的類型。比如:PlayerFactoryUtils.getPlayer(PlayerConstant.PlayerType.TYPE_IJK)
-
完善的api文檔
- api文檔充分完善到每一個細節,以及配套demo,方便快速上手。完善的代碼註釋,以及項目的類結構圖,方便快速瞭解視頻播放器的整體輪廓
-
豐富的demo案例
- 提供絕大多數場景的視頻播放器功能,完全可以套用demo中的案例,甚至你還可以在案例基礎上大幅度優化
13.參考案例和博客記錄
-
exo播放器
-
ijk播放器
-
阿里雲播放器
-
GSY播放器
-
餃子播放器