[安卓] 深度掌握Android Service:后台任务与音乐播放实战

Service是Android四大组件之一,用于在后台执行长时间运行的操作,无需用户界面。理解Service的工作原理对于开发音乐播放器、文件下载、位置跟踪等应用至关重要。本文将深入探讨Started
Service和Bound Service的区别,并通过完整的音乐播放器示例展示实际应用。

一、Service基础概念与生命周期

1.1 Service是什么?

Service是一种在后台执行长时间运行操作的Android组件,它没有用户界面,可以在应用退出后继续运行。Service主要用于:

  • 音乐播放
  • 文件下载/上传
  • 位置跟踪
  • 后台数据同步
  • 推送消息处理

1.2 Service与Thread的区别

class ServiceVsThreadActivity : AppCompatActivity() {
    
    fun demonstrateDifferences() {
        // 1. Thread(线程)
        val thread = Thread {
            // 执行后台任务
            Thread.sleep(5000)
            // 不能直接更新UI,需要Handler
            runOnUiThread {
                textView.text = "线程任务完成"
            }
        }
        thread.start()
        
        // 2. Service(服务)
        val serviceIntent = Intent(this, MyService::class.java)
        startService(serviceIntent)
        
        // 主要区别:
        // - Service是Android组件,生命周期由系统管理
        // - Thread是Java线程,生命周期由代码控制
        // - Service优先级更高,不易被系统回收
        // - Service可以跨进程通信(AIDL)
    }
}

1.3 Service生命周期

abstract class BaseService : Service() {
    
    companion object {
        const val TAG = "ServiceLifecycle"
    }
    
    // 1. 创建Service(只调用一次)
    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate: Service被创建")
        // 初始化资源:播放器、网络连接、数据库等
    }
    
    // 2. 启动Service(可多次调用)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand: 启动服务, startId=$startId")
        // 处理启动请求
        return super.onStartCommand(intent, flags, startId)
    }
    
    // 3. 绑定Service(可多次绑定)
    override fun onBind(intent: Intent?): IBinder? {
        Log.d(TAG, "onBind: 绑定服务")
        return null // 返回Binder对象
    }
    
    // 4. 解除绑定(所有客户端都解绑后调用)
    override fun onUnbind(intent: Intent?): Boolean {
        Log.d(TAG, "onUnbind: 解除绑定")
        return super.onUnbind(intent)
    }
    
    // 5. 重新绑定
    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
        Log.d(TAG, "onRebind: 重新绑定")
    }
    
    // 6. 销毁Service(只调用一次)
    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: Service被销毁")
        // 释放资源:停止播放器、关闭连接等
    }
}

1.4 Service生命周期图示

Started Service:
onCreate() → onStartCommand() → 运行中 → stopService() → onDestroy()

Bound Service:
onCreate() → onBind() → 绑定中 → onUnbind() → onDestroy()

混合模式:
onCreate() → onStartCommand() → onBind() → 运行中 → onUnbind() → onDestroy()

二、Started Service详解

2.1 基本Started Service

class BasicStartedService : Service() {
    
    companion object {
        const val ACTION_START = "ACTION_START"
        const val ACTION_STOP = "ACTION_STOP"
        const val EXTRA_DATA = "EXTRA_DATA"
    }
    
    private var isRunning = false
    private lateinit var notificationManager: NotificationManagerCompat
    private val notificationId = 1001
    
    override fun onCreate() {
        super.onCreate()
        Log.d("StartedService", "Service已创建")
        notificationManager = NotificationManagerCompat.from(this)
        createNotificationChannel()
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("StartedService", "onStartCommand: startId=$startId, flags=$flags")
        
        when (intent?.action) {
            ACTION_START -> {
                val data = intent.getStringExtra(EXTRA_DATA)
                startBackgroundTask(data, startId)
            }
            ACTION_STOP -> {
                stopForeground(true)
                stopSelf()
            }
            else -> {
                // 默认启动任务
                startBackgroundTask(null, startId)
            }
        }
        
        // 返回值说明:
        // START_STICKY:Service被杀死后会重新创建,但Intent为null
        // START_NOT_STICKY:Service被杀死后不会重新创建
        // START_REDELIVER_INTENT:Service被杀死后会重新创建,并传递最后的Intent
        return START_STICKY
    }
    
    private fun startBackgroundTask(data: String?, startId: Int) {
        if (isRunning) {
            Log.d("StartedService", "任务已在运行中")
            return
        }
        
        isRunning = true
        
        // 启动前台服务(Android 8.0+必须)
        startForeground(notificationId, createNotification("服务运行中"))
        
        // 模拟后台任务
        Thread {
            try {
                for (i in 1..10) {
                    if (!isRunning) break
                    
                    Log.d("StartedService", "处理任务: $i, 数据: $data")
                    
                    // 更新通知
                    updateNotification("处理中: $i/10")
                    
                    Thread.sleep(1000)
                }
                
                // 任务完成,停止服务
                stopForeground(true)
                stopSelf(startId)
                
            } catch (e: InterruptedException) {
                Log.e("StartedService", "任务被中断", e)
            } finally {
                isRunning = false
            }
        }.start()
    }
    
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                "started_service_channel",
                "Started Service",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "Started Service通知通道"
            }
            
            notificationManager.createNotificationChannel(channel)
        }
    }
    
    private fun createNotification(content: String): Notification {
        return NotificationCompat.Builder(this, "started_service_channel")
            .setContentTitle("Started Service")
            .setContentText(content)
            .setSmallIcon(R.drawable.ic_notification)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
    }
    
    private fun updateNotification(content: String) {
        val notification = createNotification(content)
        notificationManager.notify(notificationId, notification)
    }
    
    override fun onBind(intent: Intent?): IBinder? {
        // Started Service通常不绑定
        return null
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("StartedService", "Service已销毁")
        isRunning = false
    }
}

// 在Activity中启动Service
class StartedServiceActivity : AppCompatActivity() {
    
    fun controlStartedService() {
        // 1. 启动Service
        val startIntent = Intent(this, BasicStartedService::class.java).apply {
            action = BasicStartedService.ACTION_START
            putExtra(BasicStartedService.EXTRA_DATA, "任务数据")
        }
        startService(startIntent)
        
        // 2. 停止Service
        val stopIntent = Intent(this, BasicStartedService::class.java).apply {
            action = BasicStartedService.ACTION_STOP
        }
        startService(stopIntent)
        
        // 3. 直接停止Service
        stopService(Intent(this, BasicStartedService::class.java))
        
        // 4. 启动前台Service(API 26+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(startIntent)
        } else {
            startService(startIntent)
        }
    }
}

2.2 IntentService(已废弃,但需要了解)

// IntentService在API 30中已废弃,但概念重要
// 现在推荐使用WorkManager或JobIntentService
@Deprecated("使用WorkManager代替")
class MyIntentService : IntentService("MyIntentService") {
    
    companion object {
        const val ACTION_TASK1 = "ACTION_TASK1"
        const val ACTION_TASK2 = "ACTION_TASK2"
    }
    
    override fun onCreate() {
        super.onCreate()
        Log.d("IntentService", "Service创建")
    }
    
    override fun onHandleIntent(intent: Intent?) {
        // 在后台线程执行
        when (intent?.action) {
            ACTION_TASK1 -> performTask1()
            ACTION_TASK2 -> performTask2()
        }
    }
    
    private fun performTask1() {
        for (i in 1..5) {
            Log.d("IntentService", "任务1: $i")
            Thread.sleep(1000)
        }
    }
    
    private fun performTask2() {
        for (i in 1..3) {
            Log.d("IntentService", "任务2: $i")
            Thread.sleep(1500)
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("IntentService", "Service销毁")
    }
}

三、Bound Service详解

3.1 本地Bound Service(同一进程)

class LocalBoundService : Service() {
    
    inner class LocalBinder : Binder() {
        fun getService(): LocalBoundService = this@LocalBoundService
    }
    
    private val binder = LocalBinder()
    private var counter = 0
    
    override fun onCreate() {
        super.onCreate()
        Log.d("BoundService", "本地Bound Service已创建")
    }
    
    override fun onBind(intent: Intent?): IBinder {
        Log.d("BoundService", "绑定本地Service")
        return binder
    }
    
    override fun onUnbind(intent: Intent?): Boolean {
        Log.d("BoundService", "解绑本地Service")
        return super.onUnbind(intent)
    }
    
    // Service提供的方法
    fun performAction(data: String): String {
        counter++
        return "处理数据: $data, 计数: $counter"
    }
    
    fun getCounter(): Int = counter
    
    fun resetCounter() {
        counter = 0
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("BoundService", "本地Bound Service已销毁")
    }
}

// 在Activity中绑定和使用Service
class BoundServiceActivity : AppCompatActivity() {
    
    private var boundService: LocalBoundService? = null
    private var isBound = false
    
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d("BoundService", "Service已连接")
            val binder = service as LocalBoundService.LocalBinder
            boundService = binder.getService()
            isBound = true
            
            // 使用Service方法
            val result = boundService?.performAction("测试数据")
            Log.d("BoundService", "结果: $result")
        }
        
        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d("BoundService", "Service连接断开")
            isBound = false
            boundService = null
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 绑定Service
        val intent = Intent(this, LocalBoundService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }
    
    fun useBoundService() {
        if (isBound) {
            // 调用Service方法
            val result = boundService?.performAction("用户操作")
            textView.text = result
            
            val count = boundService?.getCounter()
            Log.d("BoundService", "当前计数: $count")
        }
    }
    
    fun resetService() {
        boundService?.resetCounter()
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 解除绑定
        if (isBound) {
            unbindService(connection)
            isBound = false
        }
    }
}

3.2 Messenger Bound Service(跨进程通信)

// Service端
class MessengerService : Service() {
    
    companion object {
        const val MSG_REGISTER_CLIENT = 1
        const val MSG_UNREGISTER_CLIENT = 2
        const val MSG_SET_VALUE = 3
        const val MSG_GET_VALUE = 4
    }
    
    private var value = 0
    private val clients = mutableListOf<Messenger>()
    
    // 处理客户端消息的Handler
    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_REGISTER_CLIENT -> {
                    val client = msg.replyTo
                    if (!clients.contains(client)) {
                        clients.add(client)
                        Log.d("MessengerService", "客户端注册,当前客户端数: ${clients.size}")
                    }
                }
                
                MSG_UNREGISTER_CLIENT -> {
                    val client = msg.replyTo
                    clients.remove(client)
                    Log.d("MessengerService", "客户端注销,当前客户端数: ${clients.size}")
                }
                
                MSG_SET_VALUE -> {
                    value = msg.arg1
                    Log.d("MessengerService", "设置值: $value")
                    
                    // 通知所有客户端
                    notifyClients()
                }
                
                MSG_GET_VALUE -> {
                    val client = msg.replyTo
                    sendValueToClient(client)
                }
                
                else -> super.handleMessage(msg)
            }
        }
    }
    
    private val messenger = Messenger(handler)
    
    override fun onCreate() {
        super.onCreate()
        Log.d("MessengerService", "Messenger Service已创建")
    }
    
    override fun onBind(intent: Intent?): IBinder {
        Log.d("MessengerService", "绑定Messenger Service")
        return messenger.binder
    }
    
    private fun notifyClients() {
        val message = Message.obtain(null, MSG_SET_VALUE, value, 0)
        
        val iterator = clients.iterator()
        while (iterator.hasNext()) {
            val client = iterator.next()
            try {
                client.send(message)
            } catch (e: RemoteException) {
                Log.e("MessengerService", "发送消息失败", e)
                iterator.remove()
            }
        }
    }
    
    private fun sendValueToClient(client: Messenger) {
        val message = Message.obtain(null, MSG_SET_VALUE, value, 0)
        try {
            client.send(message)
        } catch (e: RemoteException) {
            Log.e("MessengerService", "发送值失败", e)
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("MessengerService", "Messenger Service已销毁")
        clients.clear()
    }
}

// 客户端Activity
class MessengerClientActivity : AppCompatActivity() {
    
    private var serviceMessenger: Messenger? = null
    private var isBound = false
    
    // 客户端Messenger
    private val clientMessenger = Messenger(ClientHandler())
    
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d("MessengerClient", "Service已连接")
            serviceMessenger = Messenger(service)
            isBound = true
            
            // 注册客户端
            registerClient()
        }
        
        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d("MessengerClient", "Service连接断开")
            isBound = false
            serviceMessenger = null
        }
    }
    
    inner class ClientHandler : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MessengerService.MSG_SET_VALUE -> {
                    val value = msg.arg1
                    Log.d("MessengerClient", "收到新值: $value")
                    textView.text = "当前值: $value"
                }
                else -> super.handleMessage(msg)
            }
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 绑定Service
        val intent = Intent(this, MessengerService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }
    
    private fun registerClient() {
        val message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT)
        message.replyTo = clientMessenger
        
        try {
            serviceMessenger?.send(message)
        } catch (e: RemoteException) {
            Log.e("MessengerClient", "注册失败", e)
        }
    }
    
    fun setValue(value: Int) {
        if (isBound) {
            val message = Message.obtain(null, MessengerService.MSG_SET_VALUE, value, 0)
            message.replyTo = clientMessenger
            
            try {
                serviceMessenger?.send(message)
            } catch (e: RemoteException) {
                Log.e("MessengerClient", "设置值失败", e)
            }
        }
    }
    
    fun getValue() {
        if (isBound) {
            val message = Message.obtain(null, MessengerService.MSG_GET_VALUE)
            message.replyTo = clientMessenger
            
            try {
                serviceMessenger?.send(message)
            } catch (e: RemoteException) {
                Log.e("MessengerClient", "获取值失败", e)
            }
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        
        // 注销客户端
        if (isBound && serviceMessenger != null) {
            val message = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT)
            message.replyTo = clientMessenger
            
            try {
                serviceMessenger?.send(message)
            } catch (e: RemoteException) {
                Log.e("MessengerClient", "注销失败", e)
            }
        }
        
        // 解除绑定
        if (isBound) {
            unbindService(connection)
            isBound = false
        }
    }
}

四、前台Service与音乐播放器实战

4.1 音乐播放器Service实现

class MusicPlayerService : Service(), MediaPlayer.OnPreparedListener,
    MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
    
    companion object {
        const val ACTION_PLAY = "ACTION_PLAY"
        const val ACTION_PAUSE = "ACTION_PAUSE"
        const val ACTION_STOP = "ACTION_STOP"
        const val ACTION_NEXT = "ACTION_NEXT"
        const val ACTION_PREVIOUS = "ACTION_PREVIOUS"
        const val ACTION_SEEK_TO = "ACTION_SEEK_TO"
        
        const val EXTRA_SONG_URI = "EXTRA_SONG_URI"
        const val EXTRA_SEEK_POSITION = "EXTRA_SEEK_POSITION"
        
        const val NOTIFICATION_ID = 1002
        const val CHANNEL_ID = "music_player_channel"
    }
    
    // 播放状态
    enum class PlaybackState {
        IDLE, PREPARING, PLAYING, PAUSED, STOPPED, ERROR
    }
    
    private lateinit var mediaPlayer: MediaPlayer
    private var playbackState = PlaybackState.IDLE
    private var currentSongUri: Uri? = null
    
    private lateinit var notificationManager: NotificationManagerCompat
    private lateinit var audioManager: AudioManager
    private lateinit var mediaSession: MediaSessionCompat
    
    // 音乐列表
    private val playlist = mutableListOf<Song>()
    private var currentSongIndex = 0
    
    inner class MusicBinder : Binder() {
        fun getService(): MusicPlayerService = this@MusicPlayerService
    }
    
    private val binder = MusicBinder()
    
    data class Song(
        val id: Long,
        val title: String,
        val artist: String,
        val album: String,
        val duration: Long,
        val uri: Uri
    )
    
    override fun onCreate() {
        super.onCreate()
        Log.d("MusicPlayer", "音乐播放器Service已创建")
        
        // 初始化组件
        initMediaPlayer()
        initAudioFocus()
        initMediaSession()
        createNotificationChannel()
        notificationManager = NotificationManagerCompat.from(this)
        
        // 加载播放列表(示例)
        loadPlaylist()
    }
    
    private fun initMediaPlayer() {
        mediaPlayer = MediaPlayer().apply {
            setOnPreparedListener(this@MusicPlayerService)
            setOnCompletionListener(this@MusicPlayerService)
            setOnErrorListener(this@MusicPlayerService)
        }
    }
    
    private fun initAudioFocus() {
        audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
    }
    
    private fun initMediaSession() {
        mediaSession = MediaSessionCompat(this, "MusicPlayerService").apply {
            setCallback(object : MediaSessionCompat.Callback() {
                override fun onPlay() {
                    play()
                }
                
                override fun onPause() {
                    pause()
                }
                
                override fun onStop() {
                    stop()
                }
                
                override fun onSkipToNext() {
                    playNext()
                }
                
                override fun onSkipToPrevious() {
                    playPrevious()
                }
                
                override fun onSeekTo(pos: Long) {
                    seekTo(pos.toInt())
                }
            })
            
            // 设置播放操作
            setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
                    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
        }
    }
    
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "音乐播放器",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "音乐播放器通知通道"
                setSound(null, null) // 静音
                lockscreenVisibility = Notification.VISIBILITY_PUBLIC
            }
            
            notificationManager.createNotificationChannel(channel)
        }
    }
    
    private fun loadPlaylist() {
        // 这里应该从数据库或ContentProvider加载
        // 示例数据
        playlist.apply {
            add(Song(
                1,
                "歌曲1",
                "艺术家1",
                "专辑1",
                180000,
                Uri.parse("content://media/external/audio/media/1")
            ))
            add(Song(
                2,
                "歌曲2",
                "艺术家2",
                "专辑2",
                240000,
                Uri.parse("content://media/external/audio/media/2")
            ))
            // 添加更多歌曲...
        }
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("MusicPlayer", "onStartCommand: ${intent?.action}")
        
        intent?.let {
            handleAction(intent)
        }
        
        return START_STICKY
    }
    
    private fun handleAction(intent: Intent) {
        when (intent.action) {
            ACTION_PLAY -> {
                val songUri = intent.getParcelableExtra<Uri>(EXTRA_SONG_URI)
                if (songUri != null) {
                    playSong(songUri)
                } else {
                    play()
                }
            }
            ACTION_PAUSE -> pause()
            ACTION_STOP -> stop()
            ACTION_NEXT -> playNext()
            ACTION_PREVIOUS -> playPrevious()
            ACTION_SEEK_TO -> {
                val position = intent.getIntExtra(EXTRA_SEEK_POSITION, 0)
                seekTo(position)
            }
        }
    }
    
    override fun onBind(intent: Intent?): IBinder {
        Log.d("MusicPlayer", "绑定音乐播放器Service")
        return binder
    }
    
    fun playSong(songUri: Uri) {
        if (playbackState == PlaybackState.PLAYING ||
            playbackState == PlaybackState.PREPARING) {
            mediaPlayer.stop()
            mediaPlayer.reset()
        }
        
        try {
            currentSongUri = songUri
            playbackState = PlaybackState.PREPARING
            
            mediaPlayer.apply {
                reset()
                setDataSource(applicationContext, songUri)
                prepareAsync() // 异步准备
            }
            
            // 请求音频焦点
            val result = audioManager.requestAudioFocus(
                audioFocusChangeListener,
                AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN
            )
            
            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                // 音频焦点已获取
                updateNotification("正在播放")
            }
            
        } catch (e: Exception) {
            Log.e("MusicPlayer", "播放失败", e)
            playbackState = PlaybackState.ERROR
        }
    }
    
    fun play() {
        when (playbackState) {
            PlaybackState.PAUSED -> {
                mediaPlayer.start()
                playbackState = PlaybackState.PLAYING
                updateNotification("正在播放")
                mediaSession.isActive = true
            }
            PlaybackState.STOPPED -> {
                currentSongUri?.let { playSong(it) }
            }
            else -> {
                // 如果没有歌曲在播放,播放第一首
                if (playlist.isNotEmpty() && currentSongUri == null) {
                    playSong(playlist[0].uri)
                }
            }
        }
    }
    
    fun pause() {
        if (playbackState == PlaybackState.PLAYING) {
            mediaPlayer.pause()
            playbackState = PlaybackState.PAUSED
            updateNotification("已暂停")
        }
    }
    
    fun stop() {
        if (playbackState == PlaybackState.PLAYING ||
            playbackState == PlaybackState.PAUSED) {
            mediaPlayer.stop()
            mediaPlayer.reset()
            playbackState = PlaybackState.STOPPED
            
            // 释放音频焦点
            audioManager.abandonAudioFocus(audioFocusChangeListener)
            
            // 停止前台服务
            stopForeground(true)
            mediaSession.isActive = false
        }
    }
    
    fun playNext() {
        if (playlist.isNotEmpty()) {
            currentSongIndex = (currentSongIndex + 1) % playlist.size
            val nextSong = playlist[currentSongIndex]
            playSong(nextSong.uri)
        }
    }
    
    fun playPrevious() {
        if (playlist.isNotEmpty()) {
            currentSongIndex = (currentSongIndex - 1 + playlist.size) % playlist.size
            val prevSong = playlist[currentSongIndex]
            playSong(prevSong.uri)
        }
    }
    
    fun seekTo(position: Int) {
        if (playbackState == PlaybackState.PLAYING ||
            playbackState == PlaybackState.PAUSED) {
            mediaPlayer.seekTo(position)
        }
    }
    
    fun getCurrentPosition(): Int {
        return if (playbackState == PlaybackState.PLAYING ||
            playbackState == PlaybackState.PAUSED) {
            mediaPlayer.currentPosition
        } else {
            0
        }
    }
    
    fun getDuration(): Int {
        return if (playbackState == PlaybackState.PLAYING ||
            playbackState == PlaybackState.PAUSED) {
            mediaPlayer.duration
        } else {
            0
        }
    }
    
    fun isPlaying(): Boolean {
        return playbackState == PlaybackState.PLAYING
    }
    
    fun getCurrentSong(): Song? {
        return if (currentSongIndex in playlist.indices) {
            playlist[currentSongIndex]
        } else {
            null
        }
    }
    
    fun getPlaylist(): List<Song> = playlist
    
    fun addToPlaylist(song: Song) {
        playlist.add(song)
    }
    
    fun removeFromPlaylist(songId: Long) {
        playlist.removeAll { it.id == songId }
    }
    
    // MediaPlayer回调
    override fun onPrepared(mp: MediaPlayer?) {
        Log.d("MusicPlayer", "媒体准备完成")
        playbackState = PlaybackState.PLAYING
        mediaPlayer.start()
        
        // 启动前台服务
        startForeground(NOTIFICATION_ID, createMusicNotification())
        
        // 发送广播通知播放开始
        sendBroadcast(Intent("com.example.MUSIC_PLAYING"))
        
        // 更新媒体会话
        updateMediaSession()
    }
    
    override fun onCompletion(mp: MediaPlayer?) {
        Log.d("MusicPlayer", "播放完成")
        playbackState = PlaybackState.STOPPED
        
        // 自动播放下一首
        playNext()
        
        // 发送广播通知播放完成
        sendBroadcast(Intent("com.example.MUSIC_COMPLETED"))
    }
    
    override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
        Log.e("MusicPlayer", "播放错误: what=$what, extra=$extra")
        playbackState = PlaybackState.ERROR
        
        // 发送广播通知错误
        val intent = Intent("com.example.MUSIC_ERROR").apply {
            putExtra("what", what)
            putExtra("extra", extra)
        }
        sendBroadcast(intent)
        
        return true // 错误已处理
    }
    
    private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
        when (focusChange) {
            AudioManager.AUDIOFOCUS_GAIN -> {
                // 重新获取焦点,恢复播放
                if (playbackState == PlaybackState.PAUSED) {
                    play()
                }
            }
            AudioManager.AUDIOFOCUS_LOSS -> {
                // 永久失去焦点,停止播放
                pause()
            }
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                // 暂时失去焦点,暂停播放
                if (isPlaying()) {
                    pause()
                }
            }
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                // 短暂失去焦点,降低音量
                mediaPlayer.setVolume(0.2f, 0.2f)
            }
        }
    }
    
    private fun createMusicNotification(): Notification {
        val currentSong = getCurrentSong()
        
        // 创建播放控制PendingIntent
        val playIntent = PendingIntent.getService(
            this,
            0,
            Intent(this, MusicPlayerService::class.java).apply {
                action = ACTION_PLAY
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        val pauseIntent = PendingIntent.getService(
            this,
            1,
            Intent(this, MusicPlayerService::class.java).apply {
                action = ACTION_PAUSE
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        val stopIntent = PendingIntent.getService(
            this,
            2,
            Intent(this, MusicPlayerService::class.java).apply {
                action = ACTION_STOP
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        val nextIntent = PendingIntent.getService(
            this,
            3,
            Intent(this, MusicPlayerService::class.java).apply {
                action = ACTION_NEXT
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        val prevIntent = PendingIntent.getService(
            this,
            4,
            Intent(this, MusicPlayerService::class.java).apply {
                action = ACTION_PREVIOUS
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        // 创建通知
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle(currentSong?.title ?: "音乐播放器")
            .setContentText(currentSong?.artist ?: "未知艺术家")
            .setSmallIcon(R.drawable.ic_music_note)
            .setLargeIcon(getAlbumArt(currentSong))
            .setStyle(androidx.media.app.NotificationCompat.MediaStyle()
                .setMediaSession(mediaSession.sessionToken)
                .setShowActionsInCompactView(0, 1, 2))
            .addAction(
                NotificationCompat.Action(
                    R.drawable.ic_skip_previous,
                    "上一首",
                    prevIntent
                )
            )
            .addAction(
                if (isPlaying()) {
                    NotificationCompat.Action(
                        R.drawable.ic_pause,
                        "暂停",
                        pauseIntent
                    )
                } else {
                    NotificationCompat.Action(
                        R.drawable.ic_play,
                        "播放",
                        playIntent
                    )
                }
            )
            .addAction(
                NotificationCompat.Action(
                    R.drawable.ic_skip_next,
                    "下一首",
                    nextIntent
                )
            )
            .addAction(
                NotificationCompat.Action(
                    R.drawable.ic_stop,
                    "停止",
                    stopIntent
                )
            )
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setOngoing(true)
            .build()
    }
    
    private fun updateNotification(status: String) {
        val notification = createMusicNotification()
        notificationManager.notify(NOTIFICATION_ID, notification)
    }
    
    private fun updateMediaSession() {
        val currentSong = getCurrentSong()
        val metadata = MediaMetadataCompat.Builder()
            .putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentSong?.title)
            .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentSong?.artist)
            .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentSong?.album)
            .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, currentSong?.duration ?: 0)
            .build()
        
        mediaSession.setMetadata(metadata)
    }
    
    private fun getAlbumArt(song: Song?): Bitmap? {
        // 这里应该从MediaStore获取专辑封面
        // 示例:返回默认图片
        return null
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("MusicPlayer", "音乐播放器Service已销毁")
        
        // 释放资源
        mediaPlayer.release()
        mediaSession.release()
        audioManager.abandonAudioFocus(audioFocusChangeListener)
    }
}

4.2 音乐播放器Activity

class MusicPlayerActivity : AppCompatActivity() {
    
    private var musicService: MusicPlayerService? = null
    private var isBound = false
    
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d("MusicPlayerUI", "音乐服务已连接")
            val binder = service as MusicPlayerService.MusicBinder
            musicService = binder.getService()
            isBound = true
            
            updateUI()
            setupSeekBar()
        }
        
        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d("MusicPlayerUI", "音乐服务连接断开")
            isBound = false
            musicService = null
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_music_player)
        
        // 绑定音乐服务
        val intent = Intent(this, MusicPlayerService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
        
        // 启动服务(确保在后台运行)
        startService(intent)
        
        setupUI()
    }
    
    private fun setupUI() {
        // 播放/暂停按钮
        btnPlayPause.setOnClickListener {
            if (musicService?.isPlaying() == true) {
                pauseMusic()
            } else {
                playMusic()
            }
        }
        
        // 停止按钮
        btnStop.setOnClickListener {
            stopMusic()
        }
        
        // 下一首按钮
        btnNext.setOnClickListener {
            playNext()
        }
        
        // 上一首按钮
        btnPrevious.setOnClickListener {
            playPrevious()
        }
        
        // 播放列表按钮
        btnPlaylist.setOnClickListener {
            showPlaylist()
        }
    }
    
    private fun setupSeekBar() {
        // 更新进度
        val handler = Handler(Looper.getMainLooper())
        val updateSeekBar = object : Runnable {
            override fun run() {
                if (isBound && musicService?.isPlaying() == true) {
                    val currentPosition = musicService?.getCurrentPosition() ?: 0
                    val duration = musicService?.getDuration() ?: 1
                    
                    seekBar.progress = (currentPosition.toFloat() / duration * 100).toInt()
                    txtProgress.text = formatTime(currentPosition)
                    txtDuration.text = formatTime(duration)
                }
                handler.postDelayed(this, 1000)
            }
        }
        handler.post(updateSeekBar)
        
        // 拖动SeekBar
        seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                if (fromUser && isBound) {
                    val duration = musicService?.getDuration() ?: 0
                    val newPosition = (progress.toFloat() / 100 * duration).toInt()
                    txtProgress.text = formatTime(newPosition)
                }
            }
            
            override fun onStartTrackingTouch(seekBar: SeekBar?) {
                // 开始拖动
            }
            
            override fun onStopTrackingTouch(seekBar: SeekBar?) {
                if (isBound) {
                    val duration = musicService?.getDuration() ?: 0
                    val newPosition = (seekBar?.progress?.toFloat() ?: 0f) / 100 * duration
                    musicService?.seekTo(newPosition.toInt())
                }
            }
        })
    }
    
    private fun playMusic() {
        if (isBound) {
            musicService?.play()
            updateUI()
        }
    }
    
    private fun pauseMusic() {
        if (isBound) {
            musicService?.pause()
            updateUI()
        }
    }
    
    private fun stopMusic() {
        if (isBound) {
            musicService?.stop()
            updateUI()
        }
    }
    
    private fun playNext() {
        if (isBound) {
            musicService?.playNext()
            updateUI()
        }
    }
    
    private fun playPrevious() {
        if (isBound) {
            musicService?.playPrevious()
            updateUI()
        }
    }
    
    private fun updateUI() {
        if (isBound) {
            val isPlaying = musicService?.isPlaying() == true
            val currentSong = musicService?.getCurrentSong()
            
            // 更新按钮
            btnPlayPause.text = if (isPlaying) "暂停" else "播放"
            btnPlayPause.setCompoundDrawablesWithIntrinsicBounds(
                if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play,
                0, 0, 0
            )
            
            // 更新歌曲信息
            currentSong?.let { song ->
                txtTitle.text = song.title
                txtArtist.text = song.artist
                txtAlbum.text = song.album
            }
        }
    }
    
    private fun showPlaylist() {
        if (isBound) {
            val playlist = musicService?.getPlaylist() ?: emptyList()
            
            val adapter = ArrayAdapter(
                this,
                android.R.layout.simple_list_item_1,
                playlist.map { "${it.title} - ${it.artist}" }
            )
            
            val dialog = AlertDialog.Builder(this)
                .setTitle("播放列表")
                .setAdapter(adapter) { _, position ->
                    val song = playlist[position]
                    musicService?.playSong(song.uri)
                    updateUI()
                }
                .setNegativeButton("关闭", null)
                .create()
            
            dialog.show()
        }
    }
    
    private fun formatTime(milliseconds: Int): String {
        val totalSeconds = milliseconds / 1000
        val minutes = totalSeconds / 60
        val seconds = totalSeconds % 60
        return String.format("%02d:%02d", minutes, seconds)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 解除绑定(但Service可能仍在后台运行)
        if (isBound) {
            unbindService(connection)
            isBound = false
        }
    }
}

五、Service最佳实践与兼容性

5.1 JobIntentService(替代IntentService)

class MyJobIntentService : JobIntentService() {
    
    companion object {
        const val JOB_ID = 1000
        
        fun enqueueWork(context: Context, intent: Intent) {
            enqueueWork(
                context,
                MyJobIntentService::class.java,
                JOB_ID,
                intent
            )
        }
    }
    
    override fun onHandleWork(intent: Intent) {
        // 在后台线程执行
        val action = intent.action ?: return
        
        when (action) {
            "ACTION_UPLOAD" -> uploadFile(intent)
            "ACTION_DOWNLOAD" -> downloadFile(intent)
            "ACTION_PROCESS" -> processData(intent)
        }
    }
    
    private fun uploadFile(intent: Intent) {
        val filePath = intent.getStringExtra("file_path")
        Log.d("JobIntentService", "开始上传文件: $filePath")
        
        // 模拟上传
        for (i in 1..10) {
            if (isStopped) return // 检查是否被停止
            
            Log.d("JobIntentService", "上传进度: ${i * 10}%")
            Thread.sleep(500)
        }
        
        Log.d("JobIntentService", "文件上传完成")
        
        // 发送广播通知完成
        val broadcast = Intent("com.example.UPLOAD_COMPLETE")
        sendBroadcast(broadcast)
    }
    
    private fun downloadFile(intent: Intent) {
        val url = intent.getStringExtra("url")
        Log.d("JobIntentService", "开始下载: $url")
        
        // 模拟下载
        Thread.sleep(3000)
        
        Log.d("JobIntentService", "下载完成")
    }
    
    private fun processData(intent: Intent) {
        val data = intent.getStringExtra("data")
        Log.d("JobIntentService", "处理数据: $data")
        
        // 模拟数据处理
        Thread.sleep(2000)
        
        Log.d("JobIntentService", "数据处理完成")
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("JobIntentService", "Service销毁")
    }
}

5.2 WorkManager(现代后台任务解决方案)

class BackgroundWorkManager {
    
    fun scheduleWork() {
        // 1. 创建约束条件
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresCharging(true)
            .setRequiresBatteryNotLow(true)
            .build()
        
        // 2. 创建WorkRequest
        val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
            .setConstraints(constraints)
            .setBackoffCriteria(
                BackoffPolicy.EXPONENTIAL,
                10,
                TimeUnit.SECONDS
            )
            .setInputData(
                workDataOf(
                    "file_path" to "/path/to/file.txt",
                    "description" to "重要文件"
                )
            )
            .build()
        
        // 3. 调度工作
        WorkManager.getInstance(context).enqueue(uploadWork)
        
        // 4. 观察工作状态
        WorkManager.getInstance(context)
            .getWorkInfoByIdLiveData(uploadWork.id)
            .observe(lifecycleOwner) { workInfo ->
                when (workInfo?.state) {
                    WorkInfo.State.ENQUEUED -> Log.d("WorkManager", "任务已排队")
                    WorkInfo.State.RUNNING -> Log.d("WorkManager", "任务运行中")
                    WorkInfo.State.SUCCEEDED -> Log.d("WorkManager", "任务成功")
                    WorkInfo.State.FAILED -> Log.d("WorkManager", "任务失败")
                    WorkInfo.State.BLOCKED -> Log.d("WorkManager", "任务被阻塞")
                    WorkInfo.State.CANCELLED -> Log.d("WorkManager", "任务被取消")
                    null -> Unit
                }
            }
    }
}

class UploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    
    override fun doWork(): Result {
        return try {
            val filePath = inputData.getString("file_path")
            val description = inputData.getString("description")
            
            Log.d("UploadWorker", "开始上传: $description")
            
            // 模拟上传
            for (i in 1..10) {
                if (isStopped) {
                    return Result.failure()
                }
                
                // 更新进度
                setProgress(workDataOf("progress" to i * 10))
                Thread.sleep(500)
            }
            
            Log.d("UploadWorker", "上传完成")
            Result.success()
            
        } catch (e: Exception) {
            Log.e("UploadWorker", "上传失败", e)
            Result.failure()
        }
    }
}

5.3 Service兼容性处理

class CompatibleService : Service() {
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 检查是否需要前台服务
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Android 8.0+ 必须使用前台服务
            startForegroundServiceIfNeeded()
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            // Android 12+ 的前台服务限制
            checkForegroundServicePermission()
        }
        
        return super.onStartCommand(intent, flags, startId)
    }
    
    @RequiresApi(Build.VERSION_CODES.O)
    private fun startForegroundServiceIfNeeded() {
        val notification = createNotification()
        startForeground(NOTIFICATION_ID, notification)
    }
    
    @RequiresApi(Build.VERSION_CODES.S)
    private fun checkForegroundServicePermission() {
        if (checkSelfPermission(Manifest.permission.FOREGROUND_SERVICE) 
            != PackageManager.PERMISSION_GRANTED) {
            Log.w("CompatibleService", "缺少前台服务权限")
        }
    }
    
    private fun createNotification(): Notification {
        val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel()
            "service_channel"
        } else {
            ""
        }
        
        return NotificationCompat.Builder(this, channelId)
            .setContentTitle("兼容性服务")
            .setContentText("在不同版本Android上运行")
            .setSmallIcon(R.drawable.ic_notification)
            .setPriority(
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    NotificationCompat.PRIORITY_LOW
                } else {
                    NotificationCompat.PRIORITY_DEFAULT
                }
            )
            .build()
    }
    
    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel() {
        val channel = NotificationChannel(
            "service_channel",
            "服务通道",
            NotificationManager.IMPORTANCE_LOW
        )
        
        val manager = getSystemService(NotificationManager::class.java)
        manager.createNotificationChannel(channel)
    }
}

六、面试常见问题

6.1 Service基础概念

  1. Service是什么?它和Thread有什么区别?

    • Service是Android组件,生命周期由系统管理;Thread是Java线程,由代码控制
    • Service优先级更高,不易被系统回收
    • Service可以跨进程通信
  2. Service的生命周期方法有哪些?

    • onCreate(), onStartCommand(), onBind(), onUnbind(), onDestroy()
  3. Started Service和Bound Service有什么区别?

    • Started Service通过startService()启动,独立运行
    • Bound Service通过bindService()绑定,与客户端生命周期关联
    • 可以同时使用两种方式(混合模式)

6.2 Service启动与绑定

  1. 如何启动和停止Started Service?

    • startService(intent)启动,stopService(intent)或stopSelf()停止
  2. 如何绑定和解除绑定Bound Service?

    • bindService(intent, connection, flags)绑定
    • unbindService(connection)解除绑定
  3. ServiceConnection接口有哪些方法?

    • onServiceConnected():Service连接成功时调用
    • onServiceDisconnected():Service异常断开时调用

6.3 Service通信方式

  1. Bound Service有哪些通信方式?

    • Binder(本地Service)
    • Messenger(跨进程,基于AIDL)
    • AIDL(高级跨进程通信)
  2. 如何使用Messenger进行Service通信?

    • Service端创建Handler和Messenger
    • 客户端通过Messenger发送Message
    • 需要双向通信时,客户端也需要创建Messenger

6.4 前台Service

  1. 什么是前台Service?为什么需要它?

    • 前台Service有可见的通知,优先级更高
    • Android 8.0+要求后台Service必须转为前台Service
    • 用于音乐播放、位置跟踪等需要持续运行的任务
  2. 如何创建前台Service?

    • 创建Notification并调用startForeground(notificationId, notification)

6.5 音乐播放器实现

  1. 实现音乐播放器时需要注意什么?

    • 音频焦点管理(AudioFocus)
    • 媒体按钮处理(MediaSession)
    • 播放状态同步(UI与Service)
    • 通知栏控制
  2. 如何处理音频焦点?

    • 使用AudioManager.requestAudioFocus()
    • 实现OnAudioFocusChangeListener处理焦点变化
    • 播放前请求焦点,播放后释放焦点

6.6 兼容性与最佳实践

  1. Android 8.0对Service有哪些限制?

    • 后台Service限制,需要转为前台Service
    • 使用startForegroundService()启动前台服务
  2. IntentService被废弃后,推荐使用什么?

    • JobIntentService(兼容版本)
    • WorkManager(现代解决方案)
    • 协程+Kotlin
  3. WorkManager和Service有什么区别?

    • WorkManager用于可延迟的后台任务
    • Service用于立即执行的长时间任务
    • WorkManager自动处理设备重启、电池优化等

6.7 性能与优化

  1. 如何避免Service内存泄漏?

    • 及时解除绑定(unbindService)
    • 使用WeakReference引用Context
    • 在onDestroy()中释放资源
  2. Service在后台被杀死怎么办?

    • 使用START_STICKY或START_REDELIVER_INTENT
    • 保存状态,重启后恢复
    • 使用前台Service提高优先级

6.8 实际应用场景

  1. 什么时候使用Started Service?什么时候使用Bound Service?

    • Started Service:文件下载、音乐播放(需要独立运行)
    • Bound Service:需要与Activity交互的任务
    • 混合模式:音乐播放器(既独立运行又与UI交互)
  2. 如何实现跨进程的Service通信?

    • 使用Messenger(简单场景)
    • 使用AIDL(复杂场景)
    • 在Manifest中设置android:process属性
  3. Service和BroadcastReceiver如何配合使用?

    • Service执行任务,完成后发送广播
    • Activity注册广播接收器,接收Service通知
    • 实现组件间解耦通信

Service是Android开发中非常重要的组件,掌握其工作原理对于开发高质量应用至关重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值