Android之MediaPlayer(两种)基本使用方式
1.播放应用的资源文件(res/raw/)
java代码示例:
MainActivity.java
public class MainActivity extends AppCompatActivity { private MediaPlayer mediaPlayer; private SeekBar seekBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); seekBar = (SeekBar) findViewById(R.id.seekBar); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {} @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) { //获取拖动结束之后的位置 int progress=seekBar.getProgress(); //跳转到某个位置播放 mediaPlayer.seekTo(progress); } }); } public void play(View view){ ImageButton imageButton= (ImageButton) view; if(mediaPlayer==null){ //播放内存卡中的音频 mediaPlayer=new MediaPlayer(); //设置类型 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); /* 得到文件路径 *//* 注:文件存放在SD卡的根目录,一定要进行prepare()方法,使硬件进行准备 */ File file = new File(Environment.getExternalStorageDirectory(),"a.mp3"); try{ /* 为MediaPlayer 设置数据源 */ mediaPlayer.setDataSource(file.getAbsolutePath()); /* 准备 */ mediaPlayer.prepare(); }catch(Exception ex){ ex.printStackTrace(); } mediaPlayer.start(); // 把图标变为暂停图标 imageButton.setImageResource(android.R.drawable.ic_media_pause); //获取音乐的总时长 int duration=mediaPlayer.getDuration(); //设置进度条的最大值为音乐总时长 seekBar.setMax(duration); new MyThread().start();// //实例化MediaPlayer/// mediaPlayer = MediaPlayer.create(this, R.raw.one);//// mediaPlayer.start();// //把图标变为暂停图标// imageButton.setImageResource(android.R.drawable.ic_media_pause);// //获取音乐的总时长// int duration=mediaPlayer.getDuration();// //设置进度条的最大值为音乐总时长// seekBar.setMax(duration);// new MyThread().start(); }else if(mediaPlayer.isPlaying()){ mediaPlayer.pause(); //把图标变为播放图标 imageButton.setImageResource(android.R.drawable.ic_media_play); }else{ mediaPlayer.start(); //把图标变为暂停图标 imageButton.setImageResource(android.R.drawable.ic_media_pause); } } class MyThread extends Thread{ @Override public void run() { super.run(); while(seekBar.getProgress()<=seekBar.getMax()){ //获取当前位置音乐播放的位置 int currentPosition=mediaPlayer.getCurrentPosition(); //让进度条滚动起来 seekBar.setProgress(currentPosition); } } }}
简单的一个布局文件:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
程序说明:
以上例子介绍了MediaPlayer在播放应用内的音频和播放内存卡中的音频步骤和方式.
下面介绍的是另外一种方法
2.播放外部存储上的音频资源文件(sdcard)
代码实现
1)导入歌曲到手机SD卡的qqmusic/song目录中,这里我随便导入了几首歌曲:《三生三世》、《爱丫爱丫》、《安和桥》和《和你在一起》,路径可随自己而定。
新建一个类MusicService继承Service,在类中定义一个MyBinder,有一个方法用于返回MusicService本身,在重载onBind()方法的时候返回
public final IBinder binder = new MyBinder(); public class MyBinder extends Binder{ MusicService getService() { return MusicService.this; } } /** * onBind 是 Service 的虚方法,因此我们不得不实现它。 * 返回 null,表示客服端不能建立到此服务的连接。 */ @Override public IBinder onBind(Intent intent) { return binder; }
2)在MusicService中,声明一个MediaPlayer变量,进行设置歌曲路径,这里我选择歌曲1作为初始化时候的歌曲
private String[] musicDir = new String[]{
Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/三生三世.mp3", Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/爱丫爱丫.mp3", Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/安和桥.mp3", Environment.getExternalStorageDirectory().getAbsolutePath() +"/qqmusic/song/和你在一起.mp3"}; private int musicIndex = 1; public static MediaPlayer mp = new MediaPlayer(); public MusicService() { try { musicIndex = 1; mp.setDataSource(musicDir[musicIndex]); mp.prepare(); } catch (Exception e) { e.printStackTrace(); } }
//播放/暂停按钮 public void playOrPause() { if(mp.isPlaying()){ mp.pause(); } else { mp.start(); } } //下一首 public void nextMusic() { if(mp != null && musicIndex < 3) { mp.stop(); try { mp.reset(); mp.setDataSource(musicDir[musicIndex+1]); musicIndex++; mp.prepare(); mp.seekTo(0); mp.start(); } catch (Exception e) { Log.d("hint", "can't jump next music"); e.printStackTrace(); } }else { Toast.makeText(this, "没有更多歌曲啦", Toast.LENGTH_SHORT).show(); } } //上一首 public void preMusic() { if(mp != null && musicIndex > 0) { mp.stop(); try { mp.reset(); mp.setDataSource(musicDir[musicIndex-1]); musicIndex--; mp.prepare(); mp.seekTo(0); mp.start(); } catch (Exception e) { Log.d("hint", "can't jump pre music"); e.printStackTrace(); } }else{ Toast.makeText(this, "已经是第一首啦", Toast.LENGTH_SHORT).show(); } }
4)注册MusicService并赋予权限,允许读取外部存储空间。
以上步骤是MusicService类核心代码。
接下来我们需在
5)MainAcitvity中声明ServiceConnection,调用bindService保持与MusicService通信,通过intent的事件进行通信,在onCreate()函数中绑定Service
private ServiceConnection sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { musicService = ((MusicService.MyBinder)iBinder).getService(); } @Override public void onServiceDisconnected(ComponentName componentName) { musicService = null; } }; private void bindServiceConnection() { Intent intent = new Intent(Main.this, MusicService.class); startService(intent); bindService(intent, sc, this.BIND_AUTO_CREATE); }@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); musicService = new MusicService(); bindServiceConnection(); seekBar = (SeekBar)this.findViewById(R.id.MusicSeekBar); seekBar.setProgress(musicService.mp.getCurrentPosition()); seekBar.setMax(musicService.mp.getDuration()); musicBegin = (TextView)this.findViewById(R.id.MusicBegin); musicTime = (TextView)this.findViewById(R.id.MusicTime); musicStatus=(TextView)this.findViewById(R.id.MusicStatus); btnPlayOrPause = (ImageButton) findViewById(R.id.BtnPlayorPause); Log.d("hint", Environment.getExternalStorageDirectory().getAbsolutePath()+"/You.mp3"); }
bindService函数回调onSerciceConnented函数,通过MusiceService函数下的onBind()方法获得binder对象并实现绑定
6)通过Handle实时更新UI,这里主要使用了post方法并在Runnable中调用postDelay方法实现实时更新UI,Handle.post方法在onResume()中调用,使得程序刚开始时和重新进入应用时能够更新UI
在Runnable中更新SeekBar的状态,并设置SeekBar滑动条的响应函数,使歌曲跳转到指定位置
public android.os.Handler handler = new android.os.Handler(); public Runnable runnable = new Runnable() { @Override public void run() { if(musicService.mp.isPlaying()) { musicStatus.setText("状态:正在播放....."); btnPlayOrPause.setImageResource(android.R.drawable.ic_media_pause); } else { musicStatus.setText("状态:未播放"); btnPlayOrPause.setImageResource(android.R.drawable.ic_media_play); } musicBegin.setText(time.format(musicService.mp.getCurrentPosition())); musicTime.setText(time.format(musicService.mp.getDuration())); seekBar.setProgress(musicService.mp.getCurrentPosition()); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { musicService.mp.seekTo(seekBar.getProgress()); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); handler.postDelayed(runnable, 100); } }; @Override protected void onResume() { if(musicService.mp.isPlaying()) { musicStatus.setText("状态:正在播放....."); btnPlayOrPause.setImageResource(android.R.drawable.ic_media_pause); } else { musicStatus.setText("状态:未播放"); btnPlayOrPause.setImageResource(android.R.drawable.ic_media_play); } seekBar.setProgress(musicService.mp.getCurrentPosition()); seekBar.setMax(musicService.mp.getDuration()); handler.post(runnable); super.onResume(); Log.d("hint", "handler post runnable"); }
7)给每个按钮设置响应函数,在onDestroy()中添加解除绑定,避免内存泄漏
public void onClick(View view) { switch (view.getId()) { case R.id.BtnPlayorPause: musicService.playOrPause(); break; case R.id.BtnQuit: handler.removeCallbacks(runnable); unbindService(sc); try { System.exit(0); } catch (Exception e) { e.printStackTrace(); } break; case R.id.btnPre: musicService.preMusic(); // Toast.makeText(musicService, "上一首切换", Toast.LENGTH_SHORT).show(); break; case R.id.btnNext: musicService.nextMusic(); //Toast.makeText(musicService, "下一首切换", Toast.LENGTH_SHORT).show(); break; default: break; } } @Override public void onDestroy() { unbindService(sc); super.onDestroy(); }
8)在Button中赋予onClick属性指向接口函数
main.xml:
这样就可以实现简单音乐播放器的播放。
效果图
总结
- 读取SD卡内存的时候,应该使用android.os.Environment库中的getExternalStorageDirectory()方法,然而并不能生效。应该再使用getAbsolutePath()获取绝对路径后读取音乐才生效。
- 切换歌曲的时候try块不能正确执行。检查过后,也是执行了stop()函数后再重新setDataSource()来切换歌曲的,但是没有效果。查阅资料后,发现setDataSource()之前需要调用reSet()方法,才可以重新设置歌曲。
简述如何使用Handler实时更新UI
方法一:
Handle的post方法,在post的Runable的run方法中,使用postDelay方法再次post该Runable对象,在Runable中更新UI,达到实时更新UI的目的
方法二:
多开一个线程,线程写一个持续循环,每次进入循环内即post一次Runable,然后休眠1000ms,亦可做到实时更新UI
更多相关文章
- android EditText 不自动弹出键盘的方法
- Android(安卓)AsyncTask Download
- Android(安卓)中的看门狗—Watchdog
- Android禁用返回键
- android Fragment + FragmentStatePagerAdapter的恢复问题解决
- android 自动更新apk版本
- Android(安卓)MediaPlayer 常用方法介绍
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用