LocalBroadcastManager。
它来自Android 的support包,类名是android.support.v4.content.LocalBroadcastManager,是用来在同一个应用内的不同组件间发送Broadcast的,刚好与我们的场景相符合。同时,它发送的广播只在app内传播,不会泄漏到其他的应用,安全性也有了保证。而且它的使用方式很简单,与普通的广播类似。
其实这里面说的仅仅是数据安全的问题,还有一个非常的优点 也是非常的重要,当然数据安全是最重要的,
在动态广播里面,广播的范围仅仅是自己的activity里面才有用,或者是自己定义的view里面才有用,(虽然一般很少有人在自定义view里面放广播,因为涉及到VIEW更新,因为把UI更新放在这个层级来做,有点虎,),主要说的是范围太小
在静态广播里面,及时APP被关了,如果收到这个消息,还是会有广播的,这个范围太广,就涉及到上面的那个数据安全,而且需要在xml中注册这个接受者,就是receiver属性,这个也不安全,有可能会被进程给干掉,
现在如果有个业务需求是这样的,自定义一个类(工具、视图等),会被重复的使用,或者当前正在使用的有很多个实例,但是从对象这个方面去看,这么多实例中,某个资源同一个时刻仅能有一个实例对象持有这种资源,其他的都只能等,而且这种资源跟用户的操作有很大的关系,举个例子就是:当上一个实例在用这个资源的时候,由于用户的行为新建一个实例需要立马使用这个资源,那么就需要把上一个实例给停了,
看到这里大家肯定会想到使用 单利模式啊,使用static变量啊,使用同步方法啊,等等,
我的意思是想说使用 LocalBroadCastManager也可以解决这个问题,他可以让类对象自己发自己收,其他的实例对象复本也可以收到,然后让他做自己的操作即可,
下面举个例子就是:需要在 ListView中放视频,每个item都是一个视频,但是只能有一个同时在播放,并且用户点击了一个播放没有关闭的情况下,继续点击另外的播放,需要自动把上面的暂停了,并且保存相关状态(记录位置,暂停进度条,显示暂停图标等等),
在这样的背景下,使用LocalBroadCastManager是可以解决问题的,
下面是item那个view自身需要做的处理类的相关代码,这份代码只是demo,而且我没有处理相关状态,只是以释放和播放来解决问题,请不要直接使用。
public class VideoPlayView extends LinearLayout{
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
public MediaPlayer mediaPlayer;
private boolean isReady = false;
private boolean mIsPlaying = false;
private boolean mIsPause = false;
private Context context;
public String path;
public static String time;
LocalBroadcastManager mLocalBroadcastManager;
public static final String STATICACTION = "xxxxx.dynamic.view";
@SuppressLint("NewApi")
public VideoPlayView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context = context;
initView(context);
}
@SuppressLint("NewApi")
public VideoPlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView(context);
}
public VideoPlayView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView(context);
}
public VideoPlayView(Context context) {
super(context);
this.context = context;
initView(context);
}
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.public_infoflow_video_play, this, true);
mSurfaceView =(SurfaceView) findViewById(R.id.surfaceview);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new CustomCallBack());
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
MySetOnClickListener(mSurfaceView);
mLocalBroadcastManager = LocalBroadcastManager.getInstance(context);
IntentFilter intentFilter = new IntentFilter(STATICACTION);
mLocalBroadcastManager.registerReceiver(dynamicReceiver, intentFilter);
}
private void MySetOnClickListener(View view) {
if(view==null){
return ;
}else{
view.setOnClickListener(mClickListener);
}
}
private OnClickListener mClickListener = new OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(STATICACTION);
intent.putExtra("msg", getPath());
mLocalBroadcastManager.sendBroadcast(intent);
if(path!=null && isReady && !mIsPlaying){
mIsPlaying =true;
mIsPause = false;
isReady = false;
play(0);
}else if(mediaPlayer!=null && mediaPlayer.isPlaying()&& mIsPlaying && !mIsPause ){
mediaPlayer.pause();
mIsPlaying = false;
mIsPause = true;
isReady = true;
release();
}else{
mIsPlaying =true;
mIsPause = false;
isReady = false;
play(0);
}
}
};
private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(STATICACTION)){
String receive_path = intent.getStringExtra("msg");
if(!path.equals(receive_path)){
release();
}
}
}
};
private class CustomCallBack implements Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
isReady = true;
mIsPlaying = false;
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mIsPlaying = false;
release();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
release();
mIsPlaying = false;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsPlaying = false;
release();
mLocalBroadcastManager.unregisterReceiver(dynamicReceiver);
}
}
public String getPath() {
return path;
}
public void play(int position) {
try {
if(mediaPlayer == null){
mediaPlayer = new MediaPlayer();
}
mediaPlayer.reset();
mediaPlayer.setDataSource(path);
mediaPlayer.setDisplay(mSurfaceView.getHolder());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new PrepareListener(position));
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.release();
mIsPlaying = false;
return true;
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
release();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
protected void release() {
if(mediaPlayer!=null){
mediaPlayer.setOnErrorListener(null);
try {
mediaPlayer.stop();
mediaPlayer.release();
} catch (Exception e) {
}
}
mediaPlayer = null;
}
private final class PrepareListener implements OnPreparedListener{
private int position;
public PrepareListener(int position){
this.position = position;
}
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
}
}
public void freeResource() {
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
}
public void setPath(String path) {
this.path = path;
}
}
需要工程包的请留言,暂时代码还没有整理,后面补发。
其实使用这种方式比 单利模式,static变量,同步方法要好些,因为资源最后还是 两份直接轮换,一个current和一个next,双层机制,
音乐播放的时候是使用当前的mediaPlayer和预加载的mediaPlayer,
大视频的无缝播放也是采用两个mediaPlayer来实现的,
而且这种方式附带的在处理相关属性的过程中将会更加的独立,自己处理自己的事多好,如果使用static,判断条件实在太多,而且在退出的时候还需要考虑释放的问题,
当然这种做法也有不好的地方,全屏(如果是使用activity跳转的话)的时候再回来就麻烦了,