当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实现简单音乐播放器(MediaPlayer)

Android实现简单音乐播放器(MediaPlayer)

2019年07月24日  | 移动技术网移动技术  | 我要评论

android实现简单音乐播放器(mediaplayer),供大家参考,具体内容如下

开发工具:andorid studio 1.3
运行环境:android 4.4 kitkat

工程内容

实现一个简单的音乐播放器,要求功能有:

  • 播放、暂停功能;
  • 进度条显示播放进度功能
  • 拖动进度条改变进度功能;
  • 后台播放功能;
  • 停止功能;
  • 退出功能;

代码实现

导入歌曲到手机sd卡的music目录中,这里我导入了4首歌曲:仙剑六里面的《誓言成晖》、《剑客不能说》、《镜中人》和《浪花》,也推荐大家听喔(捂脸

然后新建一个类musicservice继承service,在类中定义一个mybinder,有一个方法用于返回musicservice本身,在重载onbind()方法的时候返回

public class musicservice extends service {

 public final ibinder binder = new mybinder();
 public class mybinder extends binder{
 musicservice getservice() {
 return musicservice.this;
 }
 }

 @override
 public ibinder onbind(intent intent) {
 return binder;
 }
}

在musicservice中,声明一个mediaplayer变量,进行设置歌曲路径,这里我选择歌曲1作为初始化时候的歌曲

private string[] musicdir = new string[]{
 environment.getexternalstoragedirectory().getabsolutepath() + "/music/仙剑奇侠传六-主题曲-《誓言成晖》.mp3",
 environment.getexternalstoragedirectory().getabsolutepath() + "/music/仙剑奇侠传六-主题曲-《剑客不能说》.mp3",
 environment.getexternalstoragedirectory().getabsolutepath() + "/music/仙剑奇侠传六-主题曲-《镜中人》.mp3",
 environment.getexternalstoragedirectory().getabsolutepath() + "/music/仙剑奇侠传六-主题曲-《浪花》.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) {
 log.d("hint","can't get to the song");
 e.printstacktrace();
 }
}

设计一些歌曲播放、暂停、停止、退出相应的逻辑,此外我还设计了上一首和下一首的逻辑

public void playorpause() {
 if(mp.isplaying()){
 mp.pause();
 } else {
 mp.start();
 }
}
public void stop() {
 if(mp != null) {
 mp.stop();
 try {
 mp.prepare();
 mp.seekto(0);
 } catch (exception e) {
 e.printstacktrace();
 }
 }
}
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();
 }
 }
}
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();
 }
 }
}

注册musicservice并赋予权限,允许读取外部存储空间

<uses-permission android:name="android.permission.write_external_storage"/>
<uses-permission android:name="android.permission.read_external_storage"/>

<service android:name="com.wsine.west.exp5_afterclass.musicservice" android:exported="true"></service>

在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(mainactivity.this, musicservice.class);
 startservice(intent);
 bindservice(intent, sc, this.bind_auto_create);
}

@override
protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);

 log.d("hint", "ready to new musicservice");
 musicservice = new musicservice();
 log.d("hint", "finish to new musicservice");
 bindserviceconnection();

 seekbar = (seekbar)this.findviewbyid(r.id.musicseekbar);
 seekbar.setprogress(musicservice.mp.getcurrentposition());
 seekbar.setmax(musicservice.mp.getduration());

 musicstatus = (textview)this.findviewbyid(r.id.musicstatus);
 musictime = (textview)this.findviewbyid(r.id.musictime);

 btnplayorpause = (button)this.findviewbyid(r.id.btnplayorpause);

 log.d("hint", environment.getexternalstoragedirectory().getabsolutepath()+"/you.mp3");
}

bindservice函数回调onserciceconnented函数,通过musiceservice函数下的onbind()方法获得binder对象并实现绑定

通过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(getresources().getstring(r.string.playing));
 btnplayorpause.settext(getresources().getstring(r.string.pause).touppercase());
 } else {
 musicstatus.settext(getresources().getstring(r.string.pause));
 btnplayorpause.settext(getresources().getstring(r.string.play).touppercase());
 }
 musictime.settext(time.format(musicservice.mp.getcurrentposition()) + "/"
 + 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(getresources().getstring(r.string.playing));
 } else {
 musicstatus.settext(getresources().getstring(r.string.pause));
 }

 seekbar.setprogress(musicservice.mp.getcurrentposition());
 seekbar.setmax(musicservice.mp.getduration());
 handler.post(runnable);
 super.onresume();
 log.d("hint", "handler post runnable");
}

给每个按钮设置响应函数,在ondestroy()中添加解除绑定,避免内存泄漏

public void onclick(view view) {
 switch (view.getid()) {
 case r.id.btnplayorpause:
 musicservice.playorpause();
 break;
 case r.id.btnstop:
 musicservice.stop();
 seekbar.setprogress(0);
 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();
 break;
 case r.id.btnnext:
 musicservice.nextmusic();
 break;
 default:
 break;
 }
}

@override
public void ondestroy() {
 unbindservice(sc);
 super.ondestroy();
}

在button中赋予onclick属性指向接口函数

<button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:id="@+id/btnplayorpause"
 android:text="@string/btnplayorpause"
 android:onclick="onclick"/>

效果图

打开界面->播放一会儿进度条实时变化->拖动进度条->点击暂停->点击stop->点击下一首(歌曲时间变化)->点击上一首->点击退出


一些总结

  • 读取sd卡内存的时候,应该使用android.os.environment库中的getexternalstoragedirectory()方法,然而并不能生效。应该再使用getabsolutepath()获取绝对路径后读取音乐才生效。
  • 切换歌曲的时候try块不能正确执行。检查过后,也是执行了stop()函数后再重新setdatasource()来切换歌曲的,但是没有效果。查阅资料后,发现setdatasource()之前需要调用reset()方法,才可以重新设置歌曲。

了解service中startservice(service)和bindservice(service, conn, flags)两种模式的执行方法特点及其生命周期,还有为什么这次要一起用

startservice方法是让service启动,让service进入后台running状态;但是这种方法,service与用户是不能交互的,更准确的说法是,service与用户不能进行直接的交互。
因此需要使用bindservice方法绑定service服务,bindservice返回一个binder接口实例,用户就可以通过该实例与service进行交互。

service的生命周期简单到不能再简单了,一条流水线表达了整个生命周期。
service的活动生命周期是在onstart()之后,这个方法会处理通过startservices()方法传递来的intent对象。音乐service可以通过开打intent对象来找到要播放的音乐,然后开始后台播放。注: service停止时没有相应的回调方法,即没有onstop()方法,只有ondestroy()销毁方法。
oncreate()方法和ondestroy()方法是针对所有的services,无论它们是否启动,通过context.startservice()和context.bindservice()方法都可以访问执行。然而,只有通过startservice()方法启动service服务时才会调用onstart()方法。


图片来自网络,忘记出处了

简述如何使用handler实时更新ui

方法一:

handle的post方法,在post的runable的run方法中,使用postdelay方法再次post该runable对象,在runable中更新ui,达到实时更新ui的目的

方法二:

多开一个线程,线程写一个持续循环,每次进入循环内即post一次runable,然后休眠1000ms,亦可做到实时更新ui

工程下载

传送门:

更多关于播放器的内容请点击《java播放器功能》进行学习。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网