使用接口实现IBinder类从而实现Med…

 今天要写的是实现安卓自带的播放器也就是mediaplayer的实现示例。至于要写自己开发的播放器,本人水平还远远不够,而且光是那些音频视频的格式处理就够有的忙了。好了,我们正式开始讲讲mediaplayer类的实现方法吧。

  首先,我们先讲讲关于mediaplayer的lifecircle。

一、Mediaplayer有两大类的创建方式:

1、  MediaPlayer  mediaplayer = MediaPlayer.create(Context context,Uri uri). 这是其中一大类中的一种创建方式,主要是用MediaPlayer的静态方法create()来创建,其中参数有多种。

2、  MediaPlayer  mediaplayer=new MediaPlayer() 这是第二种方式

这两种创建方式的区别在于,我们可以在第一种方式中,通过参数传入要播放的文件地址,并且不需要再执行prepare()或prepareAsync()方法。但是对于第二种方法,我们就需要通过setdataSource()来设置文件播放地址,以及接下来还要使用prepare()或prepareAsync()方法。

二、Mediaplayer的一些常见的方法

1、 mediaplayer.start()  //开始播放

2、mediaplayer.pause()  //暂停播放

3、mediaplayer.stop()  //停止播放

4、mediaplayer.seekto(int position)  //在position处开始播放

这些方法看上去很简单,但是要将这些方法结合起来,完成一个简单的播放器,却是不简单的。因为这就要讲到Mediaplayer的3个回调方法了。

三、Mediaplayer的3个回调方法

1、mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

                  @Override

                  public void onPrepared(MediaPlayer mp) {}

}

   该回调方法在mediaplayer对象生成之后会执行一次,以后将不会执行。

2、mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

 

                  @Override

                  public void onCompletion(MediaPlayer mp) {}

}

  该回调方法在mediaplayer对象完成播放之后会执行。

3、mediaPlayer.setOnErrorListener(new OnErrorListener() {

                  @Override

                  public boolean onError(MediaPlayer mp, int what, int extra) {}

}

该回调方法在mediaplayer对象播放错误之后会执行。

我建议大家在使用的时候一定要实现这三个方法,特别是第三个回调方法,因为我们总是不可避免的在实现mediaplayer逻辑处理的时候,会出现一些问题。简单的实现是,我都是在第一个回调方法里用start()方法来启动播放,在第二个回调方法里,将mediaplayer对象的资源释放。

   我感觉我要讲的基础知识也就这样了,接下来就是关于我做的播放器的实现方法来跟大家说一说。先说一下我的示例的功能吧,在文本框输入的路径,路径可以为本地的mp3或者是网络的MP3地址。但是我仅仅实现了本地播放时的进度条拖动处理,至于网络播放的进度条处理,需要调用seekto的回调方法,应该也是简单的,所以我没去弄。本例可以实现按钮控制音乐播放中的暂停,重播,停止,以及进度条的拖动处理。先上个截图吧。

   使用接口实现IBinder类从而实现MediaPlayer的播放

比较简陋,其实我应该去用帧布局去实现这个界面的,感觉会高大上一点。

接下来就具体谈谈我的实现方法吧:

先讲逻辑处理:

1.点击播放按钮后,歌曲开始播放,播放按钮设置为不可点击(比较简单的一种处理方式,如果用单例模式来处理这种逻辑会比较好),暂停、重播按钮如果为不可点击,则设置为可点击状态,进度条开始工作。

2.点击暂停按钮后,音乐停止播放,进度条停止工作,暂停按钮的文字变为“继续”。再次点击后,音乐继续播放,进度条开始工作,按钮的文字变为“暂停”。

3.点击重播按钮后,音乐重新开始播放,进度条也是从0处开始播放,如果此时暂停按钮的文字为“继续”,则变为“暂停”

4.点击停止按钮后,音乐停止播放,进度条也停止工作,暂停,重播按钮设置为不可点击,播放按钮设置为可点击。

5.拖动进度条到任意处,音乐从该处开始播放,如果暂停按钮的文字为“继续”,则设置为“暂停”。

我的示例只能实现按顺序或按规律的点击按钮来触发事件的话,我也没必要写这么一篇博文了,要的就是可随意点击,针对用户不同的点击,做出正确的处理,而不出现“程序已停止工作”的错误发生,这就要求进行逻辑处理的代码的严谨和可靠。不过再讲具体的代码之前,还要再讲讲我的示例的整个的实现框架(不知道是否这个词用的对不对)吧。

音乐播放是可以在后台实现的一种功能,所以我采用了服务的方式来进行音乐播放,那么如何将服务和activity连接在一起呢,这就用到了充当代理人角色的对象“IBinder”,我只要在activity里获取到了这个对象,那么我就可以对它进行操作,实现service里的方法。下面是绑定服务的代码:

bindService(service, myconn, BIND_AUTO_CREATE);

service和BIND_AUTO_CREATE这两个参数我就不说了,大家都应该能明白,先看一下myconn这个接口的实现代码吧:
private class Myconn implements ServiceConnection {

 

       @Override

       public void onServiceConnected(ComponentName name, IBinder service) {

           musicPlayer = (MusicPlayer) service;

           System.out.println("onServiceConnected");

           //准备播放前的准备工作

           musicPlayer.preplay(mediaPlayer, et_path.getText().toString(),

                  play, pause, surfaceHolder, handler);

           ChangeProgress();

       }

 

       @Override

       public void onServiceDisconnected(ComponentName name) {

           System.out.println("onServiceDisconnected");

 

       }

 

    }

这里要讲的最重要的一点是MusicPlayer是我声明的一个接口,这就说到了一个问题,我们为什么不直接使用IBinder对象,而要将它转换成一个接口。为了安全,当然本例无关这个安全的问题,我这么写是为了养成良好的习惯。我们在Service类里会创建一个IBinder的子类,在这个子类里实现其中的方法。但是这个内部类为了安全性(有的方法我不想被访问到),使用private来进行封装,那就产生了一个问题在onServiceConnected(ComponentName name, IBinder service)我就拿不到这个内部类了。比如说我的这个内部类为myBinder。那么我就没法做Service.myBinder,这个操作是实现不了的,大家可以试试。所以

问题来了,怎样能一举两得,即能够保证内部类的安全,又要能实现里面的方法,答案就是接口。在接口里定义我要实现的方法,再将myBinder实现这个接口,具体重写接口里面的方法。这样在activity里我就能取到这个接口,就像我的实现代码一样musicPlayer = (MusicPlayer) service;。

   接下来看一下接口中定义的方法。

public interface MusicPlayer {

   

    public void preplay(MediaPlayer mediaPlayer, String path, Button play,

           Button pause, SurfaceHolder surfaceHolder, Handler handler);

 

   

    public void play(int position);

 

   

    public void pause();

 

   

    public void replay();

 

   

    public void stop();

 

}

接下来就讲具体的实现代码了:

  Preplay()将传递进来的参数赋都做,this.xxx=xxx;

@Override

     public void play(final int position) {  

       //点击播放按钮后,将按钮设置为不可点击状态。开启一个新的线程,开始播放音乐

        play.setEnabled(false);

           new Thread() {

              public void run() {

 

                  Playing(position);

 

              }

 

           }.start();

 

       }

关于Playing(position)的实现代码:

     

       public void Playing(final int position) {

          

           try {

              System.out.println("playing");

              // 当改变进度条或者点重播按钮时,会进入if语句。如果mediaplayer对象不存在了

              // 就需要重新生成。

              if (mediaPlayer != null && mediaPlayer.isPlaying()) {

                  // 开始播放

                  mediaPlayer.start();

                  // 从position位置开始播放

                  mediaPlayer.seekTo(position);

              } else {

                  // 重新生成一个mediaplayer对象

                  //为了防止点击“暂停”按钮后,拖动进度条,会产生mediaPlayer.isPlaying()返回false

                  //但是此时mediaplayer还是不为空的,所以需要将此资源释放,重新生成mediaplayer对象,

                  //再次调用OnPreparedListener的回调方法

                  if (mediaPlayer != null) {

                     mediaPlayer.stop();

                     mediaPlayer.release();

                     mediaPlayer = null;

                  }

                  loadplay();//有关此方法的代码见下

                 

              }

              // 开启子线程更新进度条

              new Thread() {

                  public void run() {

                     isplaying = true;

                     while (isplaying) {

                         // 当mediaplayer不为空且在播放状态时更新进度,防止getcurrentposition()方法出错

                         if (mediaPlayer != null && mediaPlayer.isPlaying()) {

                            // 获取当前播放到的位置

                            int prograss = mediaPlayer.getCurrentPosition();

                            Message message = handler.obtainMessage();

                            message.what = UPDATEROGRESS;

                            message.arg1 = prograss;

                            handler.sendMessage(message);

                            try {

                                Thread.sleep(1000);

                            } catch (InterruptedException e) {

                                // TODO Auto-generated catch block

                                e.printStackTrace();

                            }

                         }

                     }

                  };

              }.start();

              // 播放前的回调方法,生成mediaplayer对象后,只调用一次

              mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

 

                  @Override

                  public void onPrepared(MediaPlayer mp) {

                     System.out.println("onPrepared========");

                     // 将播放文件的长度传给handler对象,设置进度条的最大值

                     if (mediaPlayer != null) {

                         System.out.println("mediaplayer不为空");

                         Message message = handler.obtainMessage();

                         message.what = MAXPROGRESS;

                         message.arg1 = mediaPlayer.getDuration();

                         handler.sendMessage(message);

                     }

                     mediaPlayer.start();

                     mediaPlayer.seekTo(position);

                  }

              });

              // 播放完成后的回调方法

              mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

 

                  @Override

                  public void onCompletion(MediaPlayer mp) {

                     System.out.println("onCompletion");

                     // 播放完成

                     isplaying = false;

                     // 释放资源

                     mediaPlayer.release();

                     mediaPlayer = null;

                     // 播放按钮可点击

                     play.setEnabled(true);

 

                  }

              });

              // 播放出错的回调方法

              mediaPlayer.setOnErrorListener(new OnErrorListener() {

 

                  @Override

                  public boolean onError(MediaPlayer mp, int what, int extra) {

                     Toast.makeText(getApplicationContext(), "播放失败",

                            Toast.LENGTH_SHORT).show();

                     System.out.println(what);

                     isplaying = false;

                     return false;

                  }

              });

           } catch (Exception e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

       }

关于loadplay()的实现代码:

 

       public void loadplay() {

           try {

              // 初始化mediaplayer对象

              mediaPlayer = new MediaPlayer();

              // 设置音频流类型

              mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

              // 设置数据来源

              mediaPlayer.setDataSource(path);

              // 异步准备,因为下面有用到线程

              mediaPlayer.prepareAsync();

              // 设置播放视频的容器

              mediaPlayer.setDisplay(sh);

              System.out.println("loadplay");

 

           } catch (IllegalArgumentException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           } catch (SecurityException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           } catch (IllegalStateException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

 

       }

关于pause()的实现代码:

@Override

       public void pause() {

           System.out.println("pause");

               //如果暂停按钮的文字为”继续“,则进行音乐的播放,将文字改为”暂停“

           if ("继续".equals(pause.getText().toString())) {

              mediaPlayer.start();

              pause.setText("暂停");

              return;

           }

//如果当前对象存在并且正在播放中,则停止音乐播放,将文字改为”继续“

           if (mediaPlayer != null && mediaPlayer.isPlaying()) {

              mediaPlayer.pause();

              pause.setText("继续");

           }

 

       }

关于replay()的实现代码:

 

@Override

       public void replay() {

           System.out.println("replay");

           //当正在播放时以及播放完成时,都要可以进行重播

           if (mediaPlayer != null) {

                //如果暂停按钮的文字为“继续“,则将暂停按钮的文字改为“暂停“

              if ("继续".equals(pause.getText().toString())) {

                  pause.setText("暂停");

              }

              play.setEnabled(false);

//从位置0开始播放

              play(0);

           }

       }

有关stop()的实现代码:

@Override

       public void stop() {

           System.out.println("stop");

             //当前对象不为空并且正在播放时 ,执行停止操作

           if (mediaPlayer != null && mediaPlayer.isPlaying()) {

              Toast.makeText(getApplicationContext(), "停止播放",

                     Toast.LENGTH_SHORT).show();

              mediaPlayer.stop();

              // 停止后要释放资源

              mediaPlayer.release();

              // 将mediaplayer变成空

              mediaPlayer = null;

              // "播放按钮"设置为可点击

              play.setEnabled(true);

              // 标记该文件为“不在播放”

              isplaying = false;

           }

 

       }

 

    }

下面是关于activity中按钮点击的一些设置:

    @Override

    public void onClick(View v) {

       switch (v.getId()) {

       case R.id.play:

           musicPlayer.play(0);

       if(!replay.isEnabled()){

               replay.setEnabled(true);

           }if(!pause.isEnabled()){

               pause.setEnabled(true);

           }

           break;

       case R.id.pause:

           musicPlayer.pause();

           break;

       case R.id.replay:

           musicPlayer.replay();

           break;

       case R.id.stop:

           musicPlayer.stop();

            replay.setEnabled(false);

            pause.setEnabled(false);

           break;

       }

 

    }

接下来就是更新进度条的操作了:

// 更新进度条

       handler = new Handler() {

 

           @Override

           public void handleMessage(Message msg) {

              switch (msg.what) {

//获取歌曲的长度,设置为最大进度

              case MAXPROGRESS:

                  int max = msg.arg1;

                  sb.setMax(max);

                  System.out.println("max:" + max);

                  break;

//获取歌曲当前的位置,更新进度条

              case UPDATEROGRESS:

                  int update = msg.arg1;

                  sb.setProgress(update);

                  //System.out.println("ACTIVITY里更新进度:" + update);

                  break;

              }

 

           }

       };

接下来是进度条的进度改变的监听事件的设置:

sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

             //当放开对进度条的拖动时,进行从该位置开始播放音乐

           @Override

           public void onStopTrackingTouch(SeekBar seekBar) {

              // 获取当前进度

              int position = seekBar.getProgress();

              // 从当前进度开始播放

              musicPlayer.play(position);

              //改变“暂停按钮”的文字为“暂停”

              if("继续".equals(pause.getText().toString())){

                  pause.setText("暂停");

              }

 

           }

 

           @Override

           public void onStartTrackingTouch(SeekBar seekBar) {

              // TODO Auto-generated method stub

 

           }

 

           @Override

           public void onProgressChanged(SeekBar seekBar, int progress,

                  boolean fromUser) {

              //System.out.println("onProgressChanged");

 

           }

       });

代码就这些了。果然贴代码是最爽的,有想要工程文件的可以私信给我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值