Java-Spring实战指南(三十四)Android Service实现后台音乐播放功能

Java-Spring实战指南(三十四)Android Service实现后台音乐播放功能

  • 前言
  • 前置准备
  • 一、Service核心概念
    • [1.1 什么是Service?](#1.1 什么是Service?)
    • [1.2 Service生命周期(重点)](#1.2 Service生命周期(重点))
    • [1.3 项目结构总览](#1.3 项目结构总览)
  • 二、代码实现
    • [2.1 第一步:添加音乐资源](#2.1 第一步:添加音乐资源)
    • [2.2 第二步:编写Service核心类(MusicService.java)](#2.2 第二步:编写Service核心类(MusicService.java))
    • [2.3 第三步:编写界面布局(activity_service.xml)](#2.3 第三步:编写界面布局(activity_service.xml))
    • [2.4 第四步:编写Activity交互逻辑(ServiceActivity.java)](#2.4 第四步:编写Activity交互逻辑(ServiceActivity.java))
    • [2.5 第五步:注册组件(AndroidManifest.xml)](#2.5 第五步:注册组件(AndroidManifest.xml))
  • 三、运行测试

前言

上一篇我们完成了SSM+Android跨端登录注册的综合实战,掌握了前后端数据交互的核心逻辑。本节课将聚焦Android另一个核心组件------Service(服务),它是运行在后台的"无界面组件",适合执行长时间运行的任务(如音乐播放、文件下载、网络请求)。

本文将基于 Android Studio 2025.1.4 + 模拟器Android 14 环境,手把手实现"后台音乐播放"功能:通过Service管理音乐播放/暂停逻辑,Activity作为界面交互入口,让你彻底理解Service的生命周期、启动/停止方式以及组件间的通信逻辑。

我的个人主页,欢迎阅读其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Java-Spring入门指南专栏
欢迎指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482


前置准备

开始前确保以下环境和资源就绪:

  • 开发工具:Android Studio 2025.1.4
  • 模拟器/真机:Android 14(API 34)
  • 音乐资源 :准备一首MP3格式音乐,命名为music.mp3(后续放入res/raw文件夹)
  • 核心知识点:了解Android组件基础、Intent通信(前序博客已覆盖)

一、Service核心概念

1.1 什么是Service?

Service是Android四大组件之一,运行在主线程后台(非独立进程),无可视化界面,适合执行不需要用户交互的长时间任务。

Service核心特性 说明 类比场景
无界面 不占用屏幕空间,后台运行 电脑后台的音乐播放器进程
生命周期长 即使Activity销毁,Service可继续运行 关闭视频软件窗口,背景音乐仍播放
主线程运行 默认在主线程执行,耗时操作需手动开子线程 需配合Thread/AsyncTask处理耗时任务

1.2 Service生命周期(重点)

本次实战使用"启动式Service"(startService()启动,stopService()停止),核心生命周期方法如下:

  1. onCreate():Service首次创建时调用(仅执行1次),用于初始化资源(如创建MediaPlayer);
  2. onStartCommand():每次调用startService()时触发,用于执行具体业务逻辑(如播放音乐);
  3. onDestroy():Service销毁时调用(执行stopService()或系统回收),用于释放资源(如停止播放、释放MediaPlayer)。

1.3 项目结构总览

复制代码
com.example.ch9service
├── MusicService.java       // 核心Service:管理音乐播放/暂停/释放
├── ServiceActivity.java    // 界面Activity:提供播放/暂停按钮交互
├── res
│   ├── raw                 // 音乐资源文件夹(存放music.mp3)
│   │   └── music.mp3
│   ├── layout
│   │   └── activity_service.xml  // 界面布局(仅1个按钮)
│   └── values
│       └── strings.xml
└── AndroidManifest.xml     // 清单文件:注册Service和Activity

二、代码实现

2.1 第一步:添加音乐资源

  1. res文件夹下新建raw文件夹(右键res→New→Android Resource Directory→Resource type选raw);
  2. 将MP3音乐文件放入raw文件夹,命名为music.mp3(注意:文件名只能包含字母、数字和下划线,不能有中文)。

2.2 第二步:编写Service核心类(MusicService.java)

MusicService负责音乐播放的核心逻辑:初始化MediaPlayer、播放、停止、释放资源,通过静态变量ispaly记录播放状态。

java 复制代码
package com.example.ch9service;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;

// 后台音乐播放Service
public class MusicService extends Service {
    private MediaPlayer player; // 音乐播放器实例
    public static boolean ispaly = false; // 播放状态标记(静态变量,供Activity访问)

    public MusicService() {
        // 必须实现的构造方法(无参)
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 启动式Service无需绑定,返回null即可(若为绑定式Service需实现)
        throw new UnsupportedOperationException("Not yet implemented");
    }

    // Service创建时调用(仅1次):初始化MediaPlayer
    @Override
    public void onCreate() {
        super.onCreate();
        // 创建MediaPlayer实例,加载raw文件夹中的音乐资源
        player = MediaPlayer.create(this, R.raw.music);
        // 设置音乐循环播放(可选,根据需求调整)
        player.setLooping(true);
    }

    // 每次调用startService()时触发:执行播放逻辑
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 若未在播放,则启动播放
        if (!player.isPlaying()) {
            player.start(); // 开始播放音乐
            ispaly = player.isPlaying(); // 更新播放状态为true
        }
        // 返回值:Service被系统回收后是否重建,START_STICKY表示重建
        return super.onStartCommand(intent, flags, startId);
    }

    // Service销毁时调用:释放资源(关键,避免内存泄漏)
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (player != null) {
            player.stop(); // 停止播放
            ispaly = player.isPlaying(); // 更新播放状态为false
            player.release(); // 释放MediaPlayer资源
            player = null; // 置空,便于GC回收
        }
    }
}

2.3 第三步:编写界面布局(activity_service.xml)

布局仅包含1个按钮,用于切换"播放"和"暂停"状态,采用ConstraintLayout居中布局。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ServiceActivity">

    <!-- 播放/暂停切换按钮 -->
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="播放音乐"
        android:textSize="18sp"
        android:paddingHorizontal="30dp"
        android:paddingVertical="10dp"
        <!-- 约束布局:水平+垂直居中 -->
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

2.4 第四步:编写Activity交互逻辑(ServiceActivity.java)

ServiceActivity作为界面入口,通过按钮点击事件启动/停止MusicService,并根据ispaly状态切换按钮文本。

java 复制代码
package com.example.ch9service;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class ServiceActivity extends AppCompatActivity {

    private Button playBtn; // 播放/暂停按钮

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this); // 启用沉浸式布局
        setContentView(R.layout.activity_service);

        // 适配系统状态栏(避免按钮被遮挡)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        // 绑定按钮控件
        playBtn = findViewById(R.id.button);
        // 设置按钮点击事件
        playBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建Intent,指定目标Service
                Intent intent = new Intent(ServiceActivity.this, MusicService.class);

                // 根据播放状态切换:未播放→启动Service(播放);已播放→停止Service(暂停)
                if (!MusicService.ispaly) {
                    startService(intent); // 启动Service,触发onStartCommand()
                    playBtn.setText("暂停音乐"); // 切换按钮文本
                } else {
                    stopService(intent); // 停止Service,触发onDestroy()
                    playBtn.setText("播放音乐"); // 切换按钮文本
                }
            }
        });
    }
}

2.5 第五步:注册组件(AndroidManifest.xml)

Service和Activity都需在清单文件中注册,否则无法使用。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication">

        <!-- 注册Service:android:exported="true"允许外部启动(此处为应用内部使用,也可设为false) -->
        <service
            android:name=".MusicService"
            android:enabled="true"
            android:exported="true"></service>

        <!-- 注册Activity(启动页) -->
        <activity
            android:name=".ServiceActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

三、运行测试


我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Java-Spring入门指南专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |

相关推荐
柯南二号2 小时前
【大前端】【Android】 Kotlin 语法超详细解析(2025 最新)
android·kotlin
微学AI5 小时前
Rust语言的深度剖析:内存安全与高性能的技术实现操作
java·安全·rust
程序猿小蒜5 小时前
基于springboot的共享汽车管理系统开发与设计
java·开发语言·spring boot·后端·spring·汽车
lsp程序员0105 小时前
使用 Web Workers 提升前端性能:让 JavaScript 不再阻塞 UI
java·前端·javascript·ui
q***46526 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
hygge9996 小时前
Spring Boot + MyBatis 整合与 MyBatis 原理全解析
java·开发语言·经验分享·spring boot·后端·mybatis
q***25216 小时前
Spring Boot接收参数的19种方式
java·spring boot·后端
WX-bisheyuange6 小时前
基于Spring Boot的民谣网站的设计与实现
java·spring boot·后端
q***14646 小时前
Spring Boot文件上传
java·spring boot·后端