Service #

一、Service概述 #

Service是Android四大组件之一,用于在后台执行长时间运行的操作,不提供用户界面。Service可以用于播放音乐、下载文件、处理网络请求等场景。

1.1 Service的特点 #

  • 运行在主线程中(需要创建子线程处理耗时操作)
  • 没有用户界面
  • 可以在后台长时间运行
  • 可以与其他组件绑定进行通信

1.2 Service的类型 #

类型 说明
启动服务 通过startService()启动,独立运行
绑定服务 通过bindService()绑定,与组件生命周期绑定
前台服务 显示通知,用户可见,不易被系统杀死

1.3 Service的注册 #

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application>
        <service
            android:name=".MyService"
            android:exported="false" />
    </application>

</manifest>

二、Service生命周期 #

2.1 生命周期图解 #

text
启动服务:
startService() -> onCreate() -> onStartCommand() -> 运行中 -> onDestroy()

绑定服务:
bindService() -> onCreate() -> onBind() -> 运行中 -> onUnbind() -> onDestroy()

启动并绑定:
startService() -> bindService() -> onCreate() -> onStartCommand() -> onBind() 
-> 运行中 -> onUnbind() -> onDestroy()

2.2 生命周期方法 #

kotlin
class MyService : Service() {
    
    override fun onCreate() {
        super.onCreate()
        Log.d("MyService", "onCreate: 服务创建")
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("MyService", "onStartCommand: 服务启动")
        return START_STICKY
    }
    
    override fun onBind(intent: Intent): IBinder? {
        Log.d("MyService", "onBind: 服务绑定")
        return binder
    }
    
    override fun onUnbind(intent: Intent): Boolean {
        Log.d("MyService", "onUnbind: 服务解绑")
        return true
    }
    
    override fun onRebind(intent: Intent) {
        super.onRebind(intent)
        Log.d("MyService", "onRebind: 服务重新绑定")
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("MyService", "onDestroy: 服务销毁")
    }
}

2.3 onStartCommand返回值 #

返回值 说明
START_STICKY 服务被杀死后会重建,intent为null
START_NOT_STICKY 服务被杀死后不会重建
START_REDELIVER_INTENT 服务被杀死后会重建,并重传最后一个intent
START_STICKY_COMPATIBILITY START_STICKY的兼容版本

三、启动服务 #

3.1 创建启动服务 #

kotlin
class DownloadService : Service() {
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val url = intent?.getStringExtra("url")
        
        // 在子线程中执行耗时操作
        Thread {
            downloadFile(url)
            stopSelf()  // 下载完成后停止服务
        }.start()
        
        return START_STICKY
    }
    
    private fun downloadFile(url: String?) {
        // 下载逻辑
    }
    
    override fun onBind(intent: Intent): IBinder? = null
}

3.2 启动和停止服务 #

kotlin
class MainActivity : AppCompatActivity() {
    
    fun startDownload(url: String) {
        val intent = Intent(this, DownloadService::class.java).apply {
            putExtra("url", url)
        }
        startService(intent)
    }
    
    fun stopDownload() {
        val intent = Intent(this, DownloadService::class.java)
        stopService(intent)
    }
}

3.3 使用IntentService(已废弃) #

kotlin
// 推荐使用JobIntentService或WorkManager
class MyJobIntentService : JobIntentService() {
    
    companion object {
        private const val JOB_ID = 1000
        
        fun enqueueWork(context: Context, work: Intent) {
            enqueueWork(context, MyJobIntentService::class.java, JOB_ID, work)
        }
    }
    
    override fun onHandleWork(intent: Intent) {
        // 在后台线程执行
    }
}

四、绑定服务 #

4.1 创建绑定服务 #

kotlin
class MusicService : Service() {
    
    private val binder = MusicBinder()
    private var mediaPlayer: MediaPlayer? = null
    
    inner class MusicBinder : Binder() {
        fun getService(): MusicService = this@MusicService
    }
    
    override fun onCreate() {
        super.onCreate()
        mediaPlayer = MediaPlayer()
    }
    
    override fun onBind(intent: Intent): IBinder {
        return binder
    }
    
    fun play(url: String) {
        mediaPlayer?.apply {
            reset()
            setDataSource(url)
            prepare()
            start()
        }
    }
    
    fun pause() {
        mediaPlayer?.pause()
    }
    
    fun stop() {
        mediaPlayer?.stop()
    }
    
    fun getProgress(): Int {
        return mediaPlayer?.currentPosition ?: 0
    }
    
    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
        mediaPlayer = null
    }
}

4.2 绑定和解绑服务 #

kotlin
class MainActivity : AppCompatActivity() {
    
    private var musicService: MusicService? = null
    private var isBound = false
    
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val binder = service as MusicService.MusicBinder
            musicService = binder.getService()
            isBound = true
        }
        
        override fun onServiceDisconnected(name: ComponentName?) {
            isBound = false
            musicService = null
        }
    }
    
    override fun onStart() {
        super.onStart()
        Intent(this, MusicService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }
    
    override fun onStop() {
        super.onStop()
        if (isBound) {
            unbindService(connection)
            isBound = false
        }
    }
    
    fun playMusic(url: String) {
        musicService?.play(url)
    }
    
    fun pauseMusic() {
        musicService?.pause()
    }
}

五、前台服务 #

5.1 创建前台服务 #

前台服务必须显示一个持续的通知,让用户知道应用正在后台执行操作。

kotlin
class ForegroundService : Service() {
    
    companion object {
        const val CHANNEL_ID = "foreground_service_channel"
        const val NOTIFICATION_ID = 1001
    }
    
    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }
    
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "前台服务",
                NotificationManager.IMPORTANCE_LOW
            )
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("正在下载")
            .setContentText("下载进度: 0%")
            .setSmallIcon(R.drawable.ic_download)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
        
        startForeground(NOTIFICATION_ID, notification)
        
        // 执行后台任务
        Thread {
            for (i in 1..100) {
                Thread.sleep(1000)
                updateNotification(i)
            }
            stopForeground(STOP_FOREGROUND_REMOVE)
            stopSelf()
        }.start()
        
        return START_STICKY
    }
    
    private fun updateNotification(progress: Int) {
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("正在下载")
            .setContentText("下载进度: $progress%")
            .setSmallIcon(R.drawable.ic_download)
            .setProgress(100, progress, false)
            .build()
        
        val manager = NotificationManagerCompat.from(this)
        manager.notify(NOTIFICATION_ID, notification)
    }
    
    override fun onBind(intent: Intent): IBinder? = null
}

5.2 启动前台服务 #

kotlin
class MainActivity : AppCompatActivity() {
    
    fun startForegroundService() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(Intent(this, ForegroundService::class.java))
        } else {
            startService(Intent(this, ForegroundService::class.java))
        }
    }
}

5.3 添加权限 #

Android 9及以上需要添加权限:

xml
<manifest>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
</manifest>

六、Service与线程 #

6.1 Service运行在主线程 #

Service默认运行在主线程中,直接在Service中执行耗时操作会阻塞UI。

kotlin
class MyService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 错误:直接在主线程执行耗时操作
        // Thread.sleep(10000)
        
        // 正确:在子线程执行
        Thread {
            // 耗时操作
        }.start()
        
        return START_STICKY
    }
}

6.2 使用协程 #

kotlin
class MyService : Service(), CoroutineScope {
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        launch {
            // 在IO线程执行耗时操作
            doWork()
        }
        return START_STICKY
    }
    
    private suspend fun doWork() {
        delay(5000)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
}

七、Service最佳实践 #

7.1 使用WorkManager替代后台服务 #

对于不需要立即执行的后台任务,推荐使用WorkManager:

kotlin
class UploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    
    override fun doWork(): Result {
        // 执行后台任务
        uploadFile()
        return Result.success()
    }
}

// 调度任务
val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
    )
    .build()

WorkManager.getInstance(context).enqueue(uploadWork)

7.2 使用Service处理需要立即执行的任务 #

kotlin
// 音乐播放、位置追踪等需要立即执行的任务
class MusicService : Service() {
    // ...
}

7.3 正确处理服务生命周期 #

kotlin
class MyService : Service() {
    private var isRunning = false
    
    override fun onCreate() {
        super.onCreate()
        isRunning = true
    }
    
    override fun onDestroy() {
        super.onDestroy()
        isRunning = false
        // 释放资源
    }
}

八、总结 #

本章详细介绍了Service:

  1. Service的基本概念和类型
  2. Service的生命周期
  3. 启动服务的创建和使用
  4. 绑定服务的创建和使用
  5. 前台服务的创建和通知
  6. Service与线程的关系
  7. 最佳实践

Service是Android后台处理的核心组件,合理使用Service可以提升应用的用户体验。

最后更新:2026-03-26