上一次谈了音乐播放的实现,这次说下最复杂的进度条和歌词更新。因为须要在播放的Activity和播放的Service间进行交互,所以就涉及了Activity对Service的绑定以及绑定后数据的传输,这个须要对服务绑定熟悉才干够理解。原理不复杂。可是步骤略微繁琐,代码贴起来可能会非常混乱。

进度条和歌词放在一起说比較好,不然比較混乱。进度条的调整大家都懂的,就是进度条调到哪里歌曲的播放就跟到哪里,歌词也得跟到哪里。首先看下上一篇看过的開始button监听事件中服务的绑定代码:

  1. //绑定播放服务
  2. bindService(intent, conn,BIND_AUTO_CREATE);
  3. //Runnable对象增加消息队列,在handler绑定的线程中运行,用于歌词更新
  4. handler.post(updateTimeCallback);
  5. start = System.currentTimeMillis();

bindService(intent, conn,BIND_AUTO_CREATE); 中的conn是绑定服务后的一个回调接口。我们看下代码:

  1. /**
  2. * 传入bindService()中的回调接口
  3. */
  4. ServiceConnection conn = new ServiceConnection(){
  5.  
  6. @Override
  7. public void onServiceConnected(ComponentName arg0, IBinder binder) {//绑定成功后调用该方法
  8. PlayActivity.this.binder = (Binder)binder;
  9. }
  10.  
  11. @Override
  12. public void onServiceDisconnected(ComponentName arg0) {
  13.  
  14. }
  15.  
  16. };

事实上就是一个语句。作用就是将播放服务返回的IBinder对象引用赋给播放Activity,这样Activity就能够获取Service返回的数据了,我们这里是为了获取此时播放的进度数据。

handler.post(updateTimeCallback);是将一个Runnable对象增加队列中。这个Runnable对象updateTimeCallback

就是实现进度条和歌词更新的核心实现代码,只是看这个类之前。我们先看下进度条的监听事件:

  1. /**
  2. * 经过试验假设把进度条调动之前和调动之后合并为一种方式实现歌词的更新的话会奔溃,原因可能是不同线程间资源的同步问题,仅仅好拆成
  3. * 进度条调动之前(change == false)和调动之后(change == true)两部分在歌词更新的线程中运行
  4. * @author yan
  5. *
  6. */
  7. class SeekBarListener implements OnSeekBarChangeListener{
  8.  
  9. @Override
  10. public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  11.  
  12. }
  13.  
  14. @Override
  15. public void onStartTrackingTouch(SeekBar arg0) {
  16. Log.d("yinan", "start--------"+seekBar.getProgress());
  17.  
  18. }
  19.  
  20. @Override
  21. public void onStopTrackingTouch(SeekBar arg0) {
  22.  
  23. if(stopMusic == false){
  24. change = true;//进度条进度人为改变。将改变后的进度发送给播放服务
  25.  
  26. Intent intent = new Intent();
  27. intent.setClass(PlayActivity.this, PlayService.class);
  28. intent.putExtra("progress", seekBar.getProgress());
  29. startService(intent);
  30. }else{
  31. seekBar.setProgress(0);
  32. }
  33.  
  34. }
  35.  
  36. }

在进度条位置得到调整之后,仍然用startService(intent)方式。将封装进度条进度的Intent对象传给Service的onStartCommand方法处理。

如今来看看实现的核心代码Runnable对象updateTimeCallback的类:

  1. /**
  2. * 异步调整进度条位置与歌曲进度一致和显示歌词的类
  3. * @author yinan
  4. *
  5. */
  6. class UpdateTimeCallback implements Runnable{
  7. Queue times = null;
  8. Queue messages = null;
  9. ArrayList<Queue> queues = null;
  10.  
  11. public UpdateTimeCallback(ArrayList<Queue> queues){
  12. times = queues.get(0);
  13. messages = queues.get(1);
  14. this.queues = queues;
  15.  
  16. }
  17. /**
  18. * run方法因为没有条用start所以并没有开辟子线程。所以在内部开启子线程
  19. */
  20. @Override
  21. public void run() {
  22. total = PlayService.totalLength;
  23. if(change == true){
  24.  
  25. Parcel data = Parcel.obtain();
  26. Parcel reply = Parcel.obtain();
  27. data.writeString("change");
  28.  
  29. try {
  30. binder.transact(0, data, reply, 0);
  31. } catch (RemoteException e) {
  32.  
  33. e.printStackTrace();
  34. }
  35. //直到Service返回才运行这一句
  36. float s = reply.readFloat();//s为歌曲播放的时间进度
  37.  
  38. float f = s*100;
  39. seekBar.setProgress((int)f);//转化为进度条进度
  40.  
  41. //此时相应的播放时间点
  42. final long t = (long) (s*total);
  43.  
  44. preparelrc(mp3Info.getLrcName());
  45. times = queues.get(0);
  46. messages = queues.get(1);
  47.  
  48. System.out.println("times"+times.size());
  49. System.out.println("messages"+messages.size());
  50. if(stopMusic == false){
  51. new Thread(){
  52. public void run(){
  53. while(nextTime < t){ //从头遍历歌词,时间队列直到时间点大于当前时间
  54. System.out.println("nextTime"+nextTime);///////////////////////////////
  55. lyric = message;
  56. if((times.size()>0)||messages.size()>0){
  57. nextTime = (Long)times.poll();
  58. message = (String)messages.poll();
  59. }else{
  60. lyric = null;
  61.  
  62. stopMusic = true;
  63.  
  64. }//保存时间点刚大于当前时间的上一条歌词,即最接近当前的歌词
  65.  
  66. System.out.println("nextTime"+nextTime);
  67. }
  68. }
  69. }.start();
  70.  
  71. System.out.println("lyric"+lyric);
  72. if(lyric != null){
  73. lrcText.setText(lyric);
  74.  
  75. }
  76. }
  77. }
  78.  
  79. if(!change){
  80.  
  81. nowTime = System.currentTimeMillis() - start;
  82. seekBar.setProgress((int)(nowTime*100/total));
  83.  
  84. if(stopMusic == false){
  85. new Thread(){
  86. public void run(){
  87. while(nextTime < nowTime){ //从头遍历歌词。时间队列直到时间点小于当前时间
  88. System.out.println("nextTime"+nextTime);
  89. lyric = message;//保存时间点刚大于当前时间的上一条歌词,即最接近当前的歌词
  90. if((times.size()>0)||messages.size()>0){
  91. nextTime = (Long)times.poll();
  92. message = (String)messages.poll();
  93. }else{
  94. lyric = null;
  95. stopMusic = true;
  96.  
  97. }
  98. }
  99. }
  100. }.start();
  101.  
  102. if(lyric != null){
  103. lrcText.setText(lyric);
  104.  
  105. }
  106. }
  107.  
  108. }
  109.  
  110. if(stopMusic == true){
  111. handler.removeCallbacks(updateTimeCallback);
  112. }
  113. handler.postDelayed( updateTimeCallback, 200);//每20毫秒运行一次线程
  114.  
  115. }
  116.  
  117. }

这段代码大致是当获取到服务返回的歌曲实时进度的数据时,将进度条的位置进行调整到相应为孩子的处理,然后将歌词更新到相应的部分。因为进度条要实时跟进。全部UpdateTimeCallback类的run方法是每隔200毫秒就运行一次,到Service中获取返回值。

这里是将处理的情况分为进度条被改变了和未被改变两种情况,用标志位change表示(一旦进度条被改变了就运行了change为true那一段代码)。

开子线程是由于对歌词的处理耗时。不适合放在UI线程中。

详细的流程是:

首先,Parcel data = Parcel.obtain();  

   Parcel reply = Parcel.obtain();

   data.writeString("change");

   binder.transact(0, data, reply, 0);

binder就是之前和Service绑定的IBinder对象,binder.transact是将一个Parcel对象(传入"change"不过一个标志)传给Service,然后Service返回歌曲进度,在float s
= reply.readFloat()这句中接收,s是一个当前进度占总进度的百分比数。然后将进度条位置设置一下就可以。

然后进行歌词更新的处理。

首先是对歌词的准备处理工作,preparelrc(mp3Info.getLrcName()),将lrc格式的歌词中的时间点和相应的详细歌词拆分成两个队列。(详细代码请看源代码)然后依据Service返回的歌曲播放进度。对两个队列进行遍历。当有时间点超过进度相应的时间点时,将该时间点相应的歌词取出,显示在Activity上。

前面总是说Service返回歌曲实时进度,我们来看看Service中对于数据返回的处理。

当进度条被调整时。会进入Service的onStartCommand方法,进入到下面的if语句中:

  1. if(intent.getIntExtra("progress", 0) != 0){///////////////////////
  2. if(isPlay){
  3. //进度条进度
  4. int progress = intent.getIntExtra("progress", 0);
  5. if(progress != 0)
  6. //将音乐进度调整到相应位置
  7. mediaPlayer.seekTo(progress*totalLength/100);
  8. }
  9.  
  10. }

Service又是怎样实时返回歌曲进度的呢?Service中有一个Binder类。是IBinder的子类:

  1. /*
  2. *运行 binder.transact()后运行
  3. */
  4. class FirstBinder extends Binder{
  5.  
  6. @Override
  7. protected boolean onTransact(int code, Parcel data, Parcel reply,
  8. int flags) throws RemoteException {
  9.  
  10. String str = data.readString();
  11.  
  12. int currentPosition = mediaPlayer.getCurrentPosition();
  13.  
  14. float s = (float)currentPosition/mediaPlayer.getDuration();
  15.  
  16. if(isPlay)
  17. reply.writeFloat(s);
  18.  
  19. return super.onTransact(code, data, reply, flags);
  20. }

是的。它就是之前Activity与Service绑定后获得的IBinder对象。每当Activity的binder.transact(0, data, reply, 0);被调用的时候。Service就会调用onTransact方法。将获取到的数据装入Parcel对象reply中,然后将整个IBinder对象返回Activity。

MP3播放器就这样讲完了,可能讲的非常乱。希望对各位有帮助。源码下载链接:http://download.csdn.net/detail/sinat_23092639/8933995

安卓MP3播放器开发实例(3)之进度条和歌词更新的实现的更多相关文章

  1. 安卓MP3播放器开发实例(1)之音乐列表界面

    学习安卓开发有一年了,想想这一年的努力,确实也收获了不少.也找到了比較如意的工作. 今天准备分享一个以前在初学阶段练习的一个项目.通过这个项目我真正的找到了开发安卓软件的感觉,从此逐渐步入安卓开发的正 ...

  2. 【Android】利用安卓的数据接口、多媒体处理编写内存卡Mp3播放器app

    通过调用安卓的MediaPlayer能够直接完毕Mp3等主流音频的播放,同一时候利用ContentResolver与Cursor能够直接读取安卓内在数据库的信息.直接获取当前sdcard中全部音频的列 ...

  3. android音乐播放器开发教程

    android音乐播放器开发教程 Android扫描sd卡和系统文件 Android 关于录音文件的编解码 实现米聊 微信一类的录音上传的功能 android操作sdcard中的多媒体文件——音乐列表 ...

  4. 你用java的swing可以做出这么炫的mp3播放器吗?

    这个mp3播放器是基于java的swing编写的,我认为界面还是可以拿出来和大家看一看评一评. 先说说创作的初衷,由于前段时间工作不是很忙,与其闲着,还不如找一些东西来给自己捣腾捣腾,在 之前写的 j ...

  5. 开源mp3播放器--madplay 编译和移植 简记

    madplay是一款开源的mp3播放器. http://madplay.sourcearchive.com/ 下面简单记录一下madplay的编译与移植到ARM开发板上的过程 一.编译x86版本的ma ...

  6. 从零开始学习PYTHON3讲义(十四)写一个mp3播放器

    <从零开始PYTHON3>第十四讲 通常来说,Python解释执行,运行速度慢,并不适合完整的开发游戏.随着电脑速度的快速提高,这种情况有所好转,但开发游戏仍然不是Python的重点工作. ...

  7. C# wave mp3 播放器探寻

    C# wave mp3 播放器探寻   最近无聊,想听听歌曲.可怜新电脑上歌曲就两三首,要听其它的就得在旧电脑上播放.可是,那台古董但不失健壮的本本被老婆无情的霸占了.无奈. 思来想去,得,写个程序播 ...

  8. 团队编程--MP3播放器

    设计思路: 这次的作业是一个MP3播放器,它是一个团队项目.由于我们都没接触过这类的编程.刚开始的时候我们是不知道从什么地方着手的.经过我们的商量我们决定从现在市场主流的音乐播放器上找到几个主要的功能 ...

  9. 基于GStreamer编写Mp3播放器

    一.简介 作者系统为CentOS6,本文在此基础上对Mp3播放器进行开发,需要使用mp3解码库libmad和gstreamer0.10-plugins-ugly,详细步骤如下.   二.操作步骤 1) ...

随机推荐

  1. FZOJ--2221-- RunningMan(水题)

    Problem 2221 RunningMan Accept: 4    Submit: 10 Time Limit: 1000 mSec    Memory Limit : 32768 KB Pro ...

  2. BZOJ 3503 高斯消元

    思路: 高斯消元就好啦 注意每个格子最多只能和4个相邻 所以是 n*m*n*m*5 的 并不会TLE //By SiriusRen #include <cstdio> #include & ...

  3. Intellij使用"easyexplore"

    刚开始接触Intellij,里面有很多东西还不太会用,平时在eclipse里面用的很方便的easyexplore能帮助快速打开文件目录,Intellij中本身就有这样的功能,只是默认没有开启,需要我们 ...

  4. BZOJ 3639: Query on a tree VII LCT_set维护子树信息

    用 set 维护子树信息,细节较多. Code: #include <cstring> #include <cstdio> #include <algorithm> ...

  5. [洛谷P1119][codevs1817]灾后重建

    题目大意:有n个村庄和一些连通两个村庄的双向道路.每个村庄在一个特定的时间修复.没有修复的村庄不能经过.现在有一系列询问,问两个村庄在t时刻的最短路(如果无法到达或两个村庄本身未修复,输出-1). 解 ...

  6. LinkedList源码学习

    链表数据结构 当前节点会保存上一个.下一个节点. 参见 LinkedList的Node类 实现: 1. 内部链表的方式. 1.1 添加元素.追加的方式,创建一个新的节点[Node],用最后一个节点关联 ...

  7. POJ 2241 The Tower of Babylon

    The Tower of Babylon Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Or ...

  8. Flexible implementation of a system management mode (SMM) in a processor

    A system management mode (SMM) of operating a processor includes only a basic set of hardwired hooks ...

  9. UML 绘图关系

    1 继承         子类继承父类   2 实现         实现类实现接口 3 依赖 (偶然.临时.比较弱关联)     类 A 使用了类 B,如果类 B 产生变化将会影响类A       ...

  10. struts2文件过滤拦截器fileUpload以及各种文件类型

    本文某些内容复制自:http://zhidao.baidu.com/link?url=F0Z-FqbZ83BOj_xXp_B8rgJDzUoeVSWGgXwPNP5fEdLU1nvBK7yO4vnX_ ...