一、代码流程
1.自定义一个AppConstant.LRC_MESSAGE_ACTION字符串表示广播"更新歌词"
2.在PlayerActivity的onResume()注册BroadcastReceiver,在onPause解除BroadcastReceiver
3.所有更新歌词的操作移到PlayerService中
二、代码
1.xml
2.java
(1)PlayerActivity.java
1 package tony.mp3player; 2 3 import tony.model.Mp3Info; 4 import tony.mp3player.service.PlayerService; 5 import android.app.Activity; 6 import android.content.BroadcastReceiver; 7 import android.content.Context; 8 import android.content.Intent; 9 import android.content.IntentFilter; 10 import android.os.Bundle; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.ImageButton; 14 import android.widget.TextView; 15 16 //更新歌词的工作不再在activity中完成,Activity只被动地接收service的广播以更新歌词, 17 //原来的相关歌词操作挪到service中完成 18 public class PlayerActivity extends Activity { 19 20 private ImageButton beginBtn = null; 21 private ImageButton pauseBtn = null; 22 private ImageButton stopBtn = null; 23 24 private TextView lrcTextView = null; 25 private Mp3Info info = null; 26 27 private IntentFilter intentFilter = null; 28 private BroadcastReceiver receiver = null; 29 30 @Override 31 protected void onCreate(Bundle savedInstanceState) { 32 super.onCreate(savedInstanceState); 33 setContentView(R.layout.player); 34 Intent intent = getIntent(); 35 info = (Mp3Info) intent.getSerializableExtra("mp3Info"); 36 beginBtn = (ImageButton) findViewById(R.id.begin); 37 pauseBtn = (ImageButton) findViewById(R.id.pause); 38 stopBtn = (ImageButton) findViewById(R.id.stop); 39 lrcTextView = (TextView) findViewById(R.id.lrcText); 40 41 beginBtn.setOnClickListener(new BeginListener()); 42 pauseBtn.setOnClickListener(new PauseListener()); 43 stopBtn.setOnClickListener(new StopListener()); 44 } 45 46 @Override 47 protected void onPause() { 48 super.onPause(); 49 unregisterReceiver(receiver); 50 } 51 52 @Override 53 protected void onResume() { 54 super.onResume(); 55 receiver = new LrcMessageBroadcastReceiver(); 56 registerReceiver(receiver, getIntentFilter()); 57 } 58 59 private IntentFilter getIntentFilter() { 60 if(intentFilter == null) { 61 intentFilter = new IntentFilter(); 62 intentFilter.addAction(AppConstant.LRC_MESSAGE_ACTION); 63 } 64 return intentFilter; 65 } 66 67 /** 68 * 广播接收器,主要接收service放送的广播,并且更新UI,也就是放置歌词 的textview 69 * @author T 70 * 71 */ 72 class LrcMessageBroadcastReceiver extends BroadcastReceiver { 73 public void onReceive(Context context, Intent intent) { 74 String lrcMessage = intent.getStringExtra("lrcMessage"); 75 lrcTextView.setText(lrcMessage); 76 } 77 } 78 79 80 class BeginListener implements OnClickListener { 81 @Override 82 public void onClick(View v) { 83 Intent intent = new Intent(); 84 intent.putExtra("mp3Info", info); 85 intent.putExtra("MSG", AppConstant.PlayerMsg.PLAY_MSG); 86 intent.setClass(PlayerActivity2.this, PlayerService.class); 87 startService(intent); 88 } 89 } 90 91 class PauseListener implements OnClickListener { 92 @Override 93 public void onClick(View v) { 94 //通知Service暂停播放MP3 95 Intent intent = new Intent(); 96 intent.putExtra("MSG", AppConstant.PlayerMsg.PAUSE_MSG); 97 intent.setClass(PlayerActivity2.this, PlayerService.class); 98 startService(intent); 99 } 100 } 101 102 class StopListener implements OnClickListener { 103 @Override 104 public void onClick(View v) { 105 //通知Service停止播放MP3文件 106 Intent intent = new Intent(); 107 intent.putExtra("MSG", AppConstant.PlayerMsg.STOP_MSG); 108 intent.setClass(PlayerActivity2.this, PlayerService.class); 109 startService(intent); 110 } 111 } 112 }
(2)PlayerService.java
1 package tony.mp3player.service; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.InputStream; 6 import java.util.List; 7 import java.util.Queue; 8 9 import tony.model.Mp3Info; 10 import tony.mp3player.AppConstant; 11 import tony.mp3player.LrcProcessor; 12 import android.app.Service; 13 import android.content.Intent; 14 import android.media.MediaPlayer; 15 import android.net.Uri; 16 import android.os.Environment; 17 import android.os.Handler; 18 import android.os.IBinder; 19 20 21 22 public class PlayerService extends Service { 23 24 private boolean isPlaying = false; 25 private boolean isPause = false; 26 private boolean isReleased = false; 27 private MediaPlayer mediaPlayer = null; 28 29 private Mp3Info info = null; 30 private List<Queue> queues = null; 31 private Handler handler = new Handler(); 32 private UpdateTimeCallback updateTimeCallback = null; 33 private long begin = 0; 34 private long nextTimeMill = 0; 35 private long currentTimeMill = 0; 36 private String msg = null; 37 private long pauseTimeMills = 0; 38 39 @Override 40 public IBinder onBind(Intent intent) { 41 return null; 42 } 43 44 @Override 45 public int onStartCommand(Intent intent, int flags, int startId) { 46 info = (Mp3Info) intent.getSerializableExtra("mp3Info"); 47 int MSG = intent.getIntExtra("MSG", 0); 48 if(info != null) { 49 if(MSG == AppConstant.PlayerMsg.PLAY_MSG) { 50 play(info); 51 } 52 } else { 53 if(MSG == AppConstant.PlayerMsg.PAUSE_MSG) { 54 pause(); 55 } 56 else if(MSG == AppConstant.PlayerMsg.STOP_MSG) { 57 stop(); 58 } 59 } 60 return super.onStartCommand(intent, flags, startId); 61 } 62 63 /** 64 * 根据歌词文件的名字,来读取歌词文件当中的信息 65 * @param lrcName 66 */ 67 private void prepareLrc(String lrcName) { 68 try { 69 InputStream inputStream; 70 inputStream = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + 71 File.separator + "mp3" + File.separator + info.getLrcName()); 72 LrcProcessor lrcProcessor = new LrcProcessor(); 73 queues = lrcProcessor.process(inputStream); 74 updateTimeCallback = new UpdateTimeCallback(queues); 75 begin = 0; 76 currentTimeMill = 0; 77 nextTimeMill = 0; 78 } catch (Exception e) { 79 e.printStackTrace(); 80 } 81 } 82 83 class UpdateTimeCallback implements Runnable{ 84 Queue<Long> times = null; 85 Queue<String> msgs = null; 86 87 public UpdateTimeCallback(List<Queue> queues) { 88 this.times = queues.get(0); 89 this.msgs = queues.get(1); 90 } 91 92 @Override 93 public void run() { 94 //计算偏移量,也就是说从开始播放MP3到现在为止,共消耗了多少时间,以毫秒为单位 95 long offset = System.currentTimeMillis() - begin; 96 if(currentTimeMill == 0) {//刚开始播放时,调用prepareLrc(),在其中设置currentTimeMill=0 97 nextTimeMill = times.poll(); 98 msg = msgs.poll(); 99 } 100 //歌词的显示是如下:例如 101 //[00:01.00]Look 102 //[00:03.00]Up 103 //[00:06.00]Down 104 //则在第1~3秒间是显示“look”,在第3~6秒间是显示"Up",在第6秒到下一个时间点显示"Down" 105 if(offset >= nextTimeMill) { 106 //发送广播,把此刻应该显示的歌词传出去 107 Intent intent = new Intent(); 108 intent.setAction(AppConstant.LRC_MESSAGE_ACTION); 109 intent.putExtra("lrcMessage", msg); 110 sendBroadcast(intent); 111 msg = msgs.poll(); 112 nextTimeMill = times.poll(); 113 } 114 currentTimeMill = currentTimeMill + 100; 115 //在run方法里调用postDelayed,则会形成循环,每0.01秒执行一次线程 116 handler.postDelayed(updateTimeCallback, 100); 117 } 118 } 119 120 private void play(Mp3Info info) { 121 if(!isPlaying) { 122 String path = getMp3Path(info); 123 mediaPlayer = MediaPlayer.create(this, Uri.parse("file://" + path)); 124 mediaPlayer.setLooping(false); 125 mediaPlayer.start(); 126 isPlaying = true; 127 isReleased = false; 128 129 prepareLrc(info.getLrcName()); 130 handler.postDelayed(updateTimeCallback, 5); 131 begin = System.currentTimeMillis(); 132 } 133 } 134 135 private void pause() { 136 if(mediaPlayer != null) { 137 if(!isReleased){ 138 if(!isPause) { 139 mediaPlayer.pause(); 140 //不再更新歌词 141 handler.removeCallbacks(updateTimeCallback); 142 //用来下面代码计算暂停了多久 143 pauseTimeMills = System.currentTimeMillis(); 144 } else { 145 mediaPlayer.start(); 146 handler.postDelayed(updateTimeCallback, 5); 147 //因为下面的时间偏移是这样计算的offset = System.currentTimeMillis() - begin; 148 //所以要把暂停的时间加到begin里去, 149 begin = System.currentTimeMillis() - pauseTimeMills + begin; 150 } 151 isPause = !isPause; 152 isPlaying = !isPlaying; 153 } 154 } 155 } 156 157 private void stop() { 158 if(mediaPlayer != null) { 159 if(isPlaying) { 160 if(!isReleased) { 161 //从Handler当中移除updateTimeCallback 162 handler.removeCallbacks(updateTimeCallback); 163 mediaPlayer.stop(); 164 mediaPlayer.release(); 165 isReleased = true; 166 isPlaying = false; 167 } 168 } 169 } 170 } 171 172 private String getMp3Path(Mp3Info mp3Info) { 173 String SDCardRoot = Environment.getExternalStorageDirectory() 174 .getAbsolutePath(); 175 String path = SDCardRoot + File.separator + "mp3" + File.separator 176 + mp3Info.getMp3Name(); 177 return path; 178 } 179 }