项目起始阶段,为了追求早点上线,使用第三方库讲的是拿来就用,结果到了优化重构阶段或者因为一些原因不得不换第三方库时,那就头疼了。
这里可以举很多例子,像图片加载库、网络请求库、第三方统计库,如果不进行封装,而是直接调用,代码耦合性非常高,也不利于以后扩展。
以上三个例子可以自己写个类包装下,就不再赘述了。而视频播放,同时涉及到MediaPlayer状态回调和所在控件的生命周期,更为复杂一点,下面我做了一个简单的分享,不过足以应付大部分的现实情况。
项目(https://github.com/wanghy360/HyVideoPlayer)结构如下:
- hyvideolibrary :播放器抽象,重连机制的逻辑
- ksylibrary:金山播放器的实现
- ijklibraray:Bilibili播放器的实现
- app:demo效果
在hyvideolibrary定义了播放器的所有行为以及它的回调方法。
/**
* Created by Wanghy on 2017/9/11
* 播放器抽象类,定义了播放器的行为方法
*
* @param <T> 播放器View
*/
public abstract class Player<T> {
protected Callback callback;
/**
* 初始化
*
* @param realPlayer 播放器View
*/
public abstract void init(T realPlayer);
/**
* @return 播放器View
*/
public abstract T getRealPlayer();
/**
* 开始播放
*
* @param liveUrl 视频Url
*/
public abstract void startVideo(String liveUrl);
/**
* 结束播放
*/
public abstract void stopPlay();
/**
* 后台播放
*/
public abstract void runInBackground();
/**
* 前台播放
*/
public abstract void runInForeground();
/**
* 释放资源
*/
public abstract void release();
/**
* reset播放
*/
public abstract void reset();
/**
* 此方法务必调用
*/
public void setCallBack(Callback callback) {
this.callback = callback;
}
public interface Callback {
/**
* 播放准备
*/
void onPrepared();
/**
* 缓冲开始
*/
void onBufferingStart();
/**
* 缓冲结束
*/
void onBufferingEnd();
/**
* 视频展现开始
*/
void onRenderingStart();
/**
* 播放结束
*/
void onCompletion();
/**
* 播放出错
*/
void onError();
}
}
还有一个VideoPlayer抽象类,事实上这个才是播放视频。Player抽象类方法较少,适用于rtmp视频流。
在PlayerPresenter里实现了重连机制,这个逻辑与播放器无关,所以独立出来。
说到Presenter,这里实现了部分MVP架构,View就是播放器所在的组件(例如Activity,Fragment乃至View),Presenter包含播放器所有代码逻辑,由于数据只有一个Url,所以并没有定义model。
自己可以在继承自PlayerPresenter的MyPlayerPresenter里,增加自己的代码逻辑。这里只是显示了Toast。
public class MyPlayerPresenter<V> extends PlayerPresenter<V, MyPlayView> {
public MyPlayerPresenter(MyPlayView view) {
super(view);
}
@Override
protected boolean isBlockReLoad() {
return false;
}
@Override
protected void reLoadLiveFail() {
Toast.makeText(view.getViewActivity(), "reLoadLiveFail", Toast.LENGTH_SHORT).show();
}
@Override
public void onRenderingStart() {
super.onRenderingStart();
Toast.makeText(view.getViewActivity(), "onRenderingStart", Toast.LENGTH_SHORT).show();
}
@Override
protected void reConnect() {
super.reConnect();
Toast.makeText(view.getViewActivity(), "reConnect", Toast.LENGTH_SHORT).show();
}
}
2个播放器实现以后,在PlayerActivity将到Presenter与View关联起来,除了要在继承自PlayerActivity的Activity里初始化videoView,2个播放器并没有其他的不同。
//ksylive
videoView = (KSYTextureView) findViewById(R.id.id_ksy_player);
//ijkplayer
videoView = (IjkVideoView) findViewById(R.id.id_ijk_player);
共同部分:
import android.os.Bundle;
import com.github.wanghy360.hyvideolibrary.player.Player;
import com.github.wanghy360.player.presenter.MyPlayerPresenter;
import com.github.wanghy360.player.view.MyPlayView;
/**
* Created by Wanghy on 2017/9/15
*/
public abstract class PlayerActivity<V> extends BaseActivity implements MyPlayView<PlayerActivity> {
protected V videoView;
private MyPlayerPresenter<V> myPlayerPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
protected void init(Player<V> player) {
myPlayerPresenter = new MyPlayerPresenter<>(this);
myPlayerPresenter.subscribe(videoView, player);
myPlayerPresenter.startVideo("rtmp://live.hkstv.hk.lxdns.com/live/hks");
}
@Override
protected void onPause() {
super.onPause();
myPlayerPresenter.runInBackground();
}
@Override
protected void onResume() {
super.onResume();
myPlayerPresenter.runInForeground();
}
@Override
protected void onDestroy() {
super.onDestroy();
myPlayerPresenter.unSubscribe();
}
@Override
public PlayerActivity getViewActivity() {
return this;
}
}
分享就介绍到这里了,下一个博客应该会写一个功能全面的播放界面,涉及到横竖切换,音量亮度调节,重力感应。