Skip to Content

Services trong Android

1. Giới thiệu

Service là component chạy background, không có UI, thực hiện long-running operations.

2. Loại Services

TypeDescriptionThread
ForegroundVisible notification, user awareMain (cần background thread)
BackgroundNo notification, limitedMain
BoundClient-server, IPCMain

3. Foreground Service

Khai báo trong Manifest

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> <service android:name=".MusicService" android:foregroundServiceType="mediaPlayback" android:exported="false" />

Implementation

class MusicService : Service() { override fun onCreate() { super.onCreate() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val notification = createNotification() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground( NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK ) } else { startForeground(NOTIFICATION_ID, notification) } // Do work in background thread CoroutineScope(Dispatchers.IO).launch { playMusic() } return START_STICKY } override fun onBind(intent: Intent?): IBinder? = null override fun onDestroy() { super.onDestroy() stopMusic() } private fun createNotification(): Notification { val channelId = createNotificationChannel() return NotificationCompat.Builder(this, channelId) .setContentTitle("Playing Music") .setContentText("Song name") .setSmallIcon(R.drawable.ic_music) .setPriority(NotificationCompat.PRIORITY_LOW) .build() } }

Start/Stop Service

// Start val intent = Intent(context, MusicService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(intent) } else { context.startService(intent) } // Stop context.stopService(Intent(context, MusicService::class.java))

4. Bound Service

class DownloadService : Service() { private val binder = LocalBinder() inner class LocalBinder : Binder() { fun getService(): DownloadService = this@DownloadService } override fun onBind(intent: Intent): IBinder = binder fun downloadFile(url: String) { // Download logic } } // Activity binding class MainActivity : ComponentActivity() { private var downloadService: DownloadService? = null private var isBound = false private val connection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { val binder = service as DownloadService.LocalBinder downloadService = binder.getService() isBound = true } override fun onServiceDisconnected(name: ComponentName?) { isBound = false } } override fun onStart() { super.onStart() Intent(this, DownloadService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() if (isBound) { unbindService(connection) isBound = false } } }

5. onStartCommand Return Values

ValueBehavior
START_STICKYRecreate if killed, null intent
START_NOT_STICKYDon’t recreate
START_REDELIVER_INTENTRecreate with last intent

6. Service Lifecycle

startService() → onCreate() → onStartCommand() → Running → onDestroy() startService() again → ──────────────────┘ bindService() → onCreate() → onBind() → Bound → onUnbind() → onDestroy()

7. Service với Coroutines

class MyService : Service() { private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) override fun onStartCommand(...): Int { scope.launch { doWork() } return START_NOT_STICKY } override fun onDestroy() { super.onDestroy() job.cancel() } }

8. Android 14+ Restrictions

// Android 14+ cần type cụ thể <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

📝 Tóm tắt

Khi nào dùngSolution
Long task, user awareForeground Service
Short taskWorkManager
Client-serverBound Service
Periodic syncWorkManager
Last updated on