我们将使用现代的Android开发技术栈:

- 语言: Kotlin
- 架构: MVVM (Model-View-ViewModel)
- UI: Jetpack Compose (更现代、更简洁) 或 XML Layout (更传统、兼容性好)
- 异步处理: Kotlin Coroutines + Flow
- 网络请求: Retrofit
- 音频播放: ExoPlayer (Google官方推荐,功能强大,性能优秀)
- 依赖注入: Hilt
第一步:项目配置 (build.gradle.kts / build.gradle)
在项目的 build.gradle 文件中添加必要的依赖。
// 在项目级 build.gradle 中
buildscript {
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.48' // 或者最新版本
classpath 'com.google.gms:google-services:4.4.0' // 如果需要Firebase
}
}
// 在应用级 build.gradle 中
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
// ...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.4' // 与 Compose BOM 版本匹配
}
}
dependencies {
// Compose BOM (Bill of Materials)
implementation platform('androidx.compose:compose-bom:2025.10.01')
// Compose 核心库
implementation 'androidx.activity:activity-compose:1.8.0'
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.material:material-icons-extended'
// ViewModel & LiveData
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
// Hilt (依赖注入)
implementation 'com.google.dagger:hilt-android:2.48'
kapt 'com.google.dagger:hilt-compiler:2.48'
implementation 'androidx.hilt:hilt-navigation-compose:1.1.0'
// Retrofit & OkHttp (网络请求)
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
// ExoPlayer (音频播放)
implementation 'androidx.media3:media3-exoplayer:1.2.0' // 使用 media3 命名空间
implementation 'androidx.media3:media3-exoplayer-hls:1.2.0' // 支持 HLS 流
implementation 'androidx.media3:media3-ui:1.2.0' // ExoPlayer 的默认 UI 组件
// Gson (JSON解析)
implementation 'com.google.code.gson:gson:2.10.1'
}
第二步:创建数据模型
定义音乐列表和单个音乐信息的实体类。
// Music.kt
data class Music(
val id: String,
val title: String,
val artist: String,
val url: String, // 网络音频URL
val coverUrl: String? = null, // 封面图片URL
val duration: Long = 0L // 时长,单位毫秒
)
// 假设我们的API返回一个音乐列表
// data class MusicResponse(val musics: List<Music>)
第三步:网络请求 (Retrofit)
创建一个Retrofit服务来获取音乐列表。
// ApiService.kt
interface ApiService {
@GET("music-list") // 假设API端点
suspend fun getMusicList(): List<Music> // 直接返回List<Music>
}
// RetrofitModule.kt (使用Hilt管理)
@Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {
@Provides
@Singleton
fun provideApiService(): ApiService {
return Retrofit.Builder()
.baseUrl("https://your-api-base-url.com/") // 替换为你的API地址
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
第四步:数据层 (Repository)
Repository负责从数据源(这里是网络API)获取数据,并暴露给ViewModel。

// MusicRepository.kt
class MusicRepository @Inject constructor(
private val apiService: ApiService
) {
private val _musicList = MutableStateFlow<List<Music>>(emptyList())
val musicList: StateFlow<List<Music>> = _musicList
suspend fun fetchMusicList() {
try {
val musics = apiService.getMusicList()
_musicList.value = musics
} catch (e: Exception) {
// 处理错误,例如设置一个空列表或错误状态
_musicList.value = emptyList()
}
}
}
第五步:播放器核心 (ExoPlayer)
创建一个单例的播放器管理器,负责播放、暂停、切换歌曲等操作。
// PlayerManager.kt
class PlayerManager @Inject constructor() : Player.Listener {
private var exoPlayer: ExoPlayer? = null
private var _currentMusic = MutableStateFlow<Music?>(null)
val currentMusic: StateFlow<Music?> = _currentMusic
private var _isPlaying = MutableStateFlow(false)
val isPlaying: StateFlow<Boolean> = _isPlaying
fun initializePlayer(context: Context) {
if (exoPlayer == null) {
exoPlayer = ExoPlayer.Builder(context).build().also {
it.addListener(this) // 添加监听器来监听播放状态变化
}
}
}
fun releasePlayer() {
exoPlayer?.release()
exoPlayer = null
}
fun play(music: Music) {
val current = _currentMusic.value
if (current == null || current.id != music.id) {
_currentMusic.value = music
}
exoPlayer?.setMediaItem(MediaItem.fromUri(Uri.parse(music.url)))
exoPlayer?.prepare()
exoPlayer?.play()
}
fun pause() {
exoPlayer?.pause()
}
fun resume() {
exoPlayer?.play()
}
fun seekTo(position: Long) {
exoPlayer?.seekTo(position)
}
fun getDuration(): Long {
return exoPlayer?.duration ?: 0L
}
fun getCurrentPosition(): Long {
return exoPlayer?.currentPosition ?: 0L
}
// Player.Listener 接口实现
override fun onPlaybackStateChanged(playbackState: Int) {
_isPlaying.value = playbackState == Player.STATE_READY
}
}
第六步:ViewModel
ViewModel连接View和Model/Repository,处理UI逻辑。
// MusicPlayerViewModel.kt
@HiltViewModel
class MusicPlayerViewModel @Inject constructor(
private val repository: MusicRepository,
private val playerManager: PlayerManager
) : ViewModel() {
val musicList = repository.musicList
val currentMusic = playerManager.currentMusic
val isPlaying = playerManager.isPlaying
init {
viewModelScope.launch {
repository.fetchMusicList()
}
}
fun onMusicSelected(music: Music) {
playerManager.play(music)
}
fun onPlayPauseClick() {
if (playerManager.isPlaying.value) {
playerManager.pause()
} else {
playerManager.resume()
}
}
fun onProgressChanged(newProgress: Float) {
val duration = playerManager.getDuration()
if (duration > 0) {
val newPosition = (newProgress * duration).toLong()
playerManager.seekTo(newPosition)
}
}
override fun onCleared() {
super.onCleared()
playerManager.releasePlayer()
}
}
第七步:UI 层 (Jetpack Compose 示例)
使用Jetpack Compose来构建用户界面。
// MusicPlayerScreen.kt
@Composable
fun MusicPlayerScreen(
viewModel: MusicPlayerViewModel = hiltViewModel()
) {
val musicList by viewModel.musicList.collectAsState()
val currentMusic by viewModel.currentMusic.collectAsState()
val isPlaying by viewModel.isPlaying.collectAsState()
val currentPosition by remember { derivedStateOf { viewModel.playerManager.getCurrentPosition() }
