安卓如何停止后台音乐播放

26

编辑 - 找到了一个简单的5-10行代码解决方案!!!请看下面的我的答案!!!耶!!!!!!

我已经搜索了5个小时,在许多SO的帖子中都找不到答案,而这似乎是最简单明显的东西!!!

编辑 - 顺便说一句,这不是一个音乐播放器应用程序,只是一个查看图片和文本的应用程序,可以打开多个活动,如菜单、关于、查看不同类型的图片等。我只想在查看图片和文本时播放一些简单的背景音乐,为什么这么困难?

另一个编辑 - 看起来主要问题确实是: “为什么按Home按钮不调用onPause或onStop?我该如何停止媒体播放器?”- 这样我就知道什么时候停止媒体播放器了?市场上下载的游戏是怎么做到的?

主页活动开始

然后

媒体播放器开始:

player = MediaPlayer.create(this, R.raw.idil);
player.setLooping(true);
player.setVolume(100,100);
player.start();

在onCreate之外声明了Player

MediaPlayer player;

当打开其他活动时,背景音乐会继续播放,这很好,这正是我想要的。

现在,当我完成我的非常简单的应用程序(仅在不同的活动中显示一些图片和文本)后,我要么点击多次返回到主页/原始活动,然后再点击一次“退出”,或者我只需按主页键“退出”,因为我已经完成了这个应用程序,不需要听那首音乐了。


选项1

onPause重写中调用player.stop();,这不是我想要的,因为当我离开主页活动进入其他活动(如“菜单”和“关于”)时,背景音乐会停止。当我打开新的活动时,我也不想使用暂停和恢复,因为我不希望美丽的背景音乐“跳跃”或被打断。


选项2

@Override
    protected void onPause() {
        super.onPause();
        if (this.isFinishing()){
            player.stop();
        }
    }

这种方式更好,因为背景音乐在活动之间不会停止,当我从主页活动中按“后退”时,音乐会停止,我可以继续在安静的环境下享受我的 Android 手机的乐趣,但是问题是,当按下 HOME 按钮以“退出”应用程序时,讨厌的背景音乐仍在播放。


奇怪地

@Override
protected void onStop() {
        super.onStop();
        if (this.isFinishing()){
            player.stop();
        }
    }

和onPause一样(我确实了解实际的差异)

编辑- player.stop()在super.onStop()上面或下面似乎都没有影响,但它影响了我看不到的某些东西,无论如何,仍然没有解决方案:(

ooooo

ooooo

编辑- 另一个选项- 但不起作用

public void onUserLeaveHint() {
    player.stop();
    super.onUserLeaveHint();
}

按 HOME 键时停止了音乐,但开始新活动时也停止了音乐 :(

编辑 - 我看到了一个非常常见的解决方法,在多个地方都看到过,但似乎太荒谬了:

基本上,要记录已打开和关闭的活动数量(onResume 和 onPause 可能是最好的选择,但这一点不重要),当该计数达到 0 时,停止后台音乐。 是的,这很简单,但为什么我必须以编程方式做到这一点,实际上,这篇文章最大的问题是:

为什么按 HOME 按钮不调用 onPause 或 onStop ???

将其放在 onDestroy 中不是选项,因为只有在系统内存不足或强制关闭应用程序时才会调用 onDestroy,并且这已经有文档说明。

覆盖 HOME 按钮也不是选项,因为我已经读到它是“不可能的”,并且我已经读到它是“极为反感的”,无论哪种情况,我都要避免那样做。

我不认为需要创建 SERVICE,即使我这样做了,什么时候我会停止服务? 看起来我会遇到同样的问题。

现在,让我感到非常困惑的是,每个从 Android 市场下载的游戏和应用程序都有非常美丽的背景音乐,当我按 BACK 或 HOME 因为我已经完成了游戏时,音乐会停止,而不是在后台继续播放。

我非常沮丧,因为我觉得自己错过了一些非常简单的东西,因为这是任何应用程序最基本的生命周期问题之一。

我花了一个月时间阅读 developer.android.com 的每一页,包括 Dev Guide、教程、示例项目,并研究了参考部分,以及在 Google 上搜索了该主题 5 个小时。

我也不明白为什么有 6 或 7 个 SO 线程存在着完全相同的问题,却没有被回答,市场上可以下载的每个应用程序都在你按 HOME 或 BACK 或其他"退出"应用程序并去做手机上的其他事情时停止播放音乐(除非它是一个后台音乐播放器)。

我错过了什么?

9个回答

22

今天我非常开心,而且还有头发 :) 我已经测试过,它有效!

首先,在您的清单中添加以下内容:

<uses-permission android:name="android.permission.GET_TASKS"/>

其次,在你的'主页/主界面'Activity/类中添加以下内容:

  @Override
  protected void onPause() {
    if (this.isFinishing()){ //basically BACK was pressed from this activity
      player.stop();
      Toast.makeText(xYourClassNamex.this, "YOU PRESSED BACK FROM YOUR 'HOME/MAIN' ACTIVITY", Toast.LENGTH_SHORT).show();
    }
    Context context = getApplicationContext();
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
    if (!taskInfo.isEmpty()) {
      ComponentName topActivity = taskInfo.get(0).topActivity; 
      if (!topActivity.getPackageName().equals(context.getPackageName())) {
        player.stop();
        Toast.makeText(xYourClassNamex.this, "YOU LEFT YOUR APP", Toast.LENGTH_SHORT).show();
      }
      else {
        Toast.makeText(xYourClassNamex.this, "YOU SWITCHED ACTIVITIES WITHIN YOUR APP", Toast.LENGTH_SHORT).show();
      }
    }
    super.onPause();
  }

显然,将xYourClassNamex替换为您的类名:)

现在显然您不需要"Toast",但它会告诉您发生了什么。 非常有趣的一点是,当您从“主屏幕/主要”活动返回时,您显然会得到两个Toast,“您已从'主屏幕/主要'活动中按下BACK”,第二个Toast是“您在应用程序中切换了活动”。我相信我知道为什么会发生这种情况,但这并不重要,因为我从意味着我的应用程序不再被“使用”的两种情况中调用“player.stop();”。如果需要更多的工作,请明确说明:) 另外很显然,对于“您在应用程序中切换了活动”,您不需要“else”,因为没有理由“停止/暂停”背景音乐,这就是我所需要的,但是如果您确实需要在启动新活动时执行某些操作,那么请看这里:)

希望这可以帮助任何想知道用户何时“离开/退出/完成”应用程序的人 :)

感谢大家的评论、帖子和帮助!!!耶!

编辑 -

每个活动的onPause都必须包含此部分:

Context context = getApplicationContext();
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
        if (!taskInfo.isEmpty()) {
          ComponentName topActivity = taskInfo.get(0).topActivity; 
          if (!topActivity.getPackageName().equals(context.getPackageName())) {
            player.stop();
            Toast.makeText(xYourClassNamex.this, "YOU LEFT YOUR APP", Toast.LENGTH_SHORT).show();
          }
        }

这样你就能知道用户是否从你的应用中的任何一个活动界面离开了。这是很有用的信息 :)


1
谷歌拒绝使用getRunningTasks的应用程序,因为它仅用于开发目的。 - rfsbraz
android.permission.GET_TASKS 已经废弃。 - Rohit Sharma

2
媒体播放器的工作方式是作为服务运行,并在用户在应用程序中停止它时手动停止。
例如:当媒体播放器正在播放歌曲,用户按下主页键时,显然用户仍想听这首歌曲作为后台任务。完成后,用户通过进入应用程序并停止(即停止播放歌曲的服务)或者如果播放列表已经完成,则停止来手动停止它。很简单。
这是期望的行为。这正是音乐应用程序的工作方式。
更新
@Override
    protected void onPause() {
        super.onPause();
        if (this.isFinishing()){
            player.stop();
        }
    }

需要进行更改

@Override
    protected void onPause() {
        super.onPause();
        if (mediaPlayer != null){
            mediaPlayser.stop();
            if (isFinishing()){
            mediaPlayer.stop();
            mediaPlayer.release();
            }
        }
    }

一份解释:

我们检查对象不为空的原因很明显。然而,当我们进入暂停状态时,我们要确保媒体播放器停止播放。但是,如果我们即将离开并且歌曲快要播放完毕,那么就停止并释放它。

更新2

请查看此链接:

https://stackoverflow.com/a/6704844/529691

这可用于检测您的应用是否已移出当前应用程序堆栈的顶部。


那很有道理,那么我在我的应用程序中使用什么来播放简单的背景音乐呢?当用户不使用我的应用程序时,他们显然不想听到它,除了MediaPlayer()之外还有其他东西吗? - Mike
这是游戏中常见的特定问题。我忘记他们使用的是什么,让我查一下我的书。 - JoxTraex
你的更新将会在打开新活动时停止背景音乐。我的应用程序与音乐无关,它只是查看照片和文字引用,只是在您浏览时播放背景音乐。当用户决定停止使用我的应用程序时,音乐只需要停止,无论是返回主屏幕还是打开其他应用程序。 - Mike
我尝试过了,但什么也没发生!我把这段代码放在我的BaseActivity中,其他所有活动都继承了它。 - Ruchir Baronia
isFinishing()是什么? - Ruchir Baronia
显示剩余5条评论

1
为什么不直接做一个停止音乐的按钮呢?这样会更方便,而且你也不必每次都强制关闭。
Button exit =(Button) findViewById(R.id.exit);

    exit.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v)
            {
                // TODO: Implement this method

                startActivity(new Intent("com.justme.worldexplorer.EXIT"));             
                finish();
                Music I'd.stop();}});}

这样,如果您不小心按下键盘上的关键硬件按钮,则不会离开

public boolean onKeyDown(int KeyCode,KeyEvent event){


    return false;
}

就这样,在清单中设置好并运行,如果有任何问题,请告诉我。


1

我曾经遇到过同样的问题。我会告诉你我的解决方案。 我重写了每个活动的onStop和onStart,并在一些延迟后计算了我的启动活动数量,当计数达到零时,我暂停了音乐。


这不是一个好的实现,应该遵循的实现是音乐应用程序的示例。 - JoxTraex
lulumeya,这似乎是一个常见的问题,但却没有解决方案。在许多活动中覆盖许多onResume、onStop和onPause方法似乎是一种可怕的方式(我说你的答案很糟糕,因为它确实有效,而我也面临着同样的问题)。但是,难道安卓开发人员必须覆盖数十个方法才能在应用程序中播放一些好听的背景音乐吗? - Mike
如果你遇到了同样的问题,那么很可能是在实现过程中出了一些问题,因为我的应用程序运行得非常好。我的应用程序是一个游戏,所有的活动都有背景音乐。无论如何,如果有什么好的解决方案可行,我也想知道。 - lulumeya
@JoxTraex 在你的示例中,当应用程序中的活动发生变化时,音乐必须暂停一小段时间,我通过这项工作解决了这个问题,但我还没有找到更好的解决方案。我认为说别人的答案很糟糕并不是一个好的态度。 - lulumeya

1
在您的活动的onStart()onStop()方法中,关于如何作为服务的侦听器进行注册/注销,每次侦听器被(注销)注册时,您的服务都会检查其ArrayList<Listener>中是否还有一些侦听器。如果listOfListeners.size()0,则您的服务将调用其stopSelf()方法并终止自身。

至少这是我实现的方式...

所以,您需要的东西是:

  • 2个接口(ObserverObservable

  • Activity中实现Observer,在Service中实现Observable

  • Service中的ArrayList<Observer>

  • Service中的registerObserver(Observer o)unregisterObserver(Observer o)

但这只是我的观点...


这是一种方法,有点不必要,因为你会在列表中有一堆基本上是垃圾的东西。不好。 - JoxTraex
“bunch of basically garbage in a list”是什么意思?如果使用ArrayList<T>List<T>,会影响应用程序的性能吗@JoxTraex?在Android中如何实现“观察者模式”,或者在Android中绝对不可以使用它,应该使用其他解决方案来实现相同的功能? - herom
我的帖子明确表示不覆盖主页键,但我会研究一下。 - Mike
你无法获取主页按钮,它永远不会被传递到应用程序中。 - JoxTraex
非常感谢 @JoxTraex,直到现在我才注意到这个问题(我从来没有在我的应用中捕捉 HOME 按钮)... - herom
显示剩余2条评论

0
private static HashMap<String, Boolean> focus = new HashMap<String, Boolean>();

public static void check(Context context, boolean hasFocus)
{
    handler.removeMessages(0);
    focus.put(((Activity)context).getLocalClassName(), hasFocus);
    handler.sendEmptyMessageDelayed(0, 500);
}

public static void check(String classname, boolean hasFocus)
{
    handler.removeMessages(0);
    focus.put(classname, hasFocus);
    handler.sendEmptyMessageDelayed(0, 500);
}

private static Handler handler = new Handler()
{
    @Override
    public void handleMessage(Message msg)
    {
        Iterator<Boolean> it = focus.values().iterator();
        while(it.hasNext())
        {
            if(it.next())
                return;
        }
        pause();
        super.handleMessage(msg);
    }
};

这是一个管理类方法。

每个活动都实现像后代码一样

@Override
public void onWindowFocusChanged(boolean hasFocus)
{
    MusicManager.check(this, hasFocus);
    super.onWindowFocusChanged(hasFocus);
}

我正在使用这段代码来完成我的游戏,它已经发布并且运行良好。

这段代码的哪一部分很糟糕?有更好的解决方案吗?我想知道是否存在。

Mike,你回答我说我的答案很糟糕而且不起作用,这是什么问题?


1
lulumeya,这绝对不是一种侮辱或者其他什么的,我的确说过“这是一个可怕的方法(我是因为它能够运行而说你的答案很糟糕,而且我也遇到了同样的问题)”-哦,天啊,我刚看到我的笔误,应该是“我是说你的答案并不糟糕”,请原谅。我的意思是,虽然我还没有尝试或测试过你的答案,但我相信它完全可以正常工作。但是你使用了30行代码,访问了多个方法,使用了处理程序、循环,而它只需要一行简单的代码--“如果应用正在关闭/退出,则执行此操作/停止音乐”...为什么这不已经在Android中了?!?!? - Mike
如果用户通过HOME、BACK、ANSWERCALL或其他方式离开应用程序{ 进行一些工作,保存,停止音乐,暂停游戏等...} - Mike

0

对于那些仍在寻找棒棒糖解决方案或者不想使用权限的人,我提供了另一种解决方案作为最后的资源。我们可以测量用户离开应用的时间。如果超过X毫秒,则可以认为他已经离开应用并停止音乐。

因此,我使用Application子类存储离线时间,并使用BaseActivity覆盖所有活动的onPause和onResume方法。


0

我在开发过程中遇到了同样的问题。

我使用了 onPause 和 onResume 以及在主容器中调用片段而不是活动,并使用 pause() 和 start() 方法解决了这个问题。

我在单例上保留了一个标志,以便知道用户何时关闭了音乐。

我发现另一个问题是我的应用程序(我不知道它是否是 Android 构建的一部分)在重新获取播放器之前就运行了 onResume 方法,因此我在 while 循环中运行接收播放器,以便它不会成为 null 指针。这很有效,因为它在不同的线程上运行,从而使应用程序能够在不崩溃的情况下运行 onResume 并同时播放音乐。

我认为这更好(需要编写更少的代码),而且没有权限侵犯。


0
在每个活动中,在onStart()中绑定服务,并在onStop()中解除绑定。当启动服务时,只需绑定它,不要调用startService(...)。
通过调用startService(),服务将继续运行,直到调用stopService(),而不管绑定到它的活动数量。如果您不调用start service,则服务将在所有活动都从其解除绑定后自动停止。
因此,通过在每个活动的onStart()和onStop()方法上绑定和解除绑定,您会发现服务(音乐)将一直持续到您使用主页按钮、返回键、屏幕超时等离开应用程序为止。

你永远不知道Android何时会结束最后一个活动。因此,也许你会按下主屏幕按钮并继续听音乐... - Vito Valov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接