使用Android Service實(shí)現(xiàn)后臺(tái)音樂(lè)播放功能
前言
本文將聚焦Android另一個(gè)核心組件——Service(服務(wù)),它是運(yùn)行在后臺(tái)的“無(wú)界面組件”,適合執(zhí)行長(zhǎng)時(shí)間運(yùn)行的任務(wù)(如音樂(lè)播放、文件下載、網(wǎng)絡(luò)請(qǐng)求)。
本文將基于 Android Studio 2025.1.4 + 模擬器Android 14 環(huán)境,手把手實(shí)現(xiàn)“后臺(tái)音樂(lè)播放”功能:通過(guò)Service管理音樂(lè)播放/暫停邏輯,Activity作為界面交互入口,讓你徹底理解Service的生命周期、啟動(dòng)/停止方式以及組件間的通信邏輯。
前置準(zhǔn)備
開(kāi)始前確保以下環(huán)境和資源就緒:
- 開(kāi)發(fā)工具:Android Studio 2025.1.4
- 模擬器/真機(jī):Android 14(API 34)
- 音樂(lè)資源:準(zhǔn)備一首MP3格式音樂(lè),命名為
music.mp3(后續(xù)放入res/raw文件夾) - 核心知識(shí)點(diǎn):了解Android組件基礎(chǔ)、Intent通信(前序博客已覆蓋)
一、Service核心概念
1.1 什么是Service?
Service是Android四大組件之一,運(yùn)行在主線(xiàn)程后臺(tái)(非獨(dú)立進(jìn)程),無(wú)可視化界面,適合執(zhí)行不需要用戶(hù)交互的長(zhǎng)時(shí)間任務(wù)。
| Service核心特性 | 說(shuō)明 | 類(lèi)比場(chǎng)景 |
|---|---|---|
| 無(wú)界面 | 不占用屏幕空間,后臺(tái)運(yùn)行 | 電腦后臺(tái)的音樂(lè)播放器進(jìn)程 |
| 生命周期長(zhǎng) | 即使Activity銷(xiāo)毀,Service可繼續(xù)運(yùn)行 | 關(guān)閉視頻軟件窗口,背景音樂(lè)仍播放 |
| 主線(xiàn)程運(yùn)行 | 默認(rèn)在主線(xiàn)程執(zhí)行,耗時(shí)操作需手動(dòng)開(kāi)子線(xiàn)程 | 需配合Thread/AsyncTask處理耗時(shí)任務(wù) |
1.2 Service生命周期(重點(diǎn))
本次實(shí)戰(zhàn)使用“啟動(dòng)式Service”(startService()啟動(dòng),stopService()停止),核心生命周期方法如下:
onCreate():Service首次創(chuàng)建時(shí)調(diào)用(僅執(zhí)行1次),用于初始化資源(如創(chuàng)建MediaPlayer);onStartCommand():每次調(diào)用startService()時(shí)觸發(fā),用于執(zhí)行具體業(yè)務(wù)邏輯(如播放音樂(lè));onDestroy():Service銷(xiāo)毀時(shí)調(diào)用(執(zhí)行stopService()或系統(tǒng)回收),用于釋放資源(如停止播放、釋放MediaPlayer)。
1.3 項(xiàng)目結(jié)構(gòu)總覽

com.example.ch9service ├── MusicService.java // 核心Service:管理音樂(lè)播放/暫停/釋放 ├── ServiceActivity.java // 界面Activity:提供播放/暫停按鈕交互 ├── res │ ├── raw // 音樂(lè)資源文件夾(存放music.mp3) │ │ └── music.mp3 │ ├── layout │ │ └── activity_service.xml // 界面布局(僅1個(gè)按鈕) │ └── values │ └── strings.xml └── AndroidManifest.xml // 清單文件:注冊(cè)Service和Activity
二、代碼實(shí)現(xiàn)
2.1 第一步:添加音樂(lè)資源
- 在
res文件夾下新建raw文件夾(右鍵res→New→Android Resource Directory→Resource type選raw); - 將MP3音樂(lè)文件放入
raw文件夾,命名為music.mp3(注意:文件名只能包含字母、數(shù)字和下劃線(xiàn),不能有中文)。
2.2 第二步:編寫(xiě)Service核心類(lèi)(MusicService.java)
MusicService負(fù)責(zé)音樂(lè)播放的核心邏輯:初始化MediaPlayer、播放、停止、釋放資源,通過(guò)靜態(tài)變量ispaly記錄播放狀態(tài)。
package com.example.ch9service;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
// 后臺(tái)音樂(lè)播放Service
public class MusicService extends Service {
private MediaPlayer player; // 音樂(lè)播放器實(shí)例
public static boolean ispaly = false; // 播放狀態(tài)標(biāo)記(靜態(tài)變量,供Activity訪(fǎng)問(wèn))
public MusicService() {
// 必須實(shí)現(xiàn)的構(gòu)造方法(無(wú)參)
}
@Override
public IBinder onBind(Intent intent) {
// 啟動(dòng)式Service無(wú)需綁定,返回null即可(若為綁定式Service需實(shí)現(xiàn))
throw new UnsupportedOperationException("Not yet implemented");
}
// Service創(chuàng)建時(shí)調(diào)用(僅1次):初始化MediaPlayer
@Override
public void onCreate() {
super.onCreate();
// 創(chuàng)建MediaPlayer實(shí)例,加載raw文件夾中的音樂(lè)資源
player = MediaPlayer.create(this, R.raw.music);
// 設(shè)置音樂(lè)循環(huán)播放(可選,根據(jù)需求調(diào)整)
player.setLooping(true);
}
// 每次調(diào)用startService()時(shí)觸發(fā):執(zhí)行播放邏輯
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 若未在播放,則啟動(dòng)播放
if (!player.isPlaying()) {
player.start(); // 開(kāi)始播放音樂(lè)
ispaly = player.isPlaying(); // 更新播放狀態(tài)為true
}
// 返回值:Service被系統(tǒng)回收后是否重建,START_STICKY表示重建
return super.onStartCommand(intent, flags, startId);
}
// Service銷(xiāo)毀時(shí)調(diào)用:釋放資源(關(guān)鍵,避免內(nèi)存泄漏)
@Override
public void onDestroy() {
super.onDestroy();
if (player != null) {
player.stop(); // 停止播放
ispaly = player.isPlaying(); // 更新播放狀態(tài)為false
player.release(); // 釋放MediaPlayer資源
player = null; // 置空,便于GC回收
}
}
}
2.3 第三步:編寫(xiě)界面布局(activity_service.xml)
布局僅包含1個(gè)按鈕,用于切換“播放”和“暫停”狀態(tài),采用ConstraintLayout居中布局。
<?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="播放音樂(lè)"
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 第四步:編寫(xiě)Activity交互邏輯(ServiceActivity.java)
ServiceActivity作為界面入口,通過(guò)按鈕點(diǎn)擊事件啟動(dòng)/停止MusicService,并根據(jù)ispaly狀態(tài)切換按鈕文本。
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);
// 適配系統(tǒng)狀態(tài)欄(避免按鈕被遮擋)
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);
// 設(shè)置按鈕點(diǎn)擊事件
playBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 創(chuàng)建Intent,指定目標(biāo)Service
Intent intent = new Intent(ServiceActivity.this, MusicService.class);
// 根據(jù)播放狀態(tài)切換:未播放→啟動(dòng)Service(播放);已播放→停止Service(暫停)
if (!MusicService.ispaly) {
startService(intent); // 啟動(dòng)Service,觸發(fā)onStartCommand()
playBtn.setText("暫停音樂(lè)"); // 切換按鈕文本
} else {
stopService(intent); // 停止Service,觸發(fā)onDestroy()
playBtn.setText("播放音樂(lè)"); // 切換按鈕文本
}
}
});
}
}
2.5 第五步:注冊(cè)組件(AndroidManifest.xml)
Service和Activity都需在清單文件中注冊(cè),否則無(wú)法使用。
<?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">
<!-- 注冊(cè)Service:android:exported="true"允許外部啟動(dòng)(此處為應(yīng)用內(nèi)部使用,也可設(shè)為false) -->
<service
android:name=".MusicService"
android:enabled="true"
android:exported="true"></service>
<!-- 注冊(cè)Activity(啟動(dòng)頁(yè)) -->
<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>
三、運(yùn)行測(cè)試

到此這篇關(guān)于使用Android Service實(shí)現(xiàn)后臺(tái)音樂(lè)播放功能的文章就介紹到這了,更多相關(guān)Android Service后臺(tái)音樂(lè)播放內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Input系統(tǒng)按鍵事件的分發(fā)處理示例詳解
這篇文章主要為大家介紹了Input系統(tǒng)按鍵事件的分發(fā)處理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
android實(shí)現(xiàn)上滑屏幕隱藏底部菜單欄的示例
這篇文章主要介紹了android實(shí)現(xiàn)上滑屏幕隱藏底部菜單欄的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Android?Activity通用懸浮可拖拽View封裝的思路詳解
這篇文章主要介紹了Android?Activity通用懸浮可拖拽View封裝,實(shí)現(xiàn)思路是通過(guò)封裝通用的基礎(chǔ)懸浮View,繼承通用View,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
android在連拍菜單中增加連拍張數(shù)選項(xiàng)功能實(shí)現(xiàn)代碼
想要增加連拍張數(shù)選項(xiàng)需要在entries, entryvalues中添加兩項(xiàng),同時(shí)在mtk_strings.xml中添加相應(yīng)的字符串,具體如下,感興趣的朋友可以參考下哈2013-06-06
Android開(kāi)發(fā)自定義雙向SeekBar拖動(dòng)條控件
這篇文章主要為大家介紹了Android開(kāi)發(fā)自定義雙向SeekBar拖動(dòng)條控件使用實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Android中的指紋識(shí)別demo開(kāi)發(fā)實(shí)例
這篇文章主要介紹了Android中的指紋識(shí)別demo開(kāi)發(fā)實(shí)例的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
kotlin中新增擴(kuò)展函數(shù)功能的原因解析
Kotlin擴(kuò)展函數(shù)通過(guò)語(yǔ)法糖在不修改原類(lèi)的前提下添加功能,遵循開(kāi)閉原則,適用于第三方庫(kù)和基礎(chǔ)類(lèi)型,避免繼承限制,本文給大家介紹kotlin中新增擴(kuò)展函數(shù)功能的原因解析,感興趣的朋友一起看看吧2025-07-07

