## 1.背景
? ?音樂的播放,為了實現在后臺播放,將在Service 中進行音樂的基本控制,所以 MediaPlay 將在Service 中實現。
在這里使用了,簡單的 startService 方式,非 Ibind 方式操作服務,故在關閉Service 上還有些bug , 但并不影響使用;
? ?*Service 學習參考資料 :?[http://www.android-doc.com/guide/components/services.html](http://www.android-doc.com/guide/components/services.html)*
? ?*Service 生命周期 :?[http://blog.csdn.net/LABLENET/article/details/48073093](http://blog.csdn.net/LABLENET/article/details/48073093)*
? ?在這個播放器中,為了播放音樂方便,故在應用啟動后,當數據加載成功就啟動了MusicService 服務。后臺服務本身是不可見的,但是在這個為了使其可見就使用了通知(MusicNotification )來人性話,顯示服務當前的狀態和當前播放音樂的信息,通知下篇實現,這里的主角是Service.?
? MusicService 服務 :
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
## 2.Service 實現
? ? (1)實現MusicService?
? ? ? ? ? ? ? 如果直接看源碼的時候,十分頭疼這些是什么東西,我在這個簡單的說明的一下 :
? ? ? ? ? ? ? MusicService 實現的功能是對音樂的基本控制:
? ? ? ? ? ? ? ? ? ? 1.應用啟動 ?(隨著應用的啟動而啟動)?就啟動?
? ? ? ? ? ? ? ? ? ? 2.狀態欄顯示 :?notification?
? ? ? ? ? ? ? ? ? ? 3.注冊 : bordcastReceiver?
? ? ? ? ? ? ? ? ? ? 4.請求第1首歌 : 更新狀態欄?
? ? ? ? ? ? ? ? ? ? 5.實現上一曲,下一曲,播放,暫停控制
? ? ? ? ? ? ? ? ? ? 6.初始化 MusicActivity 內容數據 ?(這個是歌詞/進度條顯示頁面)
? ? ? ?
? ? ? ?*MusicService 源碼 : 使用了斜杠進行了分類,應該是可以理解的,boradcastReceiver 的內容將在后面說明。*
~~~
package cn.labelnet.framework;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import cn.labelnet.model.MusicModel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.IBinder;
import android.widget.MediaController.MediaPlayerControl;
import android.widget.Toast;
public class MusicService extends Service implements OnPreparedListener,
OnCompletionListener, OnErrorListener {
/**
* MusicService 音樂播放控制 : 隨著應用的啟動而啟動 基本步揍 : 1.應用啟動 : 就啟動 2.狀態欄顯示 :
* notification 3.注冊 : bordcastReceiver 4.請求第1首歌 : 更新狀態欄 5.實現上一曲,下一曲,播放,暫停控制
*/
// 常量
private final String MUSIC_INTENT_KEY = "musics";
private final int MUSIC_INTENT_FLAG = 20001;
private final int MAIN_MUSIC_INTENT_FLAG = 20017;
// 音樂列表
private List<MusicModel> musics = null;
private int mmSize = 0;
// 通知欄
private MusicNotification musicNotifi = null;
private MusicModel mm = null;
// MediaPlay
private MediaPlayer mp = null;
private int currentTime = 0;
// Music廣播接收
private MusicBroadCast musicBroadCast = null;
// MainActivity 來的 Action
private final String MAIN_ACTIVIY_ACTION = "mainActivity.To.MusicService";
// 來自通知欄的Action
private final String MUSIC_NOTIFICATION_ACTION_PLAY = "musicnotificaion.To.PLAY";
private final String MUSIC_NOTIFICATION_ACTION_NEXT = "musicnotificaion.To.NEXT";
private final String MUSIC_NOTIFICATION_ACTION_CLOSE = "musicnotificaion.To.CLOSE";
private final String MUSIC_NOTIFICAION_INTENT_KEY = "notify.music";
// MusicService 來的 Action
private final String MUSIC_ACTIVITY_SERVICE_ACTION = "activity.to.musicservice";
private final String MUSIC_ACTIVITY_SERVICE_KEY = "musictype";
private final int MUSIC_ACTIVITY_SERVICE_REQUEST = 40001;
// 給MusicActivity 的 Action
private final String MUSIC_SERVICE_RECEIVER_ACTION = "service.to.musicactivity";
private Intent mActivityIntent = null;
private final String MUSIC_SERVICE_TO_ACTIVITY_MODEL = "model";
private final String MUSIC_SERVICE_TO_ACTIVITY_ISPLAY = "isplay";
private final String MUSIC_SERVICE_TO_ACTIVITY_NOWTIME = "nowtime";
// 響應碼 : 41001 沒數據 , 41002 : 有數據
private final String MUSIC_SERVICE_TOACTIVITY_CODE = "mpcode";
// Intent keys
private final String MAIN_MUSIC_INTENT_KEY = "mIntent";
@Override
public void onCreate() {
// 初始化MusicActivity 的 Intent ,給 MusicActivity 發送廣播 ,修改音樂播放界面
mActivityIntent = new Intent();
mActivityIntent.setAction(MUSIC_SERVICE_RECEIVER_ACTION);
// 初始化通知欄
musicNotifi = MusicNotification.getMusicNotification();
musicNotifi.setContext(getApplicationContext());
musicNotifi
.setManager((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
musicNotifi.onCreateMusicNotifi();
// 初始化MediaPlay : 設置監聽事件
mp = new MediaPlayer();
mp.setOnPreparedListener(this);
mp.setOnCompletionListener(this);
mp.setOnErrorListener(this);
// 注冊廣播
musicBroadCast = new MusicBroadCast();
IntentFilter filter = new IntentFilter();
filter.addAction(MAIN_ACTIVIY_ACTION);
filter.addAction(MUSIC_ACTIVITY_SERVICE_ACTION);
filter.addAction(MUSIC_NOTIFICATION_ACTION_PLAY);
filter.addAction(MUSIC_NOTIFICATION_ACTION_NEXT);
filter.addAction(MUSIC_NOTIFICATION_ACTION_CLOSE);
registerReceiver(musicBroadCast, filter);
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@SuppressWarnings("unchecked")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
musics = (List<MusicModel>) intent
.getSerializableExtra(MUSIC_INTENT_KEY);
}catch(Exception e){
}
if (musics != null) {
mmSize = musics.size();
}
// showToast("1." + musics.get(1).getSongname());
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
if (mp != null) {
mp.stop();
mp.release();
mp = null;
musics = null;
}
// 取消注冊的廣播
unregisterReceiver(musicBroadCast);
}
// //////////////////////////////Music Util//////////////////////
// Toast
private void showToast(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
// 音樂播放
private void play(String musicUrl) {
mp.reset();
try {
mp.setDataSource(getApplicationContext(), Uri.parse(musicUrl));
mp.prepareAsync();
} catch (IOException e) {
showToast("網絡錯誤,播放失敗");
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音樂暫停
private void pause() {
if (mp.isPlaying()) {
currentTime = mp.getCurrentPosition();
mp.pause();
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// 音樂繼續播放
private void resume() {
mp.start();
if (currentTime > 0) {
mp.seekTo(currentTime);
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音樂停止
private void stop() {
mp.stop();
try {
mp.prepare();
} catch (IOException e) {
showToast("音樂停止異常");
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// //////////////////////////////Music MediaPlayListener////////////
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// 出錯的時候
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
// 播放完畢的時候
// showToast("播放完畢,準備播放下一首!");
currentTime = 0;
// 改變通知欄
musicNotifi.onUpdataMusicNotifi(mm, false);
//改變MusicActivity
sendModelToMusicActivity();
}
@Override
public void onPrepared(MediaPlayer mp) {
// 準備加載的時候
resume();
sendModelToMusicActivity();
}
// ////////////////////////////其他工具方法//////////////////////////////////
/**
* 發送Model給MusicActivity
*/
private void sendModelToMusicActivity() {
if (mm != null) {
// 正在播放的歌曲實體
if(mm.getSeconds()==0){
mm.setSeconds(mp.getDuration()/1000);
}
mActivityIntent.putExtra(MUSIC_SERVICE_TOACTIVITY_CODE, 41002);
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_ISPLAY,
mp.isPlaying());
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_NOWTIME,
mp.getDuration() - mp.getCurrentPosition());
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_MODEL,
(Serializable) mm);
} else {
// 默認發送第一首歌信息
mm = musics.get(0);
mActivityIntent.putExtra(MUSIC_SERVICE_TOACTIVITY_CODE, 41001);
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_ISPLAY,
mp.isPlaying());
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_MODEL,
(Serializable) mm);
}
sendBroadcast(mActivityIntent);
}
// //////////////////////////////Music BroadCastReceiver////////////
// 接收廣播
private class MusicBroadCast extends BroadcastReceiver {
private int flag = 0, position = -1, kzhi = 0, musictype = 0;
@Override
public void onReceive(Context context, Intent intent) {
// 2.MainActivity 控制
flag = intent.getFlags();
mainToService(intent);
// 3.MusicNotification控制
kzhi = intent.getIntExtra("type", -1);
if (kzhi > 0) {
musicNotificationService(kzhi);
}
// 4.MusicActivity 來的控制
musictype = intent.getIntExtra(MUSIC_ACTIVITY_SERVICE_KEY, 0);
if (musictype > 0) {
musicActivityService(musictype);
}
}
/**
* 來自 MusicActivity 的控制
*
* @param musictype2
*/
private void musicActivityService(int musictype2) {
// showToast("musicActivityService 執行了 musictype2 :" + musictype2);
switch (musictype2) {
case 40001:
sendModelToMusicActivity();
break;
case 40002:
//播放與暫停
playSong();
break;
case 40003:
//下一曲
nextSong();
break;
case 40004:
//上一曲
preSong();
break;
}
}
/**
* musicNotification 來的控制
*
* @param intent
*/
private void musicNotificationService(int k) {
switch (k) {
case 30001:
// 播放
playSong();
break;
case 30002:
// 下一首
nextSong();
break;
case 30003:
// 關閉通知欄
musicNotifi.onCancelMusicNotifi();
// 停止音樂
stop();
break;
}
}
/**
* 播放
*/
private void playSong() {
if (mp.isPlaying()) {
pause();
} else {
if (currentTime > 0) {
resume();
} else {
if (mm != null) {
play(mm.getUrl());
}
}
}
sendModelToMusicActivity();
}
/**
* 下一曲
*/
private void nextSong() {
currentTime=0;
if(position<0){
position=0;
}
if (mmSize > 0) {
position++;
if (position < mmSize) {
// 不超過長度
mm = musics.get(position);
play(mm.getUrl());
} else {
// 超過長度 播放第一首
mm = musics.get(0);
play(mm.getUrl());
}
}
}
/**
* 上一曲
*/
private void preSong(){
currentTime=0;
if(position<0){
position=0;
}
if (mmSize > 0) {
position--;
if (position>=0) {
//不小于0
mm = musics.get(position);
play(mm.getUrl());
} else {
// 超過長度 播放第一首,小于0 ,播放第一首
mm = musics.get(0);
play(mm.getUrl());
}
}
}
/**
* MainActivity來的數據
*
* @param intent
*/
private void mainToService(Intent intent) {
if (MAIN_MUSIC_INTENT_FLAG == flag) {
// 來自MainActivity 的操作
position = intent.getIntExtra(MAIN_MUSIC_INTENT_KEY, -1);
// showToast("3.來自MainActivity 問候 : " + position);
if (position > -1) {
// 播放
if (musics != null) {
mm = musics.get(position);
} else {
// showToast("4.MUSICS IS NULL");
}
if (mm != null) {
/**
* 1.播放音樂 2.更新狀態欄 3.如果進度條運行的話,通知改變
*/
play(mm.getUrl());
} else {
// showToast("5.musics 數據去哪里了!");
}
} else {
// showToast("6.這怎么可能發生呢?!");
}
} else {
// showToast("不是MainActivity 來的數據");
}
}
}
}
~~~
? ? ? ?(2)注冊Service?
? ? ? ? ? ? ? ? ? 在AndroidMainfest.xml中注冊?
~~~
<service android:name="cn.labelnet.framework.MusicService" >
</service>
~~~
## (3)啟動服務
這里使用了 Serializable序列化進行了數據傳遞,不建議使用這個,效率低,可以使用Parcleable,效率高;
參考文章 :
[Android - Parcelable接口用法 和 與 Serializable 的區別](http://blog.csdn.net/lablenet/article/details/50315349)
~~~
@Override
public void getMusicModelList(List<MusicModel> models) {
// 初始化 Service : 開啟MUSIC服務
Intent intent = new Intent(MainActivity.this, MusicService.class);
intent.putExtra(MUSIC_INTENT_KEY, (Serializable) models);
intent.addFlags(MUSIC_INTENT_FLAG);
startService(intent);
//關閉進度條
swiperefresh_wei.setRefreshing(false);
isRefresh=false;
}
~~~
### 3.播放器控制
播放器控制主要實現是實現 play() , pause() , resume(),stop() 四個方法,進行播放,暫停,重新播放控制。同時實現onPrepaed() , onCompletion() , onError() 三個監聽,實現對播放器的控制,最基本的操作就在此了。后面的通知欄控制,歌詞/進度條頁面控制 均是使用 這幾個方法進行控制實現。
~~~
<span style="font-family:Comic Sans MS;font-size:18px;">// 音樂播放
private void play(String musicUrl) {
mp.reset();
try {
mp.setDataSource(getApplicationContext(), Uri.parse(musicUrl));
mp.prepareAsync();
} catch (IOException e) {
showToast("網絡錯誤,播放失敗");
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音樂暫停
private void pause() {
if (mp.isPlaying()) {
currentTime = mp.getCurrentPosition();
mp.pause();
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// 音樂繼續播放
private void resume() {
mp.start();
if (currentTime > 0) {
mp.seekTo(currentTime);
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音樂停止
private void stop() {
mp.stop();
try {
mp.prepare();
} catch (IOException e) {
showToast("音樂停止異常");
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// //////////////////////////////Music MediaPlayListener////////////
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// 出錯的時候
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
// 播放完畢的時候
// showToast("播放完畢,準備播放下一首!");
currentTime = 0;
// 改變通知欄
musicNotifi.onUpdataMusicNotifi(mm, false);
//改變MusicActivity
sendModelToMusicActivity();
}
@Override
public void onPrepared(MediaPlayer mp) {
// 準備加載的時候
resume();
sendModelToMusicActivity();
}</span>
~~~
### 4.總結
MusicService 的實現是整個播放器的核心,所以進步的音樂控制均在此實現,算是播放器的心臟。
下篇將實現通知欄實現- MusicSerivce 的臉;
- 前言
- Android實戰 - 音心音樂播放器 (開啟篇)
- Android實戰 - 音心音樂播發器 (主界面實現)
- Android實戰 - 音心播放器 (Music Service 實現)
- Android實戰 - 音心播放器 (通知實現音樂的播放/暫停/下一曲控制)
- Android實戰 - 音心播發器 (MusicService ,Notification, MainActivity 總結)
- Android實戰 - 音心播放器 (MusicActivity-音樂播放頁面界面實現)
- Android實戰 - 音心播放器 (MusciActivity-專輯圖片獲得,基本控制實現)
- Android實戰 - 音心播放器(MusicActivity - 歌詞實現)
- Android實戰 - 音心播放器 (MusicActivity - 倒計時 ,進度條實現)
- Android實戰 - 音心播放器 (MusicActivity ,MusicNotification,MusicService總結)
- Android實戰 - 音心播放器 (MusicListActivity - 分類信息界面實現)
- Android實戰 - 音心播放器 (MusicListActivity - 音樂播放和MainActivity的一個問題)
- Android實戰 - 音心播放器 (啟動頁與社交分享(ShareSDK))
- Android實戰 - 音心播放器 (優化Service退出,按兩下退出應用實現)
- Android實戰 - 音心播放器 (項目總結,應用打包發布)