共计 17633 个字符,预计需要花费 45 分钟才能阅读完成。
05. 视频播放器内核切换封装
目录介绍
- 01. 视频播放器内核封装需要
- 02. 播放器内核架构图
- 03. 如何兼容不同内核播放器
- 04. 看一下 ijk 的内核实现类
- 05. 看一下 exo 的内核实现类
- 06. 如何创立不同内核播放器
- 07. 看一下工厂类实现代码
- 08. 前期如何增加新的内核
00. 视频播放器通用框架
- 根底封装视频播放器 player,能够在 ExoPlayer、MediaPlayer,声网 RTC 视频播放器内核,原生 MediaPlayer 能够自在切换
- 对于视图状态切换和前期保护拓展,防止性能和业务呈现耦合。比方须要反对播放器 UI 高度定制,而不是该 lib 库中 UI 代码
- 针对视频播放,音频播放,播放回放,以及视频直播的性能。应用简略,代码拓展性强,封装性好,次要是和业务彻底解耦,裸露接口监听给开发者解决业务具体逻辑
- 该播放器整体架构:播放器内核 (自在切换) + 视频播放器 + 边播边缓存 + 高度定制播放器 UI 视图层
- 我的项目地址:https://github.com/yangchong2…
- 对于视频播放器整体性能介绍文档:https://juejin.im/post/688345…
01. 视频播放器内核封装需要
-
播放器内核难以切换
- 不同的视频播放器内核,因为 api 不一样,所以难以切换操作。要是想兼容内核切换,就必须本人制订一个视频接口 + 实现类的播放器
-
肯定要解耦合
- 播放器内核与播放器解耦: 反对更多的播放场景、以及新的播放业务疾速接入,并且不影响其余播放业务,比方前期增加阿里云播放器内核,或者腾讯播放器内核
-
传入不同类型不便创立不同内核
- 暗藏内核播放器创立具体细节,开发者只须要关怀所需产品对应的工厂,毋庸关怀创立细节,甚至毋庸晓得具体播放器类的类名。须要合乎开闭准则
02. 播放器内核架构图
03. 如何兼容不同内核播放器
-
发问:针对不同内核播放器,比方谷歌的 ExoPlayer,B 站的 IjkPlayer,还有原生的 MediaPlayer,有些 api 不一样,那应用的时候如何对立 api 呢?
- 比如说,ijk 和 exo 的视频播放 listener 监听 api 就齐全不同,这个时候须要做兼容解决
- 定义接口,而后各个不同内核播放器实现接口,重写形象办法。调用的时候,获取接口对象调用 api,这样就能够对立 Api
-
定义一个接口,这个接口有什么呢?这个接口定义通用视频播放器办法,比方常见的有:视频初始化,设置 url,加载,以及播放状态,简略来说能够分为三个局部。
- 第一局部:视频初始化实例对象办法,次要包含:initPlayer 初始化视频,setDataSource 设置视频播放器地址,setSurface 设置视频播放器渲染 view,prepareAsync 开始筹备播放操作
- 第二局部:视频播放器状态办法,次要包含:播放,暂停,复原,重制,设置进度,开释资源,获取进度,设置速度,设置音量
- 第三局部:player 绑定 view 后,须要监听播放状态,比方播放异样,播放实现,播放筹备,播放 size 变动,还有播放筹备
04. 看一下 ijk 的内核实现类
-
ijk 的内核实现类代码如下所示
public class IjkVideoPlayer extends AbstractVideoPlayer { protected IjkMediaPlayer mMediaPlayer; private int mBufferedPercent; private Context mAppContext; public IjkVideoPlayer(Context context) {if (context instanceof Application){mAppContext = context;} else {mAppContext = context.getApplicationContext(); } } @Override public void initPlayer() { mMediaPlayer = new IjkMediaPlayer(); //native 日志 IjkMediaPlayer.native_setLogLevel(VideoLogUtils.isIsLog() ? IjkMediaPlayer.IJK_LOG_INFO : IjkMediaPlayer.IJK_LOG_SILENT); setOptions(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); initListener();} @Override public void setOptions() {} /** * ijk 视频播放器监听 listener */ private void initListener() { // 设置监听,能够查看 ijk 中的 IMediaPlayer 源码监听事件 // 设置视频谬误监听器 mMediaPlayer.setOnErrorListener(onErrorListener); // 设置视频播放实现监听事件 mMediaPlayer.setOnCompletionListener(onCompletionListener); // 设置视频信息监听器 mMediaPlayer.setOnInfoListener(onInfoListener); // 设置视频缓冲更新监听事件 mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener); // 设置筹备视频播放监听事件 mMediaPlayer.setOnPreparedListener(onPreparedListener); // 设置视频大小更改监听器 mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener); // 设置视频 seek 实现监听事件 mMediaPlayer.setOnSeekCompleteListener(onSeekCompleteListener); // 设置工夫文本监听器 mMediaPlayer.setOnTimedTextListener(onTimedTextListener); mMediaPlayer.setOnNativeInvokeListener(new IjkMediaPlayer.OnNativeInvokeListener() {@Override public boolean onNativeInvoke(int i, Bundle bundle) {return true;} }); } /** * 设置播放地址 * * @param path 播放地址 * @param headers 播放地址申请头 */ @Override public void setDataSource(String path, Map<String, String> headers) {// 设置 dataSource if(path==null || path.length()==0){if (mPlayerEventListener!=null){mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_URL_NULL, 0); } return; } try {// 解析 path Uri uri = Uri.parse(path); if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {RawDataSourceProvider rawDataSourceProvider = RawDataSourceProvider.create(mAppContext, uri); mMediaPlayer.setDataSource(rawDataSourceProvider); } else {// 解决 UA 问题 if (headers != null) {String userAgent = headers.get("User-Agent"); if (!TextUtils.isEmpty(userAgent)) {mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "user_agent", userAgent); } } mMediaPlayer.setDataSource(mAppContext, uri, headers); } } catch (Exception e) {mPlayerEventListener.onError(); } } /** * 用于播放 raw 和 asset 外面的视频文件 */ @Override public void setDataSource(AssetFileDescriptor fd) {try { mMediaPlayer.setDataSource(new RawDataSourceProvider(fd)); } catch (Exception e) {mPlayerEventListener.onError(); } } /** * 设置渲染视频的 View, 次要用于 TextureView * @param surface surface */ @Override public void setSurface(Surface surface) {mMediaPlayer.setSurface(surface); } /** * 筹备开始播放(异步)*/ @Override public void prepareAsync() { try { mMediaPlayer.prepareAsync(); } catch (IllegalStateException e) {mPlayerEventListener.onError(); } } /** * 暂停 */ @Override public void pause() { try { mMediaPlayer.pause(); } catch (IllegalStateException e) {mPlayerEventListener.onError(); } } /** * 播放 */ @Override public void start() { try { mMediaPlayer.start(); } catch (IllegalStateException e) {mPlayerEventListener.onError(); } } /** * 进行 */ @Override public void stop() { try { mMediaPlayer.stop(); } catch (IllegalStateException e) {mPlayerEventListener.onError(); } } /** * 重置播放器 */ @Override public void reset() { mMediaPlayer.reset(); mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener); setOptions();} /** * 是否正在播放 */ @Override public boolean isPlaying() { return mMediaPlayer.isPlaying(); } /** * 调整进度 */ @Override public void seekTo(long time) {try { mMediaPlayer.seekTo((int) time); } catch (IllegalStateException e) {mPlayerEventListener.onError(); } } /** * 开释播放器 */ @Override public void release() { mMediaPlayer.setOnErrorListener(null); mMediaPlayer.setOnCompletionListener(null); mMediaPlayer.setOnInfoListener(null); mMediaPlayer.setOnBufferingUpdateListener(null); mMediaPlayer.setOnPreparedListener(null); mMediaPlayer.setOnVideoSizeChangedListener(null); new Thread() { @Override public void run() {try { mMediaPlayer.release(); } catch (Exception e) {e.printStackTrace(); } } }.start();} /** * 获取以后播放的地位 */ @Override public long getCurrentPosition() { return mMediaPlayer.getCurrentPosition(); } /** * 获取视频总时长 */ @Override public long getDuration() { return mMediaPlayer.getDuration(); } /** * 获取缓冲百分比 */ @Override public int getBufferedPercentage() { return mBufferedPercent;} /** * 设置渲染视频的 View, 次要用于 SurfaceView */ @Override public void setDisplay(SurfaceHolder holder) {mMediaPlayer.setDisplay(holder); } /** * 设置音量 */ @Override public void setVolume(float v1, float v2) {mMediaPlayer.setVolume(v1, v2); } /** * 设置是否循环播放 */ @Override public void setLooping(boolean isLooping) {mMediaPlayer.setLooping(isLooping); } /** * 设置播放速度 */ @Override public void setSpeed(float speed) {mMediaPlayer.setSpeed(speed); } /** * 获取播放速度 */ @Override public float getSpeed() { return mMediaPlayer.getSpeed(0); } /** * 获取以后缓冲的网速 */ @Override public long getTcpSpeed() { return mMediaPlayer.getTcpSpeed(); } /** * 设置视频谬误监听器 * int MEDIA_INFO_VIDEO_RENDERING_START = 3;// 视频筹备渲染 * int MEDIA_INFO_BUFFERING_START = 701;// 开始缓冲 * int MEDIA_INFO_BUFFERING_END = 702;// 缓冲完结 * int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;// 视频抉择信息 * int MEDIA_ERROR_SERVER_DIED = 100;// 视频中断,个别是视频源异样或者不反对的视频类型。* int MEDIA_ERROR_IJK_PLAYER = -10000,// 个别是视频源有问题或者数据格式不反对,比方音频不是 AAC 之类的 * int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;// 数据谬误没有无效的回收 */ private IMediaPlayer.OnErrorListener onErrorListener = new IMediaPlayer.OnErrorListener() { @Override public boolean onError(IMediaPlayer iMediaPlayer, int framework_err, int impl_err) {mPlayerEventListener.onError(); VideoLogUtils.d("IjkVideoPlayer----listener---------onError ——> STATE_ERROR ———— what:" + framework_err + ", extra:" + impl_err); return true; } }; /** * 设置视频播放实现监听事件 */ private IMediaPlayer.OnCompletionListener onCompletionListener = new IMediaPlayer.OnCompletionListener() { @Override public void onCompletion(IMediaPlayer iMediaPlayer) {mPlayerEventListener.onCompletion(); VideoLogUtils.d("IjkVideoPlayer----listener---------onCompletion ——> STATE_COMPLETED"); } }; /** * 设置视频信息监听器 */ private IMediaPlayer.OnInfoListener onInfoListener = new IMediaPlayer.OnInfoListener() { @Override public boolean onInfo(IMediaPlayer iMediaPlayer, int what, int extra) {mPlayerEventListener.onInfo(what, extra); VideoLogUtils.d("IjkVideoPlayer----listener---------onInfo ——> ———— what:" + what + ", extra:" + extra); return true; } }; /** * 设置视频缓冲更新监听事件 */ private IMediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() { @Override public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int percent) {mBufferedPercent = percent;} }; /** * 设置筹备视频播放监听事件 */ private IMediaPlayer.OnPreparedListener onPreparedListener = new IMediaPlayer.OnPreparedListener() { @Override public void onPrepared(IMediaPlayer iMediaPlayer) {mPlayerEventListener.onPrepared(); VideoLogUtils.d("IjkVideoPlayer----listener---------onPrepared ——> STATE_PREPARED"); } }; /** * 设置视频大小更改监听器 */ private IMediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() { @Override public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int width, int height, int sar_num, int sar_den) {int videoWidth = iMediaPlayer.getVideoWidth(); int videoHeight = iMediaPlayer.getVideoHeight(); if (videoWidth != 0 && videoHeight != 0) {mPlayerEventListener.onVideoSizeChanged(videoWidth, videoHeight); } VideoLogUtils.d("IjkVideoPlayer----listener---------onVideoSizeChanged ——> WIDTH:" + width + ",HEIGHT:" + height); } }; /**
}; /**
- 设置视频 seek 实现监听事件 */ private IMediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() { @Override public void onSeekComplete(IMediaPlayer iMediaPlayer) {}
}; } `
05. 看一下 exo 的内核实现类
-
exo 的内核实现类代码如下所示,和 ijk 的 api 有些区别
public class ExoMediaPlayer extends AbstractVideoPlayer implements VideoListener, Player.EventListener { protected Context mAppContext; protected SimpleExoPlayer mInternalPlayer; protected MediaSource mMediaSource; protected ExoMediaSourceHelper mMediaSourceHelper; private PlaybackParameters mSpeedPlaybackParameters; private int mLastReportedPlaybackState = Player.STATE_IDLE; private boolean mLastReportedPlayWhenReady = false; private boolean mIsPreparing; private boolean mIsBuffering; private LoadControl mLoadControl; private RenderersFactory mRenderersFactory; private TrackSelector mTrackSelector; public ExoMediaPlayer(Context context) {if (context instanceof Application){mAppContext = context;} else {mAppContext = context.getApplicationContext(); } mMediaSourceHelper = ExoMediaSourceHelper.getInstance(context); } @Override public void initPlayer() { // 创立 exo 播放器 mInternalPlayer = new SimpleExoPlayer.Builder( mAppContext, mRenderersFactory == null ? mRenderersFactory = new DefaultRenderersFactory(mAppContext) : mRenderersFactory, mTrackSelector == null ? mTrackSelector = new DefaultTrackSelector(mAppContext) : mTrackSelector, mLoadControl == null ? mLoadControl = new DefaultLoadControl() : mLoadControl, DefaultBandwidthMeter.getSingletonInstance(mAppContext), Util.getLooper(), new AnalyticsCollector(Clock.DEFAULT), /* useLazyPreparation= */ true, Clock.DEFAULT) .build(); setOptions(); // 播放器日志 if (VideoLogUtils.isIsLog() && mTrackSelector instanceof MappingTrackSelector) {mInternalPlayer.addAnalyticsListener(new EventLogger((MappingTrackSelector) mTrackSelector, "ExoPlayer")); } initListener();} /**
mTrackSelector = trackSelector; } public void setRenderersFactory(RenderersFactory renderersFactory) {
mRenderersFactory = renderersFactory; } public void setLoadControl(LoadControl loadControl) {
mLoadControl = loadControl; } /**
- 设置播放地址 @param path 播放地址 @param headers 播放地址申请头 / @Override public void setDataSource(String path, Map<String, String> headers) {// 设置 dataSource if(path==null || path.length()==0){if (mPlayerEventListener!=null){mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_URL_NULL, 0); } return; } mMediaSource = mMediaSourceHelper.getMediaSource(path, headers); } @Override
public void setDataSource(AssetFileDescriptor fd) {//no support} /**
- 筹备开始播放(异步)/ @Override public void prepareAsync() { if (mInternalPlayer == null){return;} if (mMediaSource == null){return;} if (mSpeedPlaybackParameters != null) {mInternalPlayer.setPlaybackParameters(mSpeedPlaybackParameters); } mIsPreparing = true; mMediaSource.addEventListener(new Handler(), mMediaSourceEventListener); // 筹备播放 mInternalPlayer.prepare(mMediaSource); } /*
- 播放 / @Override public void start() { if (mInternalPlayer == null){return;} mInternalPlayer.setPlayWhenReady(true); } /*
- 暂停 / @Override public void pause() { if (mInternalPlayer == null){return;} mInternalPlayer.setPlayWhenReady(false); } /*
- 进行 */ @Override public void stop() { if (mInternalPlayer == null){return;} mInternalPlayer.stop();} private MediaSourceEventListener mMediaSourceEventListener = new MediaSourceEventListener() {
@Override public void onReadingStarted(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {if (mPlayerEventListener != null && mIsPreparing) {mPlayerEventListener.onPrepared(); } } }; /**
- 重置播放器 / @Override public void reset() { if (mInternalPlayer != null) {mInternalPlayer.stop(true); mInternalPlayer.setVideoSurface(null); mIsPreparing = false; mIsBuffering = false; mLastReportedPlaybackState = Player.STATE_IDLE; mLastReportedPlayWhenReady = false; } } /*
- 是否正在播放 / @Override public boolean isPlaying() { if (mInternalPlayer == null){return false;} int state = mInternalPlayer.getPlaybackState(); switch (state) {case Player.STATE_BUFFERING: case Player.STATE_READY: return mInternalPlayer.getPlayWhenReady(); case Player.STATE_IDLE: case Player.STATE_ENDED: default: return false; } } /*
- 调整进度 / @Override public void seekTo(long time) {if (mInternalPlayer == null){return;} mInternalPlayer.seekTo(time); } /*
- 开释播放器 */ @Override public void release() { if (mInternalPlayer != null) {mInternalPlayer.removeListener(this); mInternalPlayer.removeVideoListener(this); final SimpleExoPlayer player = mInternalPlayer; mInternalPlayer = null; new Thread() { @Override public void run() {// 异步开释,避免卡顿 player.release(); } }.start();} mIsPreparing = false;
mIsBuffering = false; mLastReportedPlaybackState = Player.STATE_IDLE; mLastReportedPlayWhenReady = false; mSpeedPlaybackParameters = null; } /**
- 获取以后播放的地位 / @Override public long getCurrentPosition() { if (mInternalPlayer == null){return 0;} return mInternalPlayer.getCurrentPosition();} /*
- 获取视频总时长 / @Override public long getDuration() { if (mInternalPlayer == null){return 0;} return mInternalPlayer.getDuration();} /*
- 获取缓冲百分比 / @Override public int getBufferedPercentage() { return mInternalPlayer == null ? 0 : mInternalPlayer.getBufferedPercentage(); } /*
- 设置渲染视频的 View, 次要用于 SurfaceView */ @Override public void setSurface(Surface surface) {if (mInternalPlayer != null) {mInternalPlayer.setVideoSurface(surface); } } @Override
public void setDisplay(SurfaceHolder holder) {if (holder == null){setSurface(null); } else{setSurface(holder.getSurface()); } } /**
- 设置音量 / @Override public void setVolume(float leftVolume, float rightVolume) {if (mInternalPlayer != null){mInternalPlayer.setVolume((leftVolume + rightVolume) / 2); } } /*
- 设置是否循环播放 */ @Override public void setLooping(boolean isLooping) {if (mInternalPlayer != null){mInternalPlayer.setRepeatMode(isLooping ? Player.REPEAT_MODE_ALL : Player.REPEAT_MODE_OFF); } } @Override
public void setOptions() { // 筹备好就开始播放 mInternalPlayer.setPlayWhenReady(true); } /**
- 设置播放速度 / @Override public void setSpeed(float speed) {PlaybackParameters playbackParameters = new PlaybackParameters(speed); mSpeedPlaybackParameters = playbackParameters; if (mInternalPlayer != null) {mInternalPlayer.setPlaybackParameters(playbackParameters); } } /*
- 获取播放速度 / @Override public float getSpeed() { if (mSpeedPlaybackParameters != null) {return mSpeedPlaybackParameters.speed;} return 1f; } /*
- 获取以后缓冲的网速 */ @Override public long getTcpSpeed() { // no support return 0;} @Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {if (mPlayerEventListener == null){return;} if (mIsPreparing){return;} if (mLastReportedPlayWhenReady != playWhenReady || mLastReportedPlaybackState != playbackState) {switch (playbackState) {// 最开始调用的状态 case Player.STATE_IDLE: break; // 开始缓充 case Player.STATE_BUFFERING: mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_BUFFERING_START, getBufferedPercentage()); mIsBuffering = true; break; // 开始播放 case Player.STATE_READY: if (mIsBuffering) {mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_BUFFERING_END, getBufferedPercentage()); mIsBuffering = false; } break; // 播放器曾经播放完了媒体 case Player.STATE_ENDED: mPlayerEventListener.onCompletion(); break; default: break;} mLastReportedPlaybackState = playbackState; mLastReportedPlayWhenReady = playWhenReady; } } @Override
public void onPlayerError(ExoPlaybackException error) {if (mPlayerEventListener != null) {mPlayerEventListener.onError(); } } @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {if (mPlayerEventListener != null) {mPlayerEventListener.onVideoSizeChanged(width, height); if (unappliedRotationDegrees > 0) {mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_VIDEO_ROTATION_CHANGED, unappliedRotationDegrees); } } } @Override
public void onRenderedFirstFrame() { if (mPlayerEventListener != null && mIsPreparing) {mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_VIDEO_RENDERING_START, 0); mIsPreparing = false; } } } `
06. 如何创立不同内核播放器
-
先来看一下创立不同内核播放器的代码,只须要开发者传入一个类型参数,即可创立不同类的实例对象。代码如下所示
/** * 获取 PlayerFactory 具体实现类,获取内核 * 创建对象的时候只须要传递类型 type,而不须要对应的工厂,即可创立具体的产品对象 * TYPE_IJK IjkPlayer,基于 IjkPlayer 封装播放器 * TYPE_NATIVE MediaPlayer,基于原生自带的播放器控件 * TYPE_EXO 基于谷歌视频播放器 * TYPE_RTC 基于 RTC 视频播放器 * @param type 类型 * @return */ public static AbstractVideoPlayer getVideoPlayer(Context context,@PlayerConstant.PlayerType int type){if (type == PlayerConstant.PlayerType.TYPE_EXO){return ExoPlayerFactory.create().createPlayer(context); } else if (type == PlayerConstant.PlayerType.TYPE_IJK){return IjkPlayerFactory.create().createPlayer(context); } else if (type == PlayerConstant.PlayerType.TYPE_NATIVE){return MediaPlayerFactory.create().createPlayer(context); } else if (type == PlayerConstant.PlayerType.TYPE_RTC){return IjkPlayerFactory.create().createPlayer(context); } else {return IjkPlayerFactory.create().createPlayer(context); } } ```- 应用工厂模式创立不同对象的动机是什么,为何要这样应用?- 一个视频播放器能够提供多个内核 Player(如 ijk、exo、media,rtc 等等),这些 player 都源自同一个基类,不过在继承基类后不同的子类批改了局部属性从而使得它们能够出现不同的外观。
-
首先定义一个工厂抽象类,而后不同的内核播放器别离创立其具体的工厂实现具体类
- PlayerFactory:形象工厂,负责这个角色的是工厂办法模式的外围,任何在模式中创建对象的工厂类必须实现这个接口
- ExoPlayerFactory:具体工厂,具体工厂角色含有与业务密切相关的逻辑,并且受到使用者的调用以创立具体产品对象。
-
如何应用,分为三步,具体操作如下所示
- 1. 先调用具体工厂对象中的办法 createPlayer 办法;2. 依据传入产品类型参数取得具体的产品对象;3. 返回产品对象并应用。
- 简而言之,创建对象的时候只须要传递类型 type,而不须要对应的工厂,即可创立具体的产品对象
07. 看一下工厂类实现代码
-
形象工厂类,代码如下所示
public abstract class PlayerFactory<T extends AbstractVideoPlayer> {public abstract T createPlayer(Context context); } ```- 具体实现工厂类,代码如下所示
public class ExoPlayerFactory extends PlayerFactory<ExoMediaPlayer> {public static ExoPlayerFactory create() {
return new ExoPlayerFactory();} @Override
public ExoMediaPlayer createPlayer(Context context) {return new ExoMediaPlayer(context); } }`
– 这种创建对象最大长处- 工厂办法用来创立所须要的产品,同时暗藏了哪种具体产品类将被实例化这一细节,用户只须要关怀所需产品对应的工厂,毋庸关怀创立细节,甚至毋庸晓得具体产品类的类名。
- 退出新的产品时,比方前期新加一个阿里播放器内核,这个时候就只须要增加一个具体工厂和具体产品就能够。零碎的可扩展性也就变得十分好,完全符合“开闭准则”
08. 前期如何增加新的内核
-
比方前期想要增加一个腾讯视频内核的播放器。代码如下所示,这个是简化的
public class TxPlayerFactory extends PlayerFactory<TxMediaPlayer> {public static TxPlayerFactory create() {return new TxPlayerFactory(); } @Override public TxMediaPlayer createPlayer(Context context) {return new TxMediaPlayer(context); } } public class TxMediaPlayer extends AbstractVideoPlayer {