Android 백그라운드 작업 가이드
Android 백그라운드 작업 가이드
Android에서 백그라운드 작업을 효율적으로 처리하는 방법을 알아봅니다.
Service
Service vs IntentService
Service
- 메인 스레드에서 실행
- 장시간 작업은 별도 스레드 필요
- 수동으로 종료해야 함
IntentService
- 워커 스레드에서 실행
- 순차적으로 요청 처리
- 작업 완료 후 자동 종료
Service 기본 사용
class MyService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 백그라운드 작업 수행
return START_NOT_STICKY
}
}
Activity와 Service 통신
Messenger 사용
// Activity
val messenger = Messenger(handler)
val intent = Intent(this, MyService::class.java)
intent.putExtra("messenger", messenger)
startService(intent)
// Service
val messenger = intent.getParcelableExtra<Messenger>("messenger")
val message = Message.obtain()
message.what = MSG_RESULT
message.obj = result
messenger?.send(message)
Handler 정의
class IncomingHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val activityRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
val activity = activityRef.get() ?: return
when (msg.what) {
MSG_RESULT -> activity.handleResult(msg.obj)
}
}
}
JobScheduler
Android 5.0+에서 사용 가능한 배터리 효율적인 작업 스케줄러입니다.
JobService 구현
class MyJobService : JobService() {
private var isWorking = false
private var jobCancelled = false
override fun onStartJob(params: JobParameters): Boolean {
isWorking = true
doWorkAsync(params)
return isWorking // true면 작업 진행 중
}
override fun onStopJob(params: JobParameters): Boolean {
jobCancelled = true
return isWorking // true면 재스케줄 필요
}
private fun doWorkAsync(params: JobParameters) {
thread {
// 작업 수행
if (!jobCancelled) {
// 작업 완료
jobFinished(params, false)
}
}
}
}
Manifest 등록
<service
android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
Job 스케줄링
val componentName = ComponentName(this, MyJobService::class.java)
val jobInfo = JobInfo.Builder(JOB_ID, componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.setPersisted(true) // 재부팅 후에도 유지
.build()
val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val result = jobScheduler.schedule(jobInfo)
if (result == JobScheduler.RESULT_SUCCESS) {
Log.d(TAG, "Job scheduled!")
}
JobInfo 옵션
JobInfo.Builder(jobId, componentName)
// 네트워크 조건
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
// 충전 중일 때만
.setRequiresCharging(true)
// 기기 유휴 상태일 때만
.setRequiresDeviceIdle(true)
// 최소 대기 시간
.setMinimumLatency(5000)
// 최대 대기 시간
.setOverrideDeadline(10000)
// 주기적 실행 (15분 이상)
.setPeriodic(15 * 60 * 1000)
// ContentProvider 변경 감지
.addTriggerContentUri(...)
.build()
Job 취소
val jobScheduler = getSystemService(JOB_SCHEDULER_SERVICE) as JobScheduler
jobScheduler.cancel(JOB_ID)
// 또는 모든 Job 취소
jobScheduler.cancelAll()
AlarmManager
정확한 시간에 작업을 실행해야 할 때 사용합니다.
기본 사용
val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
val intent = Intent(this, AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE
)
// 정확한 시간에 실행 (Doze 모드에서도)
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
pendingIntent
)
Doze 모드에서 알람
// Doze 모드에서도 동작 (단, 제한적으로 사용)
alarmManager.setAndAllowWhileIdle(...)
alarmManager.setExactAndAllowWhileIdle(...)
배터리 최적화
Doze 모드
Android 6.0+에서 기기가 유휴 상태일 때 배터리 절약을 위해 적용됩니다.
제한 사항:
- 네트워크 접근 차단
- Wake Lock 무시
- 표준 AlarmManager 지연
- JobScheduler 지연
- Sync Adapter 지연
App Standby
앱이 오랜 시간 사용되지 않으면 제한됩니다.
배터리 최적화 예외 요청
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
intent.data = Uri.parse("package:$packageName")
startActivity(intent)
버전별 제한 사항
Android 8.0 (Oreo) 이상
Background Service 제한:
- 백그라운드에서
startService()호출 불가 - Foreground Service 또는 JobScheduler 사용 필요
// Foreground Service로 시작
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
Broadcast 제한:
- Manifest에 등록된 암시적 브로드캐스트 대부분 수신 불가
- 예외 목록: Broadcast Exceptions
Android 10 (Q) 이상
백그라운드 Activity 시작 제한:
- 백그라운드에서 Activity 시작 불가
- 알림을 통해 사용자 액션으로 시작해야 함
// Full-screen Intent로 대체
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setFullScreenIntent(pendingIntent, true)
.build()
Handler
UI 스레드와 백그라운드 스레드 간 통신에 사용합니다.
class MyHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_UPDATE_UI -> {
// UI 업데이트
}
}
}
}
// 사용
val handler = MyHandler(Looper.getMainLooper())
handler.post { /* UI 작업 */ }
handler.postDelayed({ /* 지연 작업 */ }, 1000)
결론
Android 버전이 올라갈수록 백그라운드 작업에 대한 제한이 강화되고 있습니다. JobScheduler나 WorkManager를 사용하여 시스템과 협력적으로 작업을 스케줄링하고, Foreground Service는 사용자에게 명확히 보여야 할 때만 사용하세요.
권장 사항:
- 즉시 실행: Coroutine, Thread
- 지연 가능: JobScheduler, WorkManager
- 정확한 시간: AlarmManager
- 지속적인 작업: Foreground Service
Comments