Android 서비스 지속 실행: JobScheduler 기반 백업 솔루션 구현

Android 생태계에서 백그라운드 서비스의 안정적 운영은 여전히 개발자들이 직면한 핵심 과제다. 특히 메시지 동기화, 위치 추적, 데이터 수집 등 장기 실행이 필수적인 작업의 경우, 시스의 메모리 관리 정책에 의해 서비스가 중단되는 것을 방지하는 기술적 대응이 필요하다.

JobScheduler 아키텍처의 이해

Android 5.0(API 21)부터 도입된 JobScheduler는 Google이 공식 권장하는 백그라운드 작업 스케줄링 메커니즘이다. 이 API는 시스템 자원 상태를 고려하여 작업을 실행하므로, 기존 ServiceBroadcastReceiver 기반의 강제적 재시작 방식보다 높은 생존률을 보장한다.

핵심 컴포넌트 설계

견고한 서비스 유지 계를 구축하려면 다음 요소들의 협력이 필수적이다.

컴포넌트역할
ServiceOrchestrator서비스 생명주기 관리 및 초기화 조건 설정
RescheduleAgentJobScheduler 등록 및 예외 상황 시 재예약
HeartbeatMonitor서비스 상태 점검 및 무응답 시 복구 시도
SystemEventBridge부팅 완료, 패키지 교체 등 이벤트 수신

구현 예시

의존성 설정

모듈 수준 build.gradle에 다음을 추가한다.

dependencies {
    implementation "androidx.work:work-runtime:2.9.0"
}

서비스 기반 클래스 정의

public abstract class PersistentTaskService extends Service {

    private static final int JOB_IDENTIFIER = 0x7a3e;
    private volatile boolean isPerformingWork = false;

    @Override
    public void onCreate() {
        super.onCreate();
        establishRescheduleChannel();
    }

    protected abstract void executeCoreOperation();

    private void establishRescheduleChannel() {
        JobInfo.Builder builder = new JobInfo.Builder(
            JOB_IDENTIFIER,
            new ComponentName(this, RescheduleExecutor.class)
        );
        
        builder.setMinimumLatency(TimeUnit.MINUTES.toMillis(15));
        builder.setOverrideDeadline(TimeUnit.MINUTES.toMillis(30));
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        builder.setPersisted(true);

        JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        scheduler.schedule(builder.build());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!isPerformingWork) {
            isPerformingWork = true;
            new Thread(() -> {
                executeCoreOperation();
                isPerformingWork = false;
            }).start();
        }
        return START_STICKY;
    }
}

JobScheduler 서비스 구현

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class RescheduleExecutor extends JobService {

    @Override
    public boolean onStartJob(JobParameters params) {
        Intent wakeIntent = new Intent(this, TraceWorker.class);
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(wakeIntent);
        } else {
            startService(wakeIntent);
        }
        
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        // 작업 중단 시 재스케줄링 허용
        return true;
    }
}

매니페스트 선언

<manifest>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    <application>
        <service
            android:name=".TraceWorker"
            android:exported="false"
            android:foregroundServiceType="dataSync" />
            
        <service
            android:name=".RescheduleExecutor"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true" />
            
        <receiver
            android:name=".BootCompletionTrigger"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

실무 적용 시 고려사항

배터리 최적화 예외 처리

Android 6.0 이상에서는 dozeModeappStandby로 인해 예약 작업이 지연될 수 있다. 사용자에게 배터리 최적화 해제를 안내하거나, setAndAllowWhileIdle() 등의 대안을 고려해야 한다.

제조사별 커스텀 정책 대응

삼성, 화웨이, 샤오미 등 주요 제조사는 자체 백그라운드 제한 메커니즘을 운영한다. Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS Intent를 활용한 사용자 안내와 함께, 제조사별 가이드 문서를 참고하여 예외 목록 등록을 권장한다.

Android 12 이상의 제약

Android 12(API 31)부터는 targetSdkVersion에 따라 백그라운드 시작 제한이 강화되었다. 정확한 알람(setExact) 사용 시 SCHEDULE_EXACT_ALARM 권한 선언이 필요하며, WorkManager로의 마이그레이션을 검토할 수 있다.

대안적 접근: WorkManager 활용

Google은 복잡한 백그라운드 요구사항에 대해 WorkManager를 권장한다. 내부적으로 JobScheduler, AlarmManager, BroadcastReceiver를 조건별로 선택 사용하므로 호환성이 우수하다.

PeriodicWorkRequest keepAliveWork = 
    new PeriodicWorkRequest.Builder(
        KeepAliveWorker.class,
        15, TimeUnit.MINUTES
    )
    .setConstraints(
        new Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
    )
    .build();

WorkManager.getInstance(context)
    .enqueueUniquePeriodicWork(
        "persistentSync",
        ExistingPeriodicWorkPolicy.KEEP,
        keepAliveWork
    );

디버깅 및 모니터링

ADB를 통한 실행 상태 확인 명령어는 다음과 같다.

# 등록된 Job 목록 확인
adb shell dumpsys jobscheduler

# 특정 패키지의 백그라운드 제한 상태 확인
adb shell appops get com.example.package

실제 운영 환경에서는 Firebase Crashlytics나 커스텀 로깅 파이프라인을 구축하여 서비스 중단 이력을 추적하는 것이 바람직하다.

태그: Android JobScheduler Service Background-Execution WorkManager

7월 14일 06:19에 게시됨