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

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

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

//绑定播放服务bindService(intent, conn,BIND_AUTO_CREATE);//Runnable对象加入消息队列,在handler绑定的线程中执行,用于歌词更新handler.post(updateTimeCallback);start = System.currentTimeMillis();bindService(intent, conn,BIND_AUTO_CREATE); 中的conn是绑定服务后的一个回调接口,我们看下代码:

/** * 传入bindService()中的回调接口 */ServiceConnection conn = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName arg0, IBinder binder) {//绑定成功后调用该方法PlayActivity.this.binder = (Binder)binder;}@Overridepublic void onServiceDisconnected(ComponentName arg0) {}};其实就是一个语句,作用就是将播放服务返回的IBinder对象引用赋给播放Activity,这样Activity就可以获取Service返回的数据了,我们这里是为了获取此时播放的进度数据。

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

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

/** * 经过试验如果把进度条调动之前和调动之后合并为一种方式实现歌词的更新的话会奔溃,原因可能是不同线程间资源的同步问题,只好拆成 * 进度条调动之前(change == false)和调动之后(change == true)两部分在歌词更新的线程中执行 * @author yan * */class SeekBarListener implements OnSeekBarChangeListener{@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}@Overridepublic void onStartTrackingTouch(SeekBar arg0) {Log.d("yinan", "start——–"+seekBar.getProgress());}@Overridepublic void onStopTrackingTouch(SeekBar arg0) {if(stopMusic == false){change = true;//进度条进度人为改变,将改变后的进度发送给播放服务Intent intent = new Intent();intent.setClass(PlayActivity.this, PlayService.class);intent.putExtra("progress", seekBar.getProgress());startService(intent);}else{seekBar.setProgress(0);}}}在进度条位置得到调整之后,仍然用startService(intent)方式,将封装进度条进度的Intent对象传给Service的onStartCommand方法处理。

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

/** * 异步调整进度条位置与歌曲进度一致和显示歌词的类 * @author yinan * */class UpdateTimeCallback implements Runnable{Queue times = null;Queue messages = null;ArrayList<Queue> queues = null;public UpdateTimeCallback(ArrayList<Queue> queues){times = queues.get(0);messages = queues.get(1);this.queues = queues;}/*** run方法由于没有条用start所以并没有开辟子线程,所以在内部开启子线程*/@Overridepublic void run() {total = PlayService.totalLength;if(change == true){Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeString("change");try {binder.transact(0, data, reply, 0);} catch (RemoteException e) {e.printStackTrace();}//直到Service返回才执行这一句float s = reply.readFloat();//s为歌曲播放的时间进度float f = s*100;seekBar.setProgress((int)f);//转化为进度条进度//此时对应的播放时间点final long t = (long) (s*total);preparelrc(mp3Info.getLrcName());times = queues.get(0);messages = queues.get(1);System.out.println("times"+times.size());System.out.println("messages"+messages.size());if(stopMusic == false){new Thread(){public void run(){while(nextTime < t){ //从头遍历歌词,时间队列直到时间点大于当前时间System.out.println("nextTime"+nextTime);///////////////////////////////lyric = message;if((times.size()>0)||messages.size()>0){nextTime = (Long)times.poll();message = (String)messages.poll();}else{lyric = null;stopMusic = true;}//保存时间点刚大于当前时间的上一条歌词,即最接近当前的歌词System.out.println("nextTime"+nextTime);}}}.start();System.out.println("lyric"+lyric);if(lyric != null){lrcText.setText(lyric);}}}if(!change){nowTime = System.currentTimeMillis() – start;seekBar.setProgress((int)(nowTime*100/total));if(stopMusic == false){new Thread(){public void run(){while(nextTime < nowTime){ //从头遍历歌词,时间队列直到时间点小于当前时间System.out.println("nextTime"+nextTime);lyric = message;//保存时间点刚大于当前时间的上一条歌词,即最接近当前的歌词if((times.size()>0)||messages.size()>0){nextTime = (Long)times.poll();message = (String)messages.poll();}else{lyric = null;stopMusic = true;}}}}.start();if(lyric != null){lrcText.setText(lyric);}}}if(stopMusic == true){handler.removeCallbacks(updateTimeCallback);}handler.postDelayed( updateTimeCallback, 200);//每20毫秒执行一次线程}}

这里是将处理的情况分为进度条被改变了和未被改变两种情况,用标志位change表示(一旦进度条被改变了就执行了change为true那一段代码)。开子线程是因为对歌词的处理耗时,不适合放在UI线程中。

具体的流程是:

不必在乎目的地,在乎的是沿途的风景以及看风景的心情,让心灵去旅行!

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

相关文章:

你感兴趣的文章:

标签云: