Android通过startService播放背景音乐

时间:2022-02-13 06:56:42

关于startservice的基本使用概述及其生命周期可参见《android中startservice基本使用方法概述》。

本文通过播放背景音乐的简单示例,演示startservice的基本使用流程,具体内容如下

系统界面如下:

Android通过startService播放背景音乐

界面上面就两个按钮,”播放音乐并退出activity” 和 “停止播放音乐”。我们在该示例中,通过操纵activity的按钮控制musicservice播放或停止播放音乐。

我将一个名为music.mp3的放到资源目录/res/raw文件夹下面,这样我们在程序中就可以通过r.raw.music引用该音乐文件,放入/res/raw文件夹中的资源文件会保持原来的面貌不会被编译成二进制。

musicservice是用于播放背景音乐的service,其代码如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.ispring.startservicedemo;
 
import android.app.service;
import android.content.intent;
import android.media.mediaplayer;
import android.os.ibinder;
import android.widget.toast;
 
import java.io.ioexception;
 
public class musicservice extends service {
 
  private mediaplayer mediaplayer = null;
 
  private boolean isready = false;
 
  @override
  public void oncreate() {
    //oncreate在service的生命周期中只会调用一次
    super.oncreate();
 
    //初始化媒体播放器
    mediaplayer = mediaplayer.create(this, r.raw.music);
    if(mediaplayer == null){
      return;
    }
 
    mediaplayer.stop();
    mediaplayer.setonerrorlistener(new mediaplayer.onerrorlistener() {
      @override
      public boolean onerror(mediaplayer mp, int what, int extra) {
        mp.release();
        stopself();
        return false;
      }
    });
 
    try{
      mediaplayer.prepare();
      isready = true;
    } catch (ioexception e) {
      e.printstacktrace();
      isready = false;
    }
 
    if(isready){
      //将背景音乐设置为循环播放
      mediaplayer.setlooping(true);
    }
  }
 
  @override
  public int onstartcommand(intent intent, int flags, int startid) {
    //每次调用context的startservice都会触发onstartcommand回调方法
    //所以onstartcommand在service的生命周期中可能会被调用多次
    if(isready && !mediaplayer.isplaying()){
      //播放背景音乐
      mediaplayer.start();
      toast.maketext(this, "开始播放背景音乐", toast.length_long).show();
    }
    return start_sticky;
  }
 
  @override
  public ibinder onbind(intent intent) {
    //该service中不支持bindservice方法,所以此处直接返回null
    return null;
  }
 
  @override
  public void ondestroy() {
    //当调用context的stopservice或service内部执行stopself方法时就会触发ondestroy回调方法
    super.ondestroy();
    if(mediaplayer != null){
      if(mediaplayer.isplaying()){
        //停止播放音乐
        mediaplayer.stop();
      }
      //释放媒体播放器资源
      mediaplayer.release();
      toast.maketext(this, "停止播放背景音乐", toast.length_long).show();
    }
  }
}

musicactivity的代码如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ispring.startservicedemo;
 
import android.app.activity;
import android.content.intent;
import android.os.bundle;
import android.view.view;
import android.widget.button;
 
 
public class musicactivity extends activity implements button.onclicklistener {
 
  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_music);
  }
 
  @override
  public void onclick(view v) {
    if(v.getid() == r.id.btnstart){
      //播放背景音乐
      intent intent = new intent(this, musicservice.class);
      startservice(intent);
      //退出当前activity
      this.finish();
    }else if(v.getid() == r.id.btnstop){
      //停止播放音乐
      intent intent = new intent(this, musicservice.class);
      stopservice(intent);
    }
  }
}

当我们单击了按钮”播放音乐并退出activity”之后,我们首先通过activity的startservice启动musicservice,然后我们立即调用了activity的finish方法销毁了当前的activity。可能大家会问为什么要销毁当前的activity?我们此处之所以调用activity的finish方法不是从功能的角度出发的,而是从理解代码运行的角度故意这么写的: 执行完activity的finish方法之后,当前的activity就销毁了, 在界面上看起来就是当前ui消失了,应用程序退出了,但稍等片刻你会听到背景音乐响起。这从侧面印证了service的一个特性: service与activity等一样,也是一种基本的应用程序组件,service无需依赖任何的activity便可独自在没有任何ui界面的情况下悠闲地在android后台默默运行。

调用了startservice之后,android framework接收到了intent信息,第一次会先创建musicservice的实例,执行musicservice的oncreate回调方法,oncreate在service的生命周期中只会调用一次,我们在其oncreate方法中将r.raw.music初始化为媒体播放器,并调用媒体播放器的prepare方法。然后我们将播放器设置为循环播放状态。需要注意的是,在实际生产环境中,我们应该注册播放器的setonpreparedlistener并调用prepareasync()方法,为了简化代码起见,我们只调用了播放器的同步方法prepare()。

调用了oncreate方法后,android会自动回调其onstartcommand方法,其实每次调用context的startservice都会触发onstartcommand回调方法,所以onstartcommand在service的生命周期中可能会被调用多次。所以我们在musicservice的onstartcommand中做了判断,判断播放器是否是在播放中,如果当前播放器没有播放我们才调用播放器的start方法播放背景音乐。

当我们单击了按钮”播放音乐并退出activity”之后,musicservice启动起来,播放背景音乐,但是activity却销毁了,程序的ui界面不见了。为了能够停止播放背景音乐,我们需要再次单击应用程序图标,重新打开musicactivity,然后单击界面上的”停止播放音乐”按钮,此时我们会调用activity的stopservice方法,android framework接收到要停止服务的intent之后会回调musicservice的ondestroy方法,在该方法中我们停止播放音乐并释放媒体播放器资源。

本文只是通过播放背景音乐这一简单示例演示通过startservice启动service基本使用流程,代码没有进行优化,希望对大家学习service有所帮助。