我在这个问题上挣扎了一段时间,最后我使用下面的类让它正常工作。这个想法是使用一个带有 Looper 的线程,该线程保持活动状态并处理所有 MediaPlayer 逻辑。
如果服务收到明确的停止消息,我只会退出 looper 并清理线程
public class PlayService extends IntentService {
private static PowerManager.WakeLock wakeLock;
private static final String TAG = "PlayService";
private static String LOCK_NAME = TAG;
public static final String EXTRA_FILE = "file";
public static final String ACTION_PLAY = "play";
private static final String ACTION_STOP = "stop";
public static final int MSG_START = 1;
public static final int MSG_STOP = 2;
public PlayService(String name) {
super(name);
}
public PlayService() {
super("PlayService");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void onHandleIntent(Intent intent) {
Message msg = new Message();
if (ACTION_PLAY.equalsIgnoreCase(intent.getAction())) {
String fileName = intent.getExtras().getString(EXTRA_FILE);
msg.what = MSG_START;
msg.obj = fileName;
} else if (ACTION_STOP.equalsIgnoreCase(intent.getAction())) {
msg.what = MSG_STOP;
}
try {
PlayMediaThread.getInstance(this).sendMessage(msg);
} catch (InterruptedException e) {
Log.e(TAG, e.getMessage());
}
}
public static PowerManager.WakeLock acquireLock(Context context) {
if (wakeLock == null || !wakeLock.isHeld()) {
PowerManager powerManager = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
LOCK_NAME);
wakeLock.setReferenceCounted(true);
wakeLock.acquire();
}
return wakeLock;
}
public static void releaseLock(Context context) {
if (wakeLock != null && wakeLock.isHeld()) {
wakeLock.release();
}
}
}
class PlayMediaThread extends Thread implements OnCompletionListener,
OnErrorListener, OnPreparedListener {
private static Semaphore semaphore = new Semaphore(0);
private static Handler handler;
private static Looper myLooper;
private static Context context;
private static String currentFileName;
private static MediaPlayer player;
private static PlayMediaThread instance;
private static final String TAG = "PlayMediaThread";
private PlayMediaThread(Context context) throws InterruptedException {
super(TAG);
PlayMediaThread.context = context;
start();
// To insure that the looper was initialized correctly before return an
// instance
semaphore.acquire();
}
public static PlayMediaThread getInstance(Context context)
throws InterruptedException {
if (instance == null) {
instance = new PlayMediaThread(context);
}
PlayMediaThread.context = context;
return instance;
}
public void sendMessage(Message msg) {
handler.sendMessage(msg);
}
public void quitLooper() {
try {
if (myLooper != null) {
myLooper.quit();
Log.i(TAG, "After quit");
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
}
@Override
public void run() {
Looper.prepare();
myLooper = Looper.myLooper();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == PlayService.MSG_START) {
startPlayer((String) msg.obj);
} else if (msg.what == PlayService.MSG_STOP) {
// when stop command is coming from the activity i.e. user
// explicitly clicked stop, I quit the looper and
// clean the thread
stopPlayer(true);
}
}
};
semaphore.release();
Log.i(TAG, "Before Loop");
Looper.loop();
}
private void stopPlayer(boolean clean) {
if (player != null) {
if (player.isPlaying()) {
player.stop();
}
player.release();
player = null;
}
if (clean) {
PlayService.releaseLock(context);
quitLooper();
instance = null;
}
}
private void startPlayer(String fileName) {
if (player != null && player.isPlaying() && currentFileName != null
&& currentFileName.equalsIgnoreCase(fileName)) {
return;
}
currentFileName = fileName;
stopPlayer(false);
player = new MediaPlayer();
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
player.setOnPreparedListener(this);
try {
player.setDataSource(context, Uri.parse(currentFileName));
player.prepare();
player.start();
PlayService.acquireLock(context);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
@Override
public boolean onError(MediaPlayer mp, int arg1, int arg2) {
Log.e(TAG, "onError");
stopPlayer(true);
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
Log.d(TAG, "onCompletion");
// Just to quit the looper and clean the thread
stopPlayer(true);
}
@Override
public void onPrepared(MediaPlayer mp) {
Log.d(TAG, "onPrepared");
}
}