/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lq.service; import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Random; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.PowerManager; import android.preference.PreferenceManager; import android.provider.MediaStore.Audio.Media; import android.util.Log; import android.widget.Toast; import com.lq.activity.MainContentActivity; import com.lq.xpressmusic.R; import com.lq.entity.LyricSentence; import com.lq.entity.TrackInfo; import com.lq.fragment.SettingFragment; import com.lq.listener.OnPlaybackStateChangeListener; import com.lq.receiver.MediaButtonReceiver; import com.lq.util.AudioFocusHelper; import com.lq.util.AudioFocusHelper.MusicFocusable; import com.lq.util.Constant; import com.lq.util.LyricDownloadManager; import com.lq.util.LyricLoadHelper; import com.lq.util.LyricLoadHelper.LyricListener; /** * 这是处理音乐回放的服务,在应用中对媒体的所有处理都交给这个服务。 * * @author lq 2013-6-1 lq2625304@gmail.com */ public class MusicService extends Service implements OnCompletionListener, OnPreparedListener, OnErrorListener, MusicFocusable, LyricListener { public class MusicPlaybackLocalBinder extends Binder { public void registerOnPlaybackStateChangeListener( OnPlaybackStateChangeListener listener) { mOnPlaybackStateChangeListeners.add(listener); } public void unregisterOnPlaybackStateChangeListener( OnPlaybackStateChangeListener listener) { mOnPlaybackStateChangeListeners.remove(listener); } public void registerLyricListener(LyricListener listener) { mLyricListener = listener; } public void unRegisterLyricListener() { mLyricListener = null; } /** * 让MediaPlayer将当前播放跳转到指定播放位置 * * @param milliSeconds * 指定的已播放的毫秒数 * */ public void seekToSpecifiedPosition(int milliSeconds) { if (mState != State.Stopped) { mMediaPlayer.seekTo(milliSeconds); } } /** 改变播放模式 */ public void changePlayMode() { mPlayMode = (mPlayMode + 1) % 4; if (mMediaPlayer != null) { // 如果正在播放歌曲 switch (mPlayMode) { case PlayMode.REPEAT_SINGLE: // 如果是单曲循环,给MediaPlayer启动单曲播放 mMediaPlayer.setLooping(true); break; default: // 如果不是单曲循环,取消MediaPlayer的单曲播放 mMediaPlayer.setLooping(false); break; } } // 通知各个OnPlaybackStateChangeListener播放模式已经改变,并传递新的播放 for (int i = 0; i < mOnPlaybackStateChangeListeners.size(); i++) { mOnPlaybackStateChangeListeners.get(i).onPlayModeChanged( mPlayMode); } } /** * 设置当前的播放列表 * * @param list * 播放列表,每项包含每首歌曲的详细信息 * */ public void setCurrentPlayList(List<TrackInfo> list) { mPlayList.clear(); if (list != null) { mPlayList.addAll(list); mHasPlayList = true; mRequestPlayPos = 0; } else { mHasPlayList = false; mRequestPlayPos = -1; mRequsetPlayId = -1; } } /** 向当前播放队列追加歌曲 */ public void appendToCurrentPlayList(List<TrackInfo> list) { if (list == null) { return; } // 只添加当前播放列表中没有的歌曲 boolean existed = false; for (int i = 0; i < list.size(); i++) { existed = false; for (int j = 0; j < mPlayList.size(); j++) { if (mPlayList.get(j).equals(list.get(i))) { existed = true; break; } } if (!existed) { mPlayList.add(list.get(i)); } } mHasPlayList = true; if (mState == State.Stopped) { mRequestPlayPos = 0; processPlayRequest(); } } /** 获取当前播放的信息 */ public Bundle getCurrentPlayInfo() { Bundle bundle = new Bundle(); TrackInfo item = null; int currentPlayPos = 0; if (mState == State.Playing || mState == State.Paused) { item = mPlayingSong; currentPlayPos = mMediaPlayer.getCurrentPosition(); } bundle.putParcelable(Constant.PLAYING_MUSIC_ITEM, item); bundle.putInt(Constant.CURRENT_PLAY_POSITION, currentPlayPos); bundle.putInt(Constant.PLAYING_STATE, mState); bundle.putInt(Constant.PLAY_MODE, mPlayMode); bundle.putInt(Constant.PLAYING_SONG_POSITION_IN_LIST, mPlayingSongPos); bundle.putParcelableArrayList(Constant.DATA_LIST, mPlayList); return bundle; } /** 如果当前正在播放歌曲,通知LyricListener载入歌词 */ public void requestLoadLyric() { Log.i(TAG, "requestLoadLyric"); if (mPlayingSong != null && mState != State.Stopped) { Log.i(TAG, "requestLoadLyric--->loadLyric"); loadLyric(mPlayingSong.getData()); mLyricLoadHelper.notifyTime(mMediaPlayer.getCurrentPosition()); } } /** 从当前播放队列移除指定歌曲 */ public void removeSongFromCurrenPlaylist(long trackId) { if (mHasPlayList) { for (int i = 0; i < mPlayList.size(); i++) { if (mPlayList.get(i).getId() == trackId) { if (i < mPlayingSongPos) { mRequestPlayPos = mPlayingSongPos--; } mPlayList.remove(i); break; } } if (mPlayList.size() == 0) { mHasPlayList = false; processStopRequest(); } else if (trackId == mPlayingSong.getId()) { --mPlayingSongPos; // mPlayingSong = mPlayList.get(mPlayingSongPos); if (mPlayMode == PlayMode.SHUFFLE && mPlayQueue.size() != 0) { mRequestPlayPos = mPlayQueue.pop(); } else { mRequestPlayPos--; } } } } } // 打印调试信息用的标记 final static String TAG = MusicService.class.getSimpleName(); // 这里定义的是我们准备处理的Intent的各种动作标记,在这里定义仅仅是为了方便引用, // 真正要让本service处理这些动作,要在AndroidManifest.xml中<service>里的<action>标签内声明它们 public static final String ACTION_PLAY = "com.lq.musicplayer.action.PLAY"; public static final String ACTION_PAUSE = "com.lq.musicplayer.action.PAUSE"; public static final String ACTION_STOP = "com.lq.musicplayer.action.STOP"; public static final String ACTION_PREVIOUS = "com.lq.musicplayer.action.PREVIOUS"; public static final String ACTION_NEXT = "com.lq.musicplayer.action.NEXT"; // 消息类型 public static final int MESSAGE_UPDATE_PLAYING_SONG_PROGRESS = 1; public static final int MESSAGE_ON_LYRIC_LOADED = 2; public static final int MESSAGE_ON_LYRIC_SENTENCE_CHANGED = 3; /** * 歌曲播放的状态(Preparing,Playing,Paused,Stopped) * */ public class State { public static final int Stopped = 0; // MediaPlayer已经停止工作,不再准备播放 public static final int Preparing = 1; // MediaPlayer正在准备中 public static final int Playing = 2; // 正在播放(MediaPlayer已经准备好了) // (但是当丢失音频焦点时,MediaPlayer在此状态下实际上也许已经暂停了, // 但是我们仍然保持这个状态,以表明我们必须在一获得音频焦点时就返回播放状态) public static final int Paused = 3; // 播放暂停 (MediaPlayer处于准备好了的状态) }; /** * 播放模式<br> * 0代表单曲循环,1代表列表循环,2代表顺序播放,3代表随机播放 */ public class PlayMode { public static final int REPEAT_SINGLE = 0; public static final int REPEAT = 1; public static final int SEQUENTIAL = 2; public static final int SHUFFLE = 3; } private int mState = State.Stopped; private int mPlayMode = 1; /** 回放状态变化的观察者集合 */ private ArrayList<OnPlaybackStateChangeListener> mOnPlaybackStateChangeListeners = new ArrayList<OnPlaybackStateChangeListener>(); private MusicPlaybackLocalBinder mBinder = new MusicPlaybackLocalBinder(); private LyricListener mLyricListener = null; // 当前播放列表 private ArrayList<TrackInfo> mPlayList = new ArrayList<TrackInfo>(); // 丢失音频焦点,我们为媒体播放设置一个低音量(1.0f为最大),而不是停止播放 private static final float DUCK_VOLUME = 0.1f; // 我们的媒体播放控制器 private MediaPlayer mMediaPlayer = null; // 音频焦点的辅助类(API LEVEL > 8 时才能使用) private AudioFocusHelper mAudioFocusHelper = null; /** 当前播放列表的播放队列,记录当前播放列表歌曲播放顺序 */ private LinkedList<Integer> mPlayQueue = new LinkedList<Integer>(); private boolean mHasPlayList = false; private boolean mHasLyric = false; private TrackInfo mPlayingSong = null; private int mPlayingSongPos = 0;// XXX 使用这个变量也许会有未知BUG private int mRequestPlayPos = -1; private long mRequsetPlayId = -1; // 我们要播放的音乐是否是来自网络的媒体流 private boolean mIsStreaming = false; // 当从网络获取媒体流时保持一个Wifi锁,防止设备突然与Wifi连接的音频断开 private WifiLock mWifiLock; // 提示播放的通知的ID private final int NOTIFICATION_ID = 1; private AudioManager mAudioManager; private NotificationManager mNotificationManager; private Notification mNotification = null; private ComponentName mAudioBecomingNoisyReceiverName = null; private Random mRandom = new Random(); private LyricLoadHelper mLyricLoadHelper = new LyricLoadHelper(); private LyricDownloadManager mLyricDownloadManager = null; /** Service的Handler,可以延迟指定时间发送消息,而messenger不可以延时发送消息 */ private ServiceIncomingHandler mServiceHandler = new ServiceIncomingHandler( MusicService.this); /** 处理来自客户端的消息 */ private static class ServiceIncomingHandler extends Handler { // 使用弱引用,避免Handler造成的内存泄露(Message持有Handler的引用,内部定义的Handler类持有外部类的引用) WeakReference<MusicService> mServiceWeakReference = null; MusicService mService = null; public ServiceIncomingHandler(MusicService service) { mServiceWeakReference = new WeakReference<MusicService>(service); mService = mServiceWeakReference.get(); } @SuppressWarnings("unchecked") @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_UPDATE_PLAYING_SONG_PROGRESS: if (mService.mState == State.Playing) { int millisecond = mService.mMediaPlayer .getCurrentPosition(); // 通知所有音乐播放的观察者,进度更新了唉 for (int i = 0; i < mService.mOnPlaybackStateChangeListeners .size(); i++) { mService.mOnPlaybackStateChangeListeners.get(i) .onPlayProgressUpdate(millisecond); } if (mService.mHasLyric) { // 通知歌词载入辅助类当前播放时间 mService.mLyricLoadHelper.notifyTime(millisecond); } mService.mServiceHandler.sendEmptyMessageDelayed( MESSAGE_UPDATE_PLAYING_SONG_PROGRESS, 500); } break; case MESSAGE_ON_LYRIC_LOADED: if (mService.mLyricListener != null) { mService.mLyricListener.onLyricLoaded( (List<LyricSentence>) msg.obj, msg.arg1); } break; case MESSAGE_ON_LYRIC_SENTENCE_CHANGED: if (mService.mLyricListener != null) { mService.mLyricListener.onLyricSentenceChanged(msg.arg1); } break; default: super.handleMessage(msg); } } } @Override public void onCreate() { Log.i(TAG, "debug: Creating MusicService"); // 创建Wifi锁(并没有获得该锁,仅仅是创建出来) mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE); mAudioBecomingNoisyReceiverName = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName()); mAudioManager .registerMediaButtonEventReceiver(mAudioBecomingNoisyReceiverName); // 创建音频焦点辅助类,API LEVEL > 8 时SDK才支持音频焦点这个特性 mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this); // 本Service实现LyricListener接口,只是为了做个代理延迟一下歌词事件的调用。 // 因为bindService启动过程稍慢,客户端的LyricListener来不及注册歌词就已经加载好了。 mLyricLoadHelper.setLyricListener(MusicService.this); mLyricDownloadManager = new LyricDownloadManager( getApplicationContext()); startWatchingExternalStorage(); } /** * 通过startService()启动本服务会调用此方法,在此接受并处理发送方传递的Intent。 * 根据传递过来的Intent的action来作出其要求的处理。 */ @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); if (action.equals(ACTION_PLAY)) { if (mHasPlayList) { // 如果是列表中点击的 if (intent.getBooleanExtra(Constant.CLICK_ITEM_IN_LIST, false)) { // 获取到点击的歌曲的ID mRequsetPlayId = intent.getLongExtra( Constant.REQUEST_PLAY_ID, 0); mRequestPlayPos = seekPosInListById(mPlayList, mRequsetPlayId); } else { mRequsetPlayId = mPlayList.get(mRequestPlayPos).getId(); } if (mRequestPlayPos != -1) { processPlayRequest(); } } } else if (action.equals(ACTION_PAUSE)) { processPauseRequest(); } else if (action.equals(ACTION_STOP)) { processStopRequest(); } else if (action.equals(ACTION_PREVIOUS)) { if (mHasPlayList) { processPreviousRequest(true); } } else if (action.equals(ACTION_NEXT)) { if (mHasPlayList) { processNextRequest(true); } } return START_NOT_STICKY; // 当本服务被系统关闭后,不必再重启启动 } @Override public IBinder onBind(Intent arg0) { return mBinder; } @Override public void onDestroy() { // Service关闭时释放所有持有的资源 mState = State.Stopped; relaxResources(true); mAudioFocusHelper.giveUpAudioFocus(); if (mAudioManager != null && mAudioBecomingNoisyReceiverName != null) { mAudioManager .unregisterMediaButtonEventReceiver(mAudioBecomingNoisyReceiverName); } if (mState != State.Stopped) { processStopRequest(); } mLyricListener = null; mOnPlaybackStateChangeListeners.clear(); mOnPlaybackStateChangeListeners = null; mPlayList.clear(); mPlayList = null; unregisterReceiver(mExternalStorageReceiver); } /** MediaPlayer完成了一首歌曲的播放时调用此方法 */ public void onCompletion(MediaPlayer player) { mHasLyric = false; mLyricLoadHelper.setIndexOfCurrentSentence(-1); // 当前播放完成,播放下一首 processNextRequest(false); } /** MediaPlayer完成了准备时调用此方法 */ public void onPrepared(MediaPlayer player) { // 准备完成了,可以播放歌曲了 mState = State.Playing; updateNotification(mPlayingSong.getTitle() + " (playing)"); configAndStartMediaPlayer(); if (!mServiceHandler.hasMessages(MESSAGE_UPDATE_PLAYING_SONG_PROGRESS)) { mServiceHandler .sendEmptyMessage(MESSAGE_UPDATE_PLAYING_SONG_PROGRESS); } } /** * 播放媒体时发生了错误调用此方法,错误发生后停止播放. */ public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(getApplicationContext(), "Media player error! Resetting.", Toast.LENGTH_SHORT).show(); switch (what) { case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: Log.e(TAG, "Error: " + "MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK" + ", extra=" + String.valueOf(extra)); break; case MediaPlayer.MEDIA_ERROR_SERVER_DIED: Log.e(TAG, "Error: " + "MEDIA_ERROR_SERVER_DIED" + ", extra=" + String.valueOf(extra)); break; case MediaPlayer.MEDIA_ERROR_UNKNOWN: Log.e(TAG, "Error: " + "MEDIA_ERROR_UNKNOWN" + ", extra=" + String.valueOf(extra)); break; case -38: Log.e(TAG, "Error: what" + what + ", extra=" + extra + ",see at http://blog.sina.com.cn/s/blog_632b619d01012991.html"); break; default: Log.e(TAG, "Error: what" + what + ", extra=" + extra); break; } processStopRequest(false); return true; // true表示我们处理了发生的错误 } public void onGainedAudioFocus() { Log.i(TAG, "gained audio focus."); // 用新的音频焦点状态来重置MediaPlayer if (mState == State.Playing) configAndStartMediaPlayer(); } public void onLostAudioFocus() { Log.i(TAG, "lost audio focus."); // 以新的焦点参数启动/重启/暂停MediaPlayer if (mMediaPlayer != null && mMediaPlayer.isPlaying()) configAndStartMediaPlayer(); } /** * 确保MediaPlayer存在,并且已经被重置。 这个方法将会在需要时创建一个MediaPlayer, * 或者重置一个已存在的MediaPlayer。 */ private void createMediaPlayerIfNeeded() { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); // 确保我们的MediaPlayer在播放时获取了一个唤醒锁, // 如果不这样做,当歌曲播放很久时,CPU进入休眠从而导致播放停止 // 要使用唤醒锁,要确保在AndroidManifest.xml中声明了android.permission.WAKE_LOCK权限 mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); // 在MediaPlayer在它准备完成时、完成播放时、发生错误时通过监听器通知我们, // 以便我们做出相应处理 mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnErrorListener(this); } else mMediaPlayer.reset(); } private void processPlayRequest() { mAudioFocusHelper.tryToGetAudioFocus(); // 如果处于“停止”状态,直接播放下一首歌曲 // 如果处于“播放”或者“暂停”状态,并且请求播放的歌曲与当前播放的歌曲不同,则播放请求的歌曲 if (mState != State.Stopped && mPlayingSong == null) { mPlayingSongPos = mRequestPlayPos; playSong(); } else if (mState == State.Stopped || (mState != State.Stopped && mPlayingSong.getId() != mRequsetPlayId)) { mPlayingSongPos = mRequestPlayPos; playSong(); } else if (mState == State.Paused && mPlayingSong.getId() == mRequsetPlayId) { // 如果处于“暂停”状态,则继续播放,并且恢复“前台服务”的状态 mState = State.Playing; setUpAsForeground(mPlayingSong.getTitle() + " (playing)"); configAndStartMediaPlayer(); } else if (mMediaPlayer.isLooping()) { mMediaPlayer.start(); } // 通知所有的观察者音乐开始播放了 for (int i = 0; i < mOnPlaybackStateChangeListeners.size(); i++) { mOnPlaybackStateChangeListeners.get(i).onMusicPlayed(); } // 更新进度条 if (!mServiceHandler.hasMessages(MESSAGE_UPDATE_PLAYING_SONG_PROGRESS)) { mServiceHandler .sendEmptyMessage(MESSAGE_UPDATE_PLAYING_SONG_PROGRESS); } } private void processPauseRequest() { if (mState == State.Playing) { mState = State.Paused; mMediaPlayer.pause(); relaxResources(false); // 暂停时,取消“前台服务”的状态,但依然保持MediaPlayer的资源 // 仍然保持着音频焦点 // 通知所有的观察者音乐暂停了 for (int i = 0; i < mOnPlaybackStateChangeListeners.size(); i++) { mOnPlaybackStateChangeListeners.get(i).onMusicPaused(); } } } /** * 播放上一首歌曲。根据播放模式计算出上一首歌的ID,然后调用播放方法。 * * @param fromUser * 是否是来自用户的请求 * */ private void processPreviousRequest(boolean fromUser) { if (mState == State.Playing || mState == State.Paused || mState == State.Stopped) { switch (mPlayMode) { case PlayMode.REPEAT: case PlayMode.SEQUENTIAL: if (--mRequestPlayPos < 0) { mRequestPlayPos = mPlayList.size() - 1; } break; case PlayMode.SHUFFLE: if (mPlayQueue.size() != 0) { mRequestPlayPos = mPlayQueue.pop(); } else { mRequestPlayPos = mRandom.nextInt(mPlayList.size()); } break; case PlayMode.REPEAT_SINGLE: if (fromUser) { // 如果是用户请求播放上一首,就顺序播放上一首 if (--mRequestPlayPos < 0) { mRequestPlayPos = mPlayList.size() - 1; } } else { // 如果不是用户请求,循环播放 mMediaPlayer.setLooping(true); } default: break; } mRequsetPlayId = mPlayList.get(mRequestPlayPos).getId(); processPlayRequest(); } } /** * 播放下一首歌曲。根据播放模式计算出下一首歌的ID,然后调用播放方法 * * @param fromUser * 是否是来自用户的请求 * */ private void processNextRequest(boolean fromUser) { if (mState == State.Playing || mState == State.Paused || mState == State.Stopped) { switch (mPlayMode) { case PlayMode.REPEAT: mRequestPlayPos = (mPlayingSongPos + 1) % mPlayList.size(); break; case PlayMode.SEQUENTIAL: mRequestPlayPos = (mPlayingSongPos + 1) % mPlayList.size(); if (mRequestPlayPos == 0) { if (fromUser) { mRequestPlayPos = 0; } else { // 播放到当前播放列表的最后一首便停止播放 mRequestPlayPos = mPlayList.size() - 1; processStopRequest(); return; } } break; case PlayMode.SHUFFLE: mPlayQueue.push(mPlayingSongPos); mRequestPlayPos = mRandom.nextInt(mPlayList.size()); break; case PlayMode.REPEAT_SINGLE: if (fromUser) { // 如果是用户请求,就顺序播放下一首 mRequestPlayPos = (mPlayingSongPos + 1) % mPlayList.size(); } else { // 如果不是用户请求,循环播放 mMediaPlayer.setLooping(true); } break; default: break; } mRequsetPlayId = mPlayList.get(mRequestPlayPos).getId(); processPlayRequest(); } } private void processStopRequest() { processStopRequest(false); } private void processStopRequest(boolean force) { if (mState == State.Playing || mState == State.Paused || force) { mState = State.Stopped; mRequsetPlayId = -1; mPlayingSongPos = 0; mRequestPlayPos = 0; // 释放所有持有的资源 relaxResources(true); mAudioFocusHelper.giveUpAudioFocus(); // 本服务已经不再使用,终结它 // stopSelf(); // 通知所有的观察者音乐停止播放了 for (int i = 0; i < mOnPlaybackStateChangeListeners.size(); i++) { mOnPlaybackStateChangeListeners.get(i).onMusicStopped(); } } } /** * 释放本服务所使用的资源,包括“前台服务”状态,通知,唤醒锁,和MediaPlayer * * @param releaseMediaPlayer * 指示MediaPlayer是否要释放掉 */ private void relaxResources(boolean releaseMediaPlayer) { // 取消 "foreground service"的状态 stopForeground(true); // 停止并释放MediaPlayer if (releaseMediaPlayer && mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; } // 如果持有Wifi锁,也将其释放 if (mWifiLock.isHeld()) mWifiLock.release(); } /** * 根据音频焦点的设置重新设置MediaPlayer的参数,然后启动或者重启它。 如果我们拥有音频焦点,则正常播放; * 如果没有音频焦点,根据当前的焦点设置将MediaPlayer切换为“暂停”状态或者低声播放。 * 这个方法已经假设mPlayer不为空,所以如果要调用此方法,确保正确的使用它。 */ private void configAndStartMediaPlayer() { if (mAudioFocusHelper.getAudioFocus() == AudioFocusHelper.NoFocusNoDuck) { // 如果丢失了音频焦点也不允许低声播放,我们必须让播放暂停,即使mState处于State.Playing状态。 // 但是我们并不修改mState的状态,因为我们会在获得音频焦点时返回立即返回播放状态。 if (mMediaPlayer.isPlaying()) mMediaPlayer.pause(); return; } else if (mAudioFocusHelper.getAudioFocus() == AudioFocusHelper.NoFocusCanDuck) mMediaPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME); // 设置一个较为安静的音量 else mMediaPlayer.setVolume(1.0f, 1.0f); // 设置大声播放 if (!mMediaPlayer.isPlaying()) mMediaPlayer.start(); } /** * 播放mPlayingSongPos指定的歌曲. */ private void playSong() { mPlayingSong = mPlayList.get(mPlayingSongPos); mState = State.Stopped; relaxResources(false); // 除了MediaPlayer,释放所有资源 try { if (mHasPlayList) { createMediaPlayerIfNeeded(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setDataSource(getApplicationContext(), ContentUris .withAppendedId(Media.EXTERNAL_CONTENT_URI, mPlayingSong.getId())); } else { processStopRequest(true); return; } mState = State.Preparing; if (mLyricListener != null) { loadLyric(mPlayingSong.getData()); } setUpAsForeground(mPlayingSong.getTitle() + " (loading)"); // 在后台准备MediaPlayer,准备完成后会调用OnPreparedListener的onPrepared()方法。 // 在MediaPlayer准备好之前,我们不能调用其start()方法 mMediaPlayer.prepareAsync(); // 如果正在从网络获取歌曲,我们要持有一个Wifi锁,以防止在音乐播放时系统休眠暂停了音乐。 // 另一方面,如果没有从网络获取歌曲,并且持有一个Wifi锁,我们要将它释放掉。 if (mIsStreaming) mWifiLock.acquire(); else if (mWifiLock.isHeld()) mWifiLock.release(); } catch (Exception ex) { Log.e("MusicService", "IOException playing next song: " + ex.getMessage()); ex.printStackTrace(); } // 每次播放新的歌曲的时候,把当前播放的歌曲信息传递给播放观察者 for (int i = 0; i < mOnPlaybackStateChangeListeners.size(); i++) { mOnPlaybackStateChangeListeners.get(i).onPlayNewSong(mPlayingSong); } } /** 更新通知栏. */ private void updateNotification(String text) { PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainContentActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); mNotification.setLatestEventInfo(getApplicationContext(), "LQMusicPlayer", text, pi); mNotificationManager.notify(NOTIFICATION_ID, mNotification); } /** * 将本服务设置为“前台服务”。“前台服务”是一个与用户正在交互的服务, 必须在通知栏显示一个通知表示正在交互 */ private void setUpAsForeground(String text) { Intent intent = new Intent(getApplicationContext(), MainContentActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); mNotification = new Notification(); mNotification.tickerText = text; mNotification.icon = R.drawable.ic_stat_playing; mNotification.flags |= Notification.FLAG_ONGOING_EVENT; mNotification.setLatestEventInfo(getApplicationContext(), getResources().getString(R.string.app_name), text, pi); startForeground(NOTIFICATION_ID, mNotification); } /** * 根据歌曲的ID,寻找出歌曲在当前播放列表中的位置 * * @param list * 歌曲列表 * @param songId * 歌曲ID * @return 返回-1表示未找到 */ public static int seekPosInListById(List<TrackInfo> list, long songId) { int result = -1; if (list != null) { for (int i = 0; i < list.size(); i++) { if (songId == list.get(i).getId()) { result = i; break; } } } return result; } @Override public void onLyricLoaded(List<LyricSentence> lyricSentences, int index) { if (mLyricListener != null) { mLyricListener.onLyricLoaded(lyricSentences, index); } else { // 来自客户端的LyricListener还没来的及注册,就延迟一会等它注册好了再把参数传递给它 mServiceHandler.sendMessageDelayed(Message.obtain(null, MESSAGE_ON_LYRIC_LOADED, index, 0, lyricSentences), 500); } } @Override public void onLyricSentenceChanged(int indexOfCurSentence) { if (mLyricListener != null) { mLyricListener.onLyricSentenceChanged(indexOfCurSentence); } else { // 来自客户端的LyricListener还没来的及注册,就延迟一会等它注册好了再把参数传递给它 mServiceHandler.sendMessageDelayed(Message.obtain(null, MESSAGE_ON_LYRIC_LOADED, indexOfCurSentence, 0), 500); } } /** * 读取歌词文件 * * @param path * 歌曲文件的路径 */ private void loadLyric(String path) { // 取得歌曲同目录下的歌词文件绝对路径 String lyricFilePath = Constant.LYRIC_SAVE_FOLDER_PATH + "/" + mPlayingSong.getTitle() + "_" + mPlayingSong.getArtist() + ".lrc"; File lyricfile = new File(lyricFilePath); if (lyricfile.exists()) { // 本地有歌词,直接读取 Log.i(TAG, "loadLyric()--->本地有歌词,直接读取"); mHasLyric = mLyricLoadHelper.loadLyric(lyricFilePath); } else { // 获取系统设置,是否自动下载歌词 SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); boolean downloadLyricAutomatically = sp.getBoolean( SettingFragment.KEY_DOWNLOAD_LYRIC_AUTOMATICALLY, true); Log.i(TAG, "loadLyric()--->获取系统设置->是否自动下载歌词:" + downloadLyricAutomatically); if (downloadLyricAutomatically) { // 尝试网络获取歌词 Log.i(TAG, "loadLyric()--->本地无歌词,尝试从网络获取"); new LyricDownloadAsyncTask().execute(mPlayingSong.getTitle(), mPlayingSong.getArtist()); } else { // 设置歌词为空 mHasLyric = mLyricLoadHelper.loadLyric(null); } } } private void startWatchingExternalStorage() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); intentFilter.addAction(Intent.ACTION_MEDIA_REMOVED); intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL); intentFilter.addAction(Intent.ACTION_MEDIA_EJECT); intentFilter.setPriority(1000); intentFilter.addDataScheme("file"); registerReceiver(mExternalStorageReceiver, intentFilter); } private BroadcastReceiver mExternalStorageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT) || intent.getAction().equals(Intent.ACTION_MEDIA_REMOVED) || intent.getAction().equals( Intent.ACTION_MEDIA_BAD_REMOVAL)) { if (mState != State.Stopped) { // SD卡移除,停止音乐播放 processStopRequest(true); // 清空当前播放队列 mBinder.setCurrentPlayList(null); // 提示SD卡不可用 Toast.makeText(getApplicationContext(), R.string.sdcard_cannot_use, Toast.LENGTH_SHORT) .show(); } } else if (intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED)) { // SD卡正常挂载 } } }; class LyricDownloadAsyncTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { // 从网络获取歌词,然后保存到本地 String lyricFilePath = mLyricDownloadManager.searchLyricFromWeb( params[0], params[1]); // 返回本地歌词路径 return lyricFilePath; } @Override protected void onPostExecute(String result) { Log.i(TAG, "网络获取歌词完毕,歌词保存路径:" + result); // 读取保存到本地的歌曲 mHasLyric = mLyricLoadHelper.loadLyric(result); }; }; }