关闭应用程序后,Android 媒体播放器如何继续播放歌曲?
Posted
技术标签:
【中文标题】关闭应用程序后,Android 媒体播放器如何继续播放歌曲?【英文标题】:How do Android mediaplayers continuing playing songs when app is closed? 【发布时间】:2011-07-12 17:19:35 【问题描述】:想知道应用程序关闭后如何播放下一首歌曲,就像播放整张 CD 或播放列表一样...
【问题讨论】:
“应用已关闭”是指根本不显示在任务管理器中,还是只显示在后台? 我想我的意思是当它在后台时。 【参考方案1】:媒体播放器只播放一个音轨。 媒体播放器所做的是监听 onCompletion 事件并播放下一首曲目。
MediaPlayer 绑定到进程,而不是 Activity,因此只要进程运行,它就会一直播放。 Activity 可能会被暂停或销毁,但这不会影响 MediaPlayer 使用的内部线程。
我正在构建一个音频播放器来学习android,你可以看到播放音频文件的服务here
编辑
关于第一条评论:该服务一直在后台运行,并在您“退出”应用程序后继续运行,因为服务和活动的生命周期不同。
为了播放下一首曲目,服务会在 MediaPlayer 上注册一个回调,以便在音频流完成时通知服务。当音频完成时,该服务通过调用 MediaPlayer.release() 清理 MediaPlayer 使用的资源,然后创建一个新的媒体播放器,其中包含要播放的下一个音轨,并注册自己以在该音轨时再次收到通知完成,无穷无尽:)。
MediaPlayer 类不理解播放列表,因此该服务负责在前一曲目完成后播放曲目。
在我创建的AudioPlayer 服务中,一个活动在AudioPlayer 中对曲目进行排队,AudioPlayer 负责按顺序播放它们。
我希望它清楚,如果你有时间,请检查我上面放的 AudioPlayer 服务的代码。这不是纯粹的美丽,但它确实发挥了作用。
【讨论】:
那么每次播放一首新歌时,服务会被销毁并重新创建吗?还是将整个歌曲列表发送到一个服务以通过? 我创建了一个带有按钮的项目,而不是像你那样的列表,来试试你在做什么。这非常有帮助。我之前没有做过服务绑定。当我运行它并按返回,或打开首选项,甚至主页键时,音频停止。有什么特别的东西可以让它持续存在吗? 嗯,我要祝酒,显示手机状态的弹出窗口...尽管它们仍在触发(即使音乐停止了)。所以,这似乎意味着服务仍在运行......【参考方案2】:您可以创建一个服务以在您的应用退出或暂停后继续播放 MediaPlayer。要让 MediaPlayer 播放连续曲目,您可以注册一个 onCompletionListener 来决定接下来播放哪个曲目。这是一个执行此操作的简单示例服务:
package edu.gvsu.cis.muzak;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;
public class MuzakService extends Service
private static final String DEBUG_TAG = "MuzakService";
private MediaPlayer mp;
private String[] tracks =
"http://freedownloads.last.fm/download/288181172/Nocturne.mp3",
"http://freedownloads.last.fm/download/367924875/Behemoths%2BSternentanz.mp3",
"http://freedownloads.last.fm/download/185193341/Snowflake%2BImpromptu.mp3",
"http://freedownloads.last.fm/download/305596593/Prel%25C3%25BAdio.mp3",
"http://freedownloads.last.fm/download/142005075/Piano%2BSonata%2B22%2B-%2Bmovement%2B2%2B%2528Beethoven%2529.mp3",
"http://freedownloads.last.fm/download/106179902/Piano%2BSonata%2B%25231%2B-%2Bmovement%2B%25234%2B%2528Brahms%2529.mp3",
;
private int currentTrack = 0;
@Override
public void onCreate()
super.onCreate();
Log.d(DEBUG_TAG, "In onCreate.");
try
Uri file = Uri.parse(tracks[this.currentTrack]);
mp = new MediaPlayer();
mp.setDataSource(this, file);
mp.prepare();
mp.setOnCompletionListener(new OnCompletionListener()
@Override
public void onCompletion(MediaPlayer mp)
currentTrack = (currentTrack + 1) % tracks.length;
Uri nextTrack = Uri.parse(tracks[currentTrack]);
try
mp.setDataSource(MuzakService.this,nextTrack);
mp.prepare();
mp.start();
catch (Exception e)
// TODO Auto-generated catch block
e.printStackTrace();
);
catch (Exception e)
Log.e(DEBUG_TAG, "Player failed", e);
@Override
public void onDestroy()
// TODO Auto-generated method stub
super.onDestroy();
Log.d(DEBUG_TAG, "In onDestroy.");
if(mp != null)
mp.stop();
@Override
public int onStartCommand(Intent intent,int flags, int startId)
super.onStart(intent, startId);
Log.d(DEBUG_TAG, "In onStart.");
mp.start();
return Service.START_STICKY_COMPATIBILITY;
@Override
public IBinder onBind(Intent intent)
Log.d(DEBUG_TAG, "In onBind with intent=" + intent.getAction());
return null;
您可以按如下方式在 Activity 中启动此服务:
Intent serv = new Intent(this,MuzakService.class);
startService(serv);
然后像这样停止它:
Intent serv = new Intent(this,MuzakService.class);
stopService(serv);
【讨论】:
如果音频在服务中播放,MediaController 如何适应所有这些? 您的代码在服务中嵌入了曲目,这与有播放列表时有什么不同吗?或者您可以将数组中的歌曲列表或其他内容发送到服务中吗? 当然,本着保持示例简单的精神,我只是在服务中嵌入了一个曲目列表。您可以通过 Bundle 将播放列表/曲目传递给服务,如 here 所述。如果您想要更精细的控制,您可以创建绑定服务。要了解这种服务与上述代码有何不同,请阅读this。 如果您想要一个交互式用户界面,您可以使用 MediaController,或者您可以滚动您自己的用户界面。有一些示例代码 here 展示了如何将 MediaController 与 MediaPlayer 一起使用。 我看过你提到的 MediaController 链接。不过,这并不使用 MediaPlayer 作为服务。如果媒体播放器是一项服务,媒体控制器(大概在创建媒体播放器服务的活动中)是否有一个钩子或其他东西可以在媒体播放器上获得?如果 Activity 进入后台并在一段时间后被带回前台,媒体控制器是否仍然与媒体播放器有“连接”?【参考方案3】:请注意,服务也在前台运行。
请查看官方文档。它用示例代码解释。 使用带有 MediaPlayer 的服务: http://developer.android.com/guide/topics/media/mediaplayer.html#mpandservices
作为前台服务运行
服务通常用于执行后台任务
但是考虑一下正在播放音乐的服务的情况。显然,这是用户主动了解的服务,任何中断都会严重影响体验。此外,它是用户可能希望在执行期间与之交互的服务。在这种情况下,服务应该作为“前台服务”运行。前台服务在系统中具有更高级别的重要性——系统几乎不会杀死该服务,因为它对用户具有直接的重要性。在前台运行时,服务还必须提供状态栏通知,以确保用户知道正在运行的服务并允许他们打开可以与服务交互的活动。
为了将你的服务变成前台服务,你必须为状态栏创建一个通知并从服务中调用 startForeground()
【讨论】:
【参考方案4】:mediap 播放器应该从服务运行,这里我已经将歌曲的数组列表从活动传递到服务,所有歌曲都是通过读取数组列表来运行的
public class MyService extends Service implements OnCompletionListener,
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener
Context context;
private static final String ACTION_PLAY = "PLAY";
private static final String TAG = "SONG SERVICE";
MediaPlayer mediaPlayer;
private int currentTrack = 0;
ArrayList<String> list;
public MyService()
context=getBaseContext();
@Override
public IBinder onBind(Intent intent)
throw new UnsupportedOperationException("Not yet implemented");
@Override
public int onStartCommand(Intent intent, int flags, int startId)
list = (ArrayList<String>)intent.getSerializableExtra("arraylist");
int count=0;
Log.d(TAG, "total count:"+list.size());
//playing song one by one
for (String string : list)
//play(string);
count++;
Log.d(TAG, "count:"+list);
play(currentTrack);
Log.d(TAG, "count:"+count);
if(count==list.size())
//stopSelf();
Log.d(TAG, "stoping service");
//mediaPlayer.setOnCompletionListener(this);
else
Log.d(TAG, "not stoping service");
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
Log.d(TAG, "oncommat");
return START_STICKY;
@Override
public void onCreate()
Toast.makeText(this, "Service was Created", Toast.LENGTH_LONG).show();
@Override
public void onStart(Intent intent, int startId)
// Perform your long running operations here.
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
@Override
public void onDestroy()
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
Log.d("service", "destroyed");
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.release();
@Override
public boolean onError(MediaPlayer mp, int what, int extra)
// TODO Auto-generated method stub
return false;
@Override
public void onPrepared(MediaPlayer mp)
// TODO Auto-generated method stub
private void play(int id)
if(mediaPlayer!=null && mediaPlayer.isPlaying())
Log.d("*****begin*****", "playing");
stopPlaying();
Log.d("*****begin*****", "stoping");
else
Log.d("*****begin*****", "nothing");
Log.d("*****play count*****", "="+currentTrack);
Log.i("******playing", list.get(currentTrack));
Uri myUri1 = Uri.parse(list.get(id));
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudiostreamType(AudioManager.STREAM_MUSIC);
//mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mediaPlayer.setOnPreparedListener(this);
//mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
try
mediaPlayer.setDataSource(context, myUri1);
Log.i("******playing", myUri1.getPath());
catch (IllegalArgumentException e)
Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
catch (SecurityException e)
Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
catch (IllegalStateException e)
Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
catch (IOException e)
e.printStackTrace();
try
mediaPlayer.prepare();
catch (IllegalStateException e)
Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
catch (IOException e)
Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
mediaPlayer.setOnCompletionListener(new OnCompletionListener()
@Override
public void onCompletion(MediaPlayer mp)
currentTrack=currentTrack+1;
play(currentTrack);
/* currentTrack = (currentTrack + 1) % list.size();
Uri nextTrack=Uri.parse(list.get(currentTrack));
try
mediaPlayer.setDataSource(context,nextTrack);
mediaPlayer.prepare();
// mediaPlayer.start();
catch (Exception e)
e.printStackTrace();
*/
);
mediaPlayer.start();
private void stopPlaying()
if (mediaPlayer != null)
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
@Override
public void onCompletion(MediaPlayer mp)
// TODO Auto-generated method stub
【讨论】:
【参考方案5】:答案是 Android 中的服务,如下所述:http://developer.android.com/guide/topics/fundamentals.html as
您将创建一个服务,当您从应用收到播放命令时,您的应用将向后台服务发送消息以播放音乐。服务不会在前台运行,因此即使您将屏幕置于睡眠状态,它也会播放音乐。
Playing BG Music Across Activities in Android
【讨论】:
那么服务是否会在每次歌曲结束时被销毁并重新创建? 一点也不。除非它们主动工作,否则服务不会消耗太多系统资源。即使您的应用程序已关闭,它也可以随时工作。示例服务:短信接收器、闹钟。 我想我应该换个说法。我的意思是,为了连续播放歌曲,你是否创建一个服务,然后当歌曲播放完时,你销毁该服务并为播放列表中的下一首歌曲创建一个新服务?或者您可以传递要播放的歌曲列表,并且该服务在播放完所有歌曲之前一直保持活动状态? 您可以传递一个列表,也可以向该服务发送消息“播放当前正在播放的音乐并开始播放该音乐”。这完全取决于您如何对服务进行编程。你不会杀死服务。如果你杀死服务。它会自动重生。因为只要应用程序说它应该运行,服务就应该一直工作。以上是关于关闭应用程序后,Android 媒体播放器如何继续播放歌曲?的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的 Android 应用中显示 YouTube 浏览? [关闭]
应用程序关闭后,用于媒体控件的 Android 广播接收器停止工作