Android 视频、音频 VideoView|MediaPlayer


1. 系统播放控件VideoView使用

1. videoview.setVideoURI(uri);   // 设置播放地址,可以是网络地址http、rtsp、mms
2.  videoview.setMediaController(new MediaController(this)); // 设置播放控制面板,系统自带的
3.  播放监听,异步加载,可能是网络地址
  
 videoview.setOnPreparedListener(new OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                videoview.start();
                //handler.sendEmptyMessage(PROGRESS);
                //1.视频的总时长,关联总长度
                int duration = videoview.getDuration();
                seekbar_video.setMax(duration);
                //2.发消息
                handler.sendEmptyMessage(PROGRESS);
            }
        });
4.   播放出错监听:
 videoview.setOnErrorListener(new OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                // TODO Auto-generated method stub
                // 播放出错,释放资源,跳入往万能播放器
                if(videoview != null){
                    videoview.stopPlayback();
                }
                finish();
              // 返回false,会弹出对话框出错对话框系统的
                return false;
            }
        });
5.  播放完成监听:
     videoview.setOnCompletionListener(new OnCompletionListener() {
            
            @Override
            public void onCompletion(MediaPlayer mp) {
              //切换到下一个
                Toast.makeText(VideoViewActivity.this, "播放完成", Toast.LENGTH_LONG).show();
            }
        });

6. 视频SeekBar的更新:
在 hander中每秒获取videoview.getCurrentPosition()当前播放进度,当前播放的毫秒数,除上视频总的秒数, 更新进度
  // 设置播放进度
seekbar_video.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
})

7.   播放缓冲:
   系统videoView默认支持缓存,获取缓冲值,获取当前缓存的毫秒数,更新到进度条上即可
    int buffer = videoview.getBufferPercentage();//0~100
8.   // 监听视频播放卡
// 系统api,如何是m3u8直播,无法获取当前播放进度,用这个
// 系统apibug,有时候卡了,不卡了,不卡了的方法没有回调
videoview.setOnInfoListener(new MediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(MediaPlayer mp, int what, int extra) {
        }
}
  如果可以获取当前播放进度,用下面的, 直播m3u8获取当前进度为0,把视频切成一段一段,每一段都是完整的有头
  比如一个mp4,有一个总的头,记录视频信息
      int buffer = currentPosition - precurrentPosition;
    //currentPosition 当前播放的进度,已经播放的时间毫秒数
    // 如何判断是否卡顿,在1S内,当前进度-上次进度<1000ms,那么在1S内,没有达到播放要求,卡顿
                        if (buffer < 500) {   
                            //视频卡了
                            ll_buffer.setVisibility(View.VISIBLE);
                        } else {
                            //视频不卡了
                            ll_buffer.setVisibility(View.GONE);
                        }
                        
9. 获取网速: 
     如何实现:每隔两秒调用一次网速api,更新UI
     
     
     基于 FFmpeg 封装开源Android播放器  Vitamio | VLC
=====================================================================

  代码实现:

package com.itheima.videoplay;

import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.support.annotation.RequiresApi;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

import java.text.SimpleDateFormat;
import java.util.Date;

public class VideoViewActivity extends Activity {

	VideoView videoview;
	Uri uri;
	TextView info,playtime,netspeed,tvSystemTime;
	private LinearLayout ll_buffer;
	SeekBar seekbar_video,seekbar_voice;
	Utils utils;
	private int precurrentPosition;  // 上一次播放进度
	private static final int SHOW_SPEED = 3; // 网速
	// 视频更新进度
	private static final int PROGRESS = 1;
	private int currentVoice;  // 当前的音量
	private boolean isNetUri;


	Handler handler=new Handler(){
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch (msg.what) {
			    case  PROGRESS:
			    	// //6. 视频SeekBar的更新:
					//1.得到当前的视频播放进程,就是毫秒
					int currentPosition = videoview.getCurrentPosition();//0
				//	playtime.setText("当前播放时间是:"+currentPosition);
					playtime.setText("当前播放时间是:"+utils.stringForTime(currentPosition));


					//3.SeekBar.setProgress(当前进度);
					seekbar_video.setProgress(currentPosition);

				//	7.   播放缓冲:
					if (isNetUri) {
						//只有网络资源才有缓存效果
						int buffer = videoview.getBufferPercentage();//0~100
						// 系统videoView默认支持缓存,获取缓冲,更新到进度条上即可
						int totalBuffer = buffer * seekbar_video.getMax();
						int secondaryProgress = totalBuffer / 100;
						seekbar_video.setSecondaryProgress(secondaryProgress);
					} else {
						//本地视频没有缓冲效果
						seekbar_video.setSecondaryProgress(0);
					}


					// 8.   // 监听视频播放卡
					if(videoview.isPlaying()){
						int buffer = currentPosition - precurrentPosition;
						//  原理:
						//  currentPosition 当前播放的进度,已经播放的时间毫秒数
						//  如何判断是否卡顿,在1S内,当前进度-上次进度<1000ms,那么在1S内,没有达到播放要求
						if (buffer < 500) {
							//视频卡了
							ll_buffer.setVisibility(View.VISIBLE);
						} else {
							//视频不卡了
							ll_buffer.setVisibility(View.GONE);
						}
					}else{
						ll_buffer.setVisibility(View.GONE);
					}
					precurrentPosition = currentPosition;

					tvSystemTime.setText("系统时间:"+getSysteTime()+"");

					//3.每秒更新一次
					handler.removeMessages(PROGRESS);
					handler.sendEmptyMessageDelayed(PROGRESS, 1000);

			        break;
				case SHOW_SPEED://显示网速
					//9. 获取网速: 
					//1.得到网络速度
					String netSpeed = utils.getNetSpeed(VideoViewActivity.this);
					//显示网络速
//					tv_laoding_netspeed.setText("玩命加载中..."+netSpeed);
//					tv_buffer_netspeed.setText("缓存中..."+netSpeed);
					netspeed.setText("网速:"+netSpeed);

					//2.每两秒更新一次
					handler.removeMessages(SHOW_SPEED);
					handler.sendEmptyMessageDelayed(SHOW_SPEED, 2000);

					break;
			}
		}
	};

	@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_video_view);
	    info = (TextView) findViewById(R.id.info);
		ll_buffer = (LinearLayout) findViewById(R.id.ll_buffer);
		playtime= findViewById(R.id.playtime);  // 当前播放时间
		netspeed=  findViewById(R.id.netspeed);  // 网络速度
		seekbar_video = findViewById(R.id.seekbar_video); // 进度条,缓存进度条
		tvSystemTime = findViewById(R.id.tvSystemTime); // 系统时间
		seekbar_voice = findViewById(R.id.seekbar_voice); // 设置音量
		utils=new Utils();
		// 获取点击视频,选择自己播放器的地址url
	     uri = getIntent().getData();//文件夹,图片浏览器
		 if(uri==null){
			 uri=Uri.parse("http://vfx.mtime.cn/Video/2018/10/22/mp4/181022083653874222.mp4");
		 }
		 if(utils.isNetUri(uri.toString())){
			 isNetUri=true;
		 }
		 videoview=  findViewById(R.id.videoview);
		 String savePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1moviestest/hello.mp4";
		// videoview.setVideoPath(savePath);
///1. videoview.setVideoURI(uri);   // 设置播放地址,可以是网络地址http、rtsp、mms
		  videoview.setVideoURI(uri);
		  info.setText(uri.toString());

		 // 2.  videoview.setMediaController(new MediaController(this)); // 设置播放控制面板,系统自带的
		 videoview.setMediaController(new MediaController(this));

		handler.sendEmptyMessage(SHOW_SPEED);

		// 3.  播放监听,异步加载,可能是网络地址
		 videoview.setOnPreparedListener(new OnPreparedListener() {

			@Override
			public void onPrepared(MediaPlayer mp) {
				videoview.start();
				//handler.sendEmptyMessage(PROGRESS);

				//1.视频的总时长,关联总长度
				int duration = videoview.getDuration();
				seekbar_video.setMax(duration);
				//2.发消息
				handler.sendEmptyMessage(PROGRESS);

			}
		});
	//	4.   播放出错监听:
		 videoview.setOnErrorListener(new OnErrorListener() {

			@Override
			public boolean onError(MediaPlayer mp, int what, int extra) {
				// TODO Auto-generated method stub
				// 播放出错,释放资源,跳入往万能播放器
				/***
				 *  播放出错原因:
				 *  1. 播放视频格式不支持,        利用万能播放器
				 *  2. 播放网络视频,网络中断,
				 *     2.1. 网路确实断了,提示用户网络断了
				 *     2.2. 网络断断续续, 尝试重新播放,重新设置地址,设置进度
				 *  3. 播放本地视频,中间有空白: 重新下载资源
				 */
				if(videoview != null){
					videoview.stopPlayback();
				}
				finish();
              // 返回false,会弹出对话框出错对话框系统的
				return false;
			}
		});
	//	5.  播放完成监听:
		 videoview.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer mp) {
			  //切换到下一个
				Toast.makeText(VideoViewActivity.this, "播放完成", Toast.LENGTH_LONG).show();
			}
		});



		// 监听视频播放卡, Android4.2 以后才出现
		// 卡顿以后显示加载圈圈
		// 系统apibug多次测试,有时候卡了,不卡了,不卡了的方法没有回调
		//
		/*videoview.setOnInfoListener(new MediaPlayer.OnInfoListener() {
			@Override
			public boolean onInfo(MediaPlayer mp, int what, int extra) {
				switch (what) {
					case MediaPlayer.MEDIA_INFO_BUFFERING_START://视频卡了,拖动卡
                    Toast.makeText(VideoViewActivity.this, "卡了", Toast.LENGTH_SHORT).show();
						ll_buffer.setVisibility(View.VISIBLE);
						break;

					case MediaPlayer.MEDIA_INFO_BUFFERING_END://视频卡结束了,拖动卡结束了
                    Toast.makeText(VideoViewActivity.this, "卡结束了", Toast.LENGTH_SHORT).show();
						ll_buffer.setVisibility(View.GONE);
						break;
				}
				return true;
			}
		});*/

 //6. 视频SeekBar的更新:
		seekbar_video.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
			// 当手指滑动的时候会引起进度变化,会回调这个方法
			// 自动更新,上面hander设置progress, fromUser是flase
			// 当fromUser=true,用户手动拖动更新
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
				if(fromUser){
					videoview.seekTo(progress);
				}
			}
  // 当手指触碰回调这个方法
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
			}
  // 当手指离开的时候回调这个方法
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
			}
		});
	}
	@Override
	protected void onStop() {
		super.onStop();  //细节,把super.stop放在下面,首先关闭自己,然后关闭系统的
	}

	@Override
	protected void onDestroy() {
		handler.removeCallbacksAndMessages(null);
		super.onDestroy();
	}
	/**
	 * 得到系统时间
	 *
	 * @return
	 */
	public String getSysteTime() {
		SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
		return format.format(new Date());
	}
}

xml布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#000"
    >
    
    <VideoView
        android:id="@+id/videoview"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_centerInParent="false"
       />
        
      <!-- 显示文件信息 -->
    <TextView
        android:id="@+id/info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/videoview"
       android:maxLines="1"
        android:textColor="#fff"
        android:text="地址:"
        />


    <!-- 显示当前播放时间 -->
    <TextView
        android:id="@+id/playtime"
        android:ellipsize="end"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
        android:textColor="#fff"
        android:text="时长:"
       android:layout_below="@+id/info"/>

    <!-- 显示播放网速 -->
    <TextView
        android:id="@+id/netspeed"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/playtime"
        android:text="网速:"
        android:textColor="#fff"/>


    <!-- SeekBar: thumb指示器头部-->
    <!-- 如何修改,在android-sdk-windows6\platforms\android-24\data\res\values
     下styles.xml中找Widget.SeekBar 找到progressDrawable,

     同理: 系统Progress的样式也可以这样修改,在sdk的源码下找到对应的Style,然后找到样式,修改即可
     问题:seekbar设置thumb后在真机出现黑边(不透明区域)
     https://blog.csdn.net/qq_19269585/article/details/77461311
     解决:设置    android:splitTrack="false"
     -->
    <LinearLayout
        android:id="@+id/seekbar_video_ll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_player_bottom_seekbar"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_below="@+id/netspeed">
        <SeekBar
            android:id="@+id/seekbar_video"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:maxHeight="6dp"
            android:minHeight="6dp"
            android:progressDrawable="@drawable/progress_horizontal"
            android:thumb="@drawable/progress_thumb"
            android:splitTrack="false"
           />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/powersource"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/seekbar_video_ll"
        >

        <TextView
            android:id="@+id/tvSystemTime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="当前时间:"
            android:textColor="#fff"
            android:layout_toRightOf="@+id/iv_battery"
            android:layout_marginLeft="20dp"
            />

    </LinearLayout>



    <!--  原型加载进度   -->
    <!--<include layout="@layout/ll_buffer" android:id="@+id/ll_buffer"/>-->

    <LinearLayout
        android:id="@+id/ll_buffer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:orientation="horizontal"
        android:padding="3dp"
        android:visibility="gone">



        <ProgressBar
            style="?android:attr/progressBarStyle"
            android:layout_width="60dp"
            android:layout_height="60dp" />

        <TextView
            android:id="@+id/tv_buffer_netspeed"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="3dp"
            android:gravity="center"
            android:text="缓存中...30kb/s"
            android:textColor="#f00"
            android:textSize="10sp" />

    </LinearLayout>
   
</RelativeLayout>

效果图:

2. 把自己播放器注册为系统播放器:

功能清单配置文件:

<activity
    android:name=".VideoViewActivity"
    android:label="置哥播放器">

    <!-- 视频播放意图过滤器 -->
    <!-- 源码:android_source2.3\packages\apps\Gallery\AndroidManifest.xml下  com.android.camera.MovieView中 -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="rtsp" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />

        <data android:mimeType="video/*" />
        <data android:mimeType="application/sdp" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="http" />
        <data android:mimeType="video/mp4" />
        <data android:mimeType="video/3gp" />
        <data android:mimeType="video/3gpp" />
        <data android:mimeType="video/3gpp2" />
    </intent-filter>
</activity>

 Activity代码实现:

 public class VideoViewActivity extends Activity {
   @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_video_view);
			// 获取点击视频,选择自己播放器的地址url
	     uri = getIntent().getData();//文件夹,图片浏览器
		 if(uri==null){
			 uri=Uri.parse("http://vfx.mtime.cn/Video/2018/10/22/mp4/181022083653874222.mp4");
		 }
		  videoview.setVideoURI(uri);
		}
   
   }

3.  MediaPlayer(播放器)+ SurfaceView(幕布) 实现视频播放:
   

// mediaPlayer 解码以后把画面播放到幕布上
  mediaPlayer.setDisplay(sv.getHolder());
  int max=mediaPlayer.getDuration();   // 获取播放进度,播放监听

  SurfaceView:   电影屏幕
     双缓冲机制:
       A线程   加载数据
       B线程   显示界面
     
  SurfceHolder: 投影仪
  Thread:    工作人员,不断的摇

package com.itheima.videoplay;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;

public class MainActivity extends Activity implements OnClickListener{
    private EditText edt1;
    private Button bt_play;
    private Button bt_pause;
    private Button bt_replay;
    private Button bt_stop;
    private MediaPlayer mediaPlayer;
    private SurfaceView sv;
    private int currentPositon;
    private SeekBar sb;
    private boolean isplaying;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		copyVideo("hello.mp4");
        edt1=(EditText) findViewById(R.id.edt1);
        bt_play=(Button) findViewById(R.id.bt_play);
        bt_pause=(Button) findViewById(R.id.bt_pause);
        bt_replay=(Button) findViewById(R.id.bt_replay);
        bt_stop=(Button) findViewById(R.id.bt_stop);
        sv=(SurfaceView) findViewById(R.id.sv);
        sb=(SeekBar) findViewById(R.id.sb);
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				int process=seekBar.getProgress();
			 if(mediaPlayer!=null && mediaPlayer.isPlaying()){
				mediaPlayer.seekTo(process);
			  }
			}
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
			}
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
			}
		});
      /* 为了解决低版本模拟式添加的 */
     /* 下面设置Surface 不维护自己的 缓冲区,而是等待屏幕渲染引擎内容推动到客户端 */
      /* 2.2、2.3有问题模拟器,用1.6 */
        sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        // getHolder监听器
        // 当播放进入后台的时候,在重新进来,画面出现黑色,推出的时候SurfaceView.holder销毁了,重新设置一下sv的holder
        sv.getHolder().addCallback(new Callback() {
			// 退出时候,点击back或者home键
			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
			if(mediaPlayer!=null && mediaPlayer.isPlaying()){
				currentPositon=mediaPlayer.getCurrentPosition();
				  stop();
		    	}
			}
			// 如果在次进来的时候,或者第一次创建surfaceView的时候
			@Override
			public void surfaceCreated(SurfaceHolder holder) {
	          if(currentPositon>0){
	        	  play(currentPositon);
	          }
			}
         // 画布大小改变的时候
			@Override
			public void surfaceChanged(SurfaceHolder holder, int format, int width,
					int height) {
			}
		});
        bt_play.setOnClickListener(this);
        bt_pause.setOnClickListener(this);
        bt_replay.setOnClickListener(this);
        bt_stop.setOnClickListener(this);
    }

	// 数据准备
	private void copyVideo(final String dbname) {
		new Thread(){
			public void run() {
				try {
					File file = new File(getFilesDir(),dbname);
					if(file.exists()&&file.length()>0){
						Log.e("denganzhi1","数据库是存在的。无需拷贝");
						return ;
					}
					InputStream is = getAssets().open(dbname);
					FileOutputStream fos  = openFileOutput(dbname, MODE_PRIVATE);
					byte[] buffer = new byte[1024];
					int len = 0;
					while((len = is.read(buffer))!=-1){
						fos.write(buffer, 0, len);
					}
					is.close();
					fos.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			};
		}.start();
	}



	@Override
	public void onClick(View v) {
	   switch (v.getId()) {
	case R.id.bt_play:
		 play(0);
		break;
	case R.id.bt_pause:
		pause();
		break;
	case R.id.bt_replay:
		replay();
		break;
	case R.id.bt_stop:
		stop();
		break;
    	}
	}

// 在服务中写
   private void play(final int currentPositon) {
 //  String path=edt1.getText().toString().trim();
   final String savePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1moviestest/hello.mp4";

	   File file = new File(getFilesDir(),"hello.mp4");
      if(file.exists()==false){
      	Toast.makeText(MainActivity.this,"视频文件不存在",Toast.LENGTH_SHORT).show();
      	return;
	  }else{
		  try {
			  mediaPlayer = new MediaPlayer();
			  // 音频流的类型 STREAM_Ring:铃声  STREAM_alarm:更短
			  mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
			  // 把视频显示在 SurfaceView  的容器中
			  // 把画面播放到 SurfaceView的幕布上
			  mediaPlayer.setDisplay(sv.getHolder());
			  // 这里路径:可以使网络路径,http://xxx:8080/aa.3gp
			  mediaPlayer.setDataSource(file.getAbsolutePath());
			  // 这里是同步的 prepare()没有准备完毕,start()不能执行,一般不用
			  //	  mediaPlayer.prepare();
			  //  mediaPlayer.start();
			  //   这样做就不会造成主线程的柱塞,如果数据比较大,在子线程中准备
			  mediaPlayer.prepareAsync();//异步的,在另一个线程中跑, 什么时候准备好,在回调中
			  mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

				  @Override
				  public void onPrepared(MediaPlayer mp) {
					  mediaPlayer.start();

					  // 设置最大的总长度
					  int max=mediaPlayer.getDuration();
					  sb.setMax(max);

					  mediaPlayer.seekTo(currentPositon);

					  // 在子线程中跟新进度
					  new Thread(){
						  public void run() {
							  isplaying=true;
							  while(isplaying){
								  int positon=mediaPlayer.getCurrentPosition();
								  sb.setProgress(positon);
								  SystemClock.sleep(500);
							  }
						  }
					  }.start();
				  }
			  });

			  // 播放完以后的监听器
			  mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

				  @Override
				  public void onCompletion(MediaPlayer mp) {
					  bt_play.setEnabled(true);
				  }
			  });
			  // 如果音频有问题,播着挂了
			  mediaPlayer.setOnErrorListener(new OnErrorListener() {
				  @Override
				  public boolean onError(MediaPlayer mp, int what, int extra) {
					  bt_play.setEnabled(true);
					  isplaying=false;
					  return false;
				  }
			  });

			  // 播放的时候不可以点击
			  bt_play.setEnabled(false);

		  } catch (Exception e) {
			  Toast.makeText(this, "视频播放失败", 1).show();
			  e.printStackTrace();
		  }
	  }
	}
   
	// 暂停
	private void pause() {
		// 如果是继续,那么播放即可
		if(bt_pause.getText().toString().trim().equals("继续")){
	      mediaPlayer.start();
		  bt_pause.setText("暂停");  
		 Toast.makeText(this, "继续", 1).show();
		  return;
		}
		
		 if(mediaPlayer!=null && mediaPlayer.isPlaying()){
			    mediaPlayer.pause();
			    bt_pause.setText("继续");
			    isplaying=false;
			    return;
			  }  	
	}
// 如果是一个网络资源播放
/* 重播: stop() -> release() -play()
 */
	private void replay() {
		 if(mediaPlayer!=null && mediaPlayer.isPlaying()){
			mediaPlayer.seekTo(0);
			return ;
		  }  
		 play(0);
	}

	private void stop() {
	  if(mediaPlayer!=null && mediaPlayer.isPlaying()){
		  mediaPlayer.stop();
		  mediaPlayer.release();
		  mediaPlayer=null;
	  }
	}
}

xml布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/edt1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" 
        android:text="/sdcard/mm.mp4">

        <requestFocus />
    </EditText>

    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
      
    
    <Button
        android:id="@+id/bt_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="播放" />
    
     <Button
        android:id="@+id/bt_pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="暂停" />
    
    <Button
        android:id="@+id/bt_replay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="重播" />
   
    
    <Button
        android:id="@+id/bt_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止" />
    </LinearLayout>
 
    <SurfaceView android:id="@+id/sv"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"/>
  

</LinearLayout>

效果图:

案例2:MediaPlayer+ SurfaceView使用
* 1. 设置播放属性
* 2. 2.  设置  高度 和宽度  等比例 横屏

package com.denganzhi.camera2;

import android.Manifest;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
import android.media.MediaPlayer;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.RelativeLayout;

import java.io.IOException;

/***
 *  MediaPlayer+ SurfaceView使用
 * 1. 设置播放属性
 * 2. 2.  设置  高度 和宽度  等比例 横屏
 */
public class MediaPlayActivity extends AppCompatActivity {

    private SurfaceView surfaceView;
    private MediaPlayer mPlayer;
    private ImageButton playBn, pauseBn, stopBn;
    // 记录当前视频的播放位置
    int position = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_play);
        // 创建MediaPlayer
        mPlayer = new MediaPlayer();
        surfaceView = this.findViewById(R.id.surfaceView);
        // 设置播放时打开屏幕
        surfaceView.getHolder().setKeepScreenOn(true);
        surfaceView.getHolder().addCallback(new SurfaceListener());
        // 获取界面上的三个按钮
        playBn = findViewById(R.id.play);
        pauseBn = findViewById(R.id.pause);
        stopBn = findViewById(R.id.stop);
        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0x123);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions, @NonNull int[] grantResults)
    {
        if (requestCode == 0x123 && grantResults[0] ==
                PackageManager.PERMISSION_GRANTED) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            View.OnClickListener listener = source -> {
                switch (source.getId())
                {
                    // “播放”按钮被单击
                    case R.id.play:
                        play();
                        break;
                    // “暂停”按钮被单击
                    case R.id.pause:
                        if (mPlayer.isPlaying()) {
                            mPlayer.pause();
                        } else {
                            mPlayer.start();
                        }
                        break;
                    // “停止”按钮被单击
                    case R.id.stop:
                        if (mPlayer.isPlaying())
                            mPlayer.stop();
                }
            };
            // 为三个按钮的单击事件绑定事件监听器
            playBn.setOnClickListener(listener);
            pauseBn.setOnClickListener(listener);
            stopBn.setOnClickListener(listener);
        }
    }

    private void play()
    {
        mPlayer.reset();
        // 1. 设置播放属性
        AudioAttributes audioAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build();
        mPlayer.setAudioAttributes(audioAttributes);
        try {
            // 设置需要播放的视频
            mPlayer.setDataSource(Environment.getExternalStorageDirectory().toString()  + "/1moviestest/hello.mp4");
            // 把视频画面输出到SurfaceView
            mPlayer.setDisplay(surfaceView.getHolder());  // ①
            mPlayer.prepare();
        }
        catch(IOException e)
        { e.printStackTrace(); }
        // 获取窗口管理器
        WindowManager wManager = getWindowManager();
        DisplayMetrics metrics = new DisplayMetrics();
        // 获取屏幕大小
        wManager.getDefaultDisplay().getMetrics(metrics);
        // 设置视频保持纵横比缩放到占满整个屏幕
        //2.  设置  高度 和宽度  等比例 横屏
        surfaceView.setLayoutParams(new RelativeLayout.LayoutParams(metrics.widthPixels,
                mPlayer.getVideoHeight() * metrics.widthPixels / mPlayer.getVideoWidth()));
        mPlayer.start();
    }

    private class SurfaceListener implements SurfaceHolder.Callback
    {
        @Override
        public void surfaceCreated(SurfaceHolder holder)
        {
            if (position > 0) {
                // 开始播放
                play();
                // 并直接从指定位置开始播放
                mPlayer.seekTo(position);
                position = 0;
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
        {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder)
        {

        }
    }

    // 当其他Activity被打开时,暂停播放
    @Override
    public void onPause()
    {
        super.onPause();
        if (mPlayer.isPlaying()) {
            // 保存当前的播放位置
            position = mPlayer.getCurrentPosition();
            mPlayer.stop();
        }
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        // 停止播放
        if (mPlayer.isPlaying()) mPlayer.stop();
        // 释放资源
        mPlayer.release();
    }
}
布局文件xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="360dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/play" />

        <ImageButton
            android:id="@+id/pause"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/pause" />

        <ImageButton
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/stop" />
    </LinearLayout>
</RelativeLayout>

 

4.  android 系统提供 MediaPlayer 来实现音乐播放:

Mediapalyer生命周期图:音视频框架都要根据生命周期来封装

音乐播放实现核心代码:

  1. 服务中音乐播放
  2. 歌词解析显示 canvas.setTranslateY 移动

package com.itheima.videoplay;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.drawable.AnimationDrawable;
import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

import com.itheima.videoplay.words.LyricUtils;
import com.itheima.videoplay.words.MyLyricView;

import java.io.File;

import static com.itheima.videoplay.MusicPlayerService.OPENAUDIO;

public class AudioActivity extends Activity {
    private ImageView ivIcon;
    private Button playandpause,pausemusic;
    private ServiceConnection conn=null;
    private IMusicPlayerService musicPlayerService;
    MyReceiver myReceiver;
    TextView tv_artist,tv_name,currentplaytext;
    SeekBar seekbar_audio;
    private Utils utils;
    private static final int PROGRESS = 1;  //进度更新
    private static final int SHOW_LYRIC = 2;  // 显示歌词

    private MyLyricView myLyricView;

    private BaseVisualizerView baseVisualizerView;
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case PROGRESS :
                    //Log.e("dengnazhi1","---------------过来了--》");
                    try {
                        int currentPosition = musicPlayerService.getCurrentPosition();
                        seekbar_audio.setProgress(currentPosition);
                        //3.时间进度跟新
                        String time = utils.stringForTime(currentPosition)+"/"+utils.stringForTime(musicPlayerService.getDuration());
                        currentplaytext.setText(time);
                       // Log.e("denganzhi1","curretnPostion:"+currentPosition+"time:"+time);
                        // 每一秒钟更新一次
                        handler.removeMessages(PROGRESS);
                        handler.sendEmptyMessageDelayed(PROGRESS,1000);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                        Log.e("dengnazhi1",e.getMessage()+"");
                    }
                    break;

                case  SHOW_LYRIC:
                   // 3.  当前播放歌曲进度实时更新歌词绘制位置
                        // 1. 得到当前播放进度
                        // 2. 根据当前播放进度,在Lyric歌词类中计算出哪一句高亮显示
                     //   当前播放的那一句
                    // 3. 实时发送消息,更新LyricView
                    try {

                        //1.得到当前的进度
                        int currentPosition = musicPlayerService.getCurrentPosition();
                        //2.把进度传入ShowLyricView控件,并且计算该高亮哪一句
                        //然后根据当前播放位置,找出播放的那一句,高亮显示
                        myLyricView.setshowNextLyric(currentPosition);
                        //3.实时的发消息
                        handler.removeMessages(SHOW_LYRIC);
                        handler.sendEmptyMessage(SHOW_LYRIC);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio);

        myReceiver=new MyReceiver();
        IntentFilter intentFilter=new IntentFilter();
        intentFilter.addAction(OPENAUDIO);
        registerReceiver(myReceiver,intentFilter);

        utils = new Utils();
        //ivIcon = (ImageView)findViewById( R.id.iv_icon );
        tv_name = (TextView)findViewById(R.id.tv_name);
        tv_artist = (TextView)findViewById(R.id.tv_artist);
        playandpause = (Button)findViewById(R.id.playandpause);
        pausemusic = (Button)findViewById(R.id.pausemusic);
        seekbar_audio = (SeekBar)findViewById(R.id.seekbar_audio);
        myLyricView = findViewById(R.id.myLyricView);
        currentplaytext = (TextView)findViewById(R.id.currentplaytext);
        baseVisualizerView = findViewById(R.id.baseVisualizerView);

//        ivIcon.setBackgroundResource(R.drawable.animation_list);
//        AnimationDrawable rocketAnimation = (AnimationDrawable) ivIcon.getBackground();
//        rocketAnimation.start();

          // 拖动播放音乐
        seekbar_audio.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if(fromUser) {
                    try {
                        musicPlayerService.seekTo(progress);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });

  // 绑定服务
        if(conn==null){
            conn=new ServiceConnection() {
                @Override
                public void onServiceDisconnected(ComponentName name) {
                }

                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    musicPlayerService=(IMusicPlayerService) service;
                    if(musicPlayerService!=null ){
                        try {
                            // 从通知栏进来或者关闭这个这个页面以后从列表页面进来
                            if(musicPlayerService.isPlaying()==false){
                                musicPlayerService.openAudio(10000);
                            }else{
                                // 退出Acticity重新进来,重新绑定,此时正在播放音乐,直接更新UI即可
                                // 也可以不发送广告,这里是在主线程,可以直接更新UI
//                                Intent intent = new Intent(OPENAUDIO);
//                                sendBroadcast(intent);
                                  receiverUpateUI();
                            }
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            bindService(new Intent(AudioActivity.this, MusicPlayerService.class), conn, Context.BIND_AUTO_CREATE );
            startService(new Intent(AudioActivity.this,MusicPlayerService.class));
        }


        // 播放下一首,开始播放
//        playandpause.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//
//                try {
//                    musicPlayerService.openAudio(10000);
//                } catch (RemoteException e) {
//                    e.printStackTrace();
//                }
//
//            }
//        });
        // 在当前音乐播放,暂停
        pausemusic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if(musicPlayerService != null && musicPlayerService.isPlaying()){
                        musicPlayerService.pause();
                        pausemusic.setText("播放");
                    }else{
                        musicPlayerService.start();
                        pausemusic.setText("暂停");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
        });
    }

    class  MyReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
                    // 歌曲准备好
           // Toast.makeText(AudioActivity.this,"歌曲准备好了",Toast.LENGTH_SHORT).show();
            receiverUpateUI();

            //Android应用源码音乐实时跳动频谱显示,网上找的 , 必须New 的时候有问题
            // https://www.cnblogs.com/Free-Thinker/p/4746938.html
           //  setupVisualizerFxAndUi();
        }
    }
    public void  receiverUpateUI(){
        try {
            tv_name.setText(musicPlayerService.getName());
            tv_artist.setText(musicPlayerService.getArtist());
            // 设置音乐播放器进度条最大值
            seekbar_audio.setMax(musicPlayerService.getDuration());
            // 开始更新进度
            handler.sendEmptyMessage(PROGRESS);



            String lyricPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1moviestest/235319.lrc";
            LyricUtils lyricUtils=new LyricUtils();
            lyricUtils.readLyricFile(new File(lyricPath));
           // lyrics=lyricUtils.getLyrics();
            myLyricView.setLyrics(lyricUtils.getLyrics());
            if(lyricUtils.isExistsLyric()){
                // 显示歌词
                handler.sendEmptyMessage(SHOW_LYRIC);
            }


        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private Visualizer mVisualizer;
    /**
     * 生成一个VisualizerView对象,使音频频谱的波段能够反映到 VisualizerView上
     */
    private void setupVisualizerFxAndUi()
    {

        try {
            int audioSessionid = musicPlayerService.getAudioSessionId();
            Log.e("denganzhi","audioSessionid=="+audioSessionid);
            mVisualizer = new Visualizer(audioSessionid);
            // 参数内必须是2的位数
            mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
            // 设置允许波形表示,并且捕获它
            baseVisualizerView.setVisualizer(mVisualizer);
            mVisualizer.setEnabled(true);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mVisualizer != null){
            mVisualizer.release();
        }
    }



    //    public  void pausemusic(View view) throws RemoteException {
//        musicPlayerService.pause();
//    }
//
//    public void pausemusicplay(View view) throws RemoteException {
//        musicPlayerService.start();
//    }
    public  void stopMusice(View view) throws RemoteException {
         musicPlayerService.stop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
// 移除消息
        handler.removeCallbacksAndMessages(null);
        // Activity死了,但是音乐还在播放
        // 绑定解除了,但是服务还是启动的,Activity和 服务器的核心关系
        if(conn!=null){
            unbindService(conn);
            conn=null;
        }
        if(myReceiver!=null){
            unregisterReceiver(myReceiver);
            myReceiver=null;
        }
    }
}

MusicPlayerService 服务中播放核心类

package com.itheima.videoplay;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import java.io.File;
import java.io.IOException;

public class MusicPlayerService extends Service {

    public static final String OPENAUDIO = "com.atguigu.mobileplayer_OPENAUDIO";
    private MediaPlayer mediaPlayer;

    public MusicPlayerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("denganzhi1","service---->oncreate-->");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
      return  new MyMusicBinder();
    }


    private void openAudio(int position){
        if (mediaPlayer != null) {  //必须这么做,没播放下一个之前必须重置化,取消上一个
//                mediaPlayer.release();
            mediaPlayer.reset();
        }
        try {

            mediaPlayer = new MediaPlayer();
            // 准备好
            mediaPlayer.setOnPreparedListener(new MyOnPreparedListener());
            mediaPlayer.setOnErrorListener(new MyOnErrorListener());
            mediaPlayer.setOnCompletionListener(new MyOnCompletionListener());
          //  String savePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1moviestest/water_hander.mp3";
          //  String savePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1moviestest/235319.mp3";
            File file = new File(getFilesDir(),"235319.mp3");
            mediaPlayer.setDataSource(file.getAbsolutePath());

            mediaPlayer.prepareAsync(); //准备好了在start


        } catch (IOException e) {
            e.printStackTrace();
        }

    }
   // 播暂停音乐
    private void pause() {
        if(mediaPlayer!=null && mediaPlayer.isPlaying()){
            mediaPlayer.pause();
        }
    //    manager.cancel(1);
    }

  // 停止
    private void stop() {
        if(mediaPlayer!=null){
            mediaPlayer.stop();
            mediaPlayer.reset();  // 重置
            mediaPlayer.release(); //释放资源
            mediaPlayer=null;
        }
        manager.cancel(1);

    }
    //是否在播放音频
    private boolean isPlaying(){
        if(mediaPlayer!=null && mediaPlayer.isPlaying()){
            return true;
        }else {
            return false;
        }

    }
    private String getName() {
        return "歌曲名称";
    }
    private String getArtist() {
      //  return mediaItem.getArtist();
        return "得到艺术家";
    }
    private NotificationManager manager;
    private void start() {
        if(mediaPlayer!=null){
            mediaPlayer.start();
        }
        //当播放歌曲的时候,在状态显示正在播放,点击的时候,可以进入音乐播放页面
        manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //最主要
        Intent intent = new Intent(this, AudioActivity.class);
        intent.putExtra("notification",true);//标识来自状态拦
        PendingIntent pendingIntent = PendingIntent.getActivity(this,1,intent,PendingIntent.FLAG_UPDATE_CURRENT);
        Notification notification = new Notification.Builder(this)
                .setSmallIcon(R.drawable.notification_music_playing)
                .setContentTitle("321音乐")
                .setContentText("正在播放:"+getName())
                .setContentIntent(pendingIntent)
                .build();
        manager.notify(1, notification);
    }
    //得到当前的播放进度
    private int getCurrentPosition() {
        if(mediaPlayer!=null && mediaPlayer.isPlaying()){
            return mediaPlayer.getCurrentPosition();
        }else{
            return 0;
        }

    }
    //得到当前音频的总时长
    private int getDuration() {
        if(mediaPlayer!= null && mediaPlayer.isPlaying()){
            return mediaPlayer.getDuration();
        }
        return 0;
    }


    /**==========================================================*/
    class MyOnErrorListener implements MediaPlayer.OnErrorListener {

        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
        //    next();
            return true;
        }
    }

    class  MyOnCompletionListener implements MediaPlayer.OnCompletionListener {

        @Override
        public void onCompletion(MediaPlayer mp) {
       //     next();
        }
    }

    class MyOnPreparedListener implements MediaPlayer.OnPreparedListener {

        @Override
        public void onPrepared(MediaPlayer mp) {
            //通知Activity来获取信息--广播
//            notifyChange(OPENAUDIO);
     //       EventBus.getDefault().post(mediaItem);
            Intent intent = new Intent(OPENAUDIO);
            sendBroadcast(intent);
            start();
        }
    }
    /**==========================================================*/


  // AIDL接口定义

    class  MyMusicBinder extends  IMusicPlayerService.Stub{
        MusicPlayerService service = MusicPlayerService.this;
        @Override
        public void openAudio(int position)  {
            service.openAudio(position);

        }

        @Override
        public void start() {
             service.start();
        }

        @Override
        public void pause()  {
            service.pause();
        }

        @Override
        public void stop()  {
             service.stop();
        }

        @Override
        public int getCurrentPosition() {
            return service.getCurrentPosition();
        }

        @Override
        public int getDuration()  {
            return service.getDuration();
        }

        @Override
        public String getArtist()  {
            return service.getArtist();
        }

        @Override
        public String getName()  {
            return service.getName();
        }

        @Override
        public String getAudioPath()  {
            return null;
        }

        @Override
        public void next()  {

        }

        @Override
        public void pre()  {

        }

        @Override
        public void setPlayMode(int playmode) {

        }

        @Override
        public int getPlayMode() {
            return 0;
        }
       // 用于播放暂停按钮切换判断
        @Override
        public boolean isPlaying()  {
              return service.isPlaying();
        }

        @Override
        public void seekTo(int position) {
            mediaPlayer.seekTo(position);
        }

        @Override
        public int getAudioSessionId() throws RemoteException {
            return mediaPlayer.getAudioSessionId();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(mediaPlayer!=null){
            mediaPlayer.stop();//停止
            mediaPlayer.reset();//重置
            mediaPlayer.release();//释放资源
            mediaPlayer = null;//赋空,如果不赋空那么会出现bug在播的时候
        }
    }
}

效果图:

 第三方优秀播放器:JieCaoVideoPlayer,  VideoView第三方封装、播放出错:UniversalVideoView

 源码地址: https://download.csdn.net/download/dreams_deng/12254622

5.  android 音效播放api 

如果需要 播放密集、短促音效的时候,MediaPlayer不合适,缺点:

1.   资源占用过大 ,延迟时间比较长       2. 不支持同时播放 多个音效   

public class MainActivity extends AppCompatActivity {

    SoundPool.Builder soundPoolBuild=null;
    int soundId;
    SoundPool soundPool;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sv= (SurfaceView) findViewById(R.id.sv);
        // 声音池
        soundPoolBuild=new SoundPool.Builder();
        soundPool= soundPoolBuild.build();
        // 加载声音到声音池
        soundId = soundPool.load(this,R.raw.abc,1);

    }
	 public void playSound(View view) {
  /**  
   *   play(int soundID, float leftVolume, float rightVolume,
            int priority, int loop, float rate)
   * 
   *   播放哪个声音
   *   leftVolume、rightVolume 指定左、右的音量
   *   priority  : 指定播放声音优先级,数值越大,优先级越高
   *   loop : 指定是否循环     0不循环 -1循环
   *   rate: 播放比率  数值从0.5到 2   1为正常 
   */
 soundPool.play(soundId,1.0f,1.0f,0,-1,1.0f);

    }
	
}

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值