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->点击下一首(歌曲时间变化)->点击上一首->点击退出

   
   

一些总结

  1. 读取SD卡内存的时候,应该使用android.os.Environment库中的getExternalStorageDirectory()方法,然而并不能生效。应该再使用getAbsolutePath()获取绝对路径后读取音乐才生效。
  2. 切换歌曲的时候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

工程下载

传送门:下载

http://www.cnblogs.com/wsine/p/5177372.html

Android实现简单音乐播放器(startService和bindService后台运行程序)的更多相关文章

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

    Android实现简单音乐播放器(MediaPlayer) 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 工程内容 实现一个简单的音乐播放器,要求功能 ...

  2. Android 实现简单音乐播放器(二)

    在Android 实现简单音乐播放器(一)中,我介绍了MusicPlayer的页面设计. 现在,我简单总结一些功能实现过程中的要点和有趣的细节,结合MainActivity.java代码进行说明(写出 ...

  3. Android 实现简单音乐播放器(一)

    今天掐指一算,学习Android长达近两个月了,今天开始,对过去一段时间的学习收获以及遇到的疑难杂症做一些总结. 简单音乐播放器是我自己完成的第一个功能较为完整的APP,可以说是我的Android学习 ...

  4. Android——简单音乐播放器

    使用MediaPlayer做的简单音乐播放器,更多内容请到百度经验查看   http://jingyan.baidu.com/article/60ccbceb63452364cab197f1.html ...

  5. html5 简单音乐播放器

    html5 简单音乐播放器 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> < ...

  6. iOS之基于FreeStreamer的简单音乐播放器(模仿QQ音乐)

    代码地址如下:http://www.demodashi.com/demo/11944.html 天道酬勤 前言 作为一名iOS开发者,每当使用APP的时候,总难免会情不自禁的去想想,这个怎么做的?该怎 ...

  7. Android开发6:Service的使用(简单音乐播放器的实现)

    前言 啦啦啦~各位好久不见啦~博主最近比较忙,而且最近一次实验也是刚刚结束~ 好了不废话了,直接进入我们这次的内容~ 在这篇博文里我们将学习Service(服务)的相关知识,学会使用 Service ...

  8. Android开发实战之简单音乐播放器

    最近开始学习音频相关.所以,很想自己做一个音乐播放器,于是,花了一天学习,将播放器的基本功能实现了出来.我觉得学习知识点还是蛮多的,所以写篇博客总结一下关于一个音乐播放器实现的逻辑.希望这篇博文对你的 ...

  9. Android开发学习之路--MediaPlayer之简单音乐播放器初体验

    很多时候我们都会用手机来播放音乐,播放视频,那么具体地要怎么实现呢,其实主要是MediaPlayer类来完成的.下面通过简单的例子来实现一首歌曲的播放吧.新建工程MediaPlayerStudy,这里 ...

随机推荐

  1. Linux中vim编辑器莫名下方出现H的问题

    在使用vim编辑文件的时候,不知道自己是按了哪个快捷键,导致了,每次编辑文件时,下方命令行出现数字+H的格式命令,使得整个文件没法编辑,强制退出后进入不能解决问题,各种文件的编辑都不行,找不出原因,最 ...

  2. BZOJ 1864 三色二叉树 - 树型dp

    传送门 题目大意: 给一颗二叉树染色红绿蓝,父亲和儿子颜色必须不同,两个儿子颜色必须不同,问最多和最少能染多少个绿色的. 题目分析: 裸的树型dp:\(dp[u][col][type]\)表示u节点染 ...

  3. Kail Linux渗透测试培训手册3第二章信息采集

    Kail Linux渗透测试培训手册3第二章信息采集 信息收集是网络攻击中最重要的步骤之一.渗透攻击.我们需要收集各种信息目标.该信息收集.攻击成功的概率越大.介绍信息收集的相关工具.本文选自< ...

  4. 【16.67%】【codeforces 667C】Reberland Linguistics

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  5. Android 平台下Ftp 使用模拟器需要注意的问题

    以下代码在pc上测试通过,可是在android模拟器上就不工作,不过还可以链接到服务器但不能得到文件 纠结了半天,原来是模式的问题,具体请Google 模拟器中采用建议被动模式 public void ...

  6. myeclipse 8.6 software and workspace center is currently not available

    Myeclipse安装EGit插件的时候偏偏遇到software and workspace center is currently not available 不用打开myeclipse  conf ...

  7. 新建py文件时取名千万要小心 不要和已有模块重名

    这是因为我新建了一个email.py的文件 后来我将文件名rename成了myemail.py没有看改名提示,结果导致所有的对email的import和调用全部改成了对myemail的import和调 ...

  8. UML该元素的行为为基础的元素

     Behavioral thingsare the dynamic parts of UML models. These are the verbs of a model, representin ...

  9. 我所理解的设计模式(C++实现)——观察者模式(Observer Pattern)

    概述: 近期中国股市起起伏伏,当然了起伏就用商机,小明发现商机后果断想入市,买入了中国证券,他想在电脑client上,网页上,手机上,iPad上都能够查看到该证券的实时行情,这样的情况下我们应该怎么设 ...

  10. WPF之动画

    原文:WPF之动画 线性关键帧.不连续关键帧动画: <Window.Triggers> <EventTrigger RoutedEvent="Window.Loaded&q ...