티스토리 뷰

최근 몇 년 간, 안드로이드의 백그라운드 작업에 변화가 있었고, API 레벨23 부터 Doze 모드가 추가되었다. 묵시적(implicit) 인텐트가 제한되었고, 백그라운드 동작에도 제한이 생겼다.

 

대부분의 새로운 솔루션은 오래된 안드로이드 버전에서는 동작하지 않을 것이고, 이전의 솔루션들은 새로운 안드로이드 버전에서 동작하지 않을 것이다.

 

이 글에서는 백그라운드에서 블루투스를 스캔하기 위한 두 개의 전략을 알아볼 것이다. API 26 이전과 이후의 버전 말이다.

 

API 레벨 26(오레오) 이전 버전에서의 백그라운드 작업

  • 알람 매니저, Broadcast Receiver와 함께 서비스를 사용
  • 브로드캐스트 리시버는 알람을 깨워 서비스를 시작하게 함
  • API 레벨 26 이상부터는 앱이 종료된 상태에서 서비스를 시작하도록 허용되지 않는다.

오레오 이후 버전에서의 백그라운드 작업

  • 오레오 부터는 앱이 죽었을 때 서비스를 시작하지 못하게 막는다.
  • 몇 가지 솔루션이 제공되지만, 문제는 도즈 모드에서는 이 솔루션도 사용할 수 없다는 것이다.
  • (사용자가 디바이스를 전원이 공급되지 않고 화면을 끈 상태로 일정 시간 놔두면 기기는 도즈모드로 진입한다.)
  • 도즈 모드에서 적용되는 제한 사항은 다음과 같다. (원문 링크)
    • 네트워크 접근이 지연됨
    • 시스템이 wake lock을 무시함
    • 표준 AlarmManager 알람이 다음 유지보수 기간으로 연기됨
    • 시스템에서 Wi-Fi 검색을 실행하지 않음
    • 시스템에서 동기화 어댑터 실행을 허용하지 않음
    • 시스템에서 JobScheduler 실행을 허용하지 않음
    •  

주기적 Job Scheduler

  • 잡 스케줄러는 API 23부터 서용할 수 있으며 Service와 Alarm Manager의 조합을 대체했다.
  • 주기적 잡스케줄러는 최소 15분을 주기로 깨울 수 있으며, 여러 많은 조건들을 적용시킬 수 있다.
  • 이 방법은 기기가 도즈 모드에 진입하지 않는 동안에는 완벽하게 동작한다.

.setAndAllowWhileIdle() 함수와 JobScheduler와 함께 Alarm 사용하기

  • 일반적인 Alarm Manager는 도즈 모드가 아닌 경우 .set() 혹은 setRepeating() 으로 잘 동작할 것이다.

불행히도 위의 어떤 방법도 도즈 모드에서는 실행이 안된다. WorkManager도 안된다. 그러나 대체 가능한  가지 방법이 있다.

 

Foreground 서비스

  • Foreground 서비스는 작업을 취소하지도 도즈모드에 진입하지도 않는다. 따라서 백그라운드 작업을 무한히 실행할 수 있다.
  • 그러나 이 해결책은 한 가지 단점이 있는데, 기기의 상태 표시줄에 항상 알림(notification)이 표시된다는 것이다. 
  • 사용자는 항상 이 알림을 봐야하고 취소할 수도 없다.
  • 이 방법을 사용하면 사용자가 앱을 삭제할 가능성을 높여준다.
  • 또한 이 방법은 기기의 전력과 자원을 더 많이 사용한다.
  • 아래는 예제 코드
public class ForegroundService extends Service {

	@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String input = intent.getStringExtra("inputExtra");
        createNotificationChannel();
       
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, YourActivity.class), 0);
       
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Foreground Service")
                .setContentText(input)
                .setSmallIcon(R.drawable.ic_stat_name)
                .setContentIntent(pendingIntent)
                .build();
       
        startForeground(1, notification);
       
        // here is the code you wanna run in background
        return START_NOT_STICKY;
    }

	private void createNotificationChannel() {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID,
                "Foreground Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT);
     
               getSystemService(NotificationManager.class).createNotificationChannel(serviceChannel);
    }
}

 

푸시 Notification 사용하기 (FCM, Firebase Cloud Messaging)

  • 또 다른 방법은 서버에서 앱으로 푸시 알림을 보내는 것이다.
  • 푸시 알림은 앱이 도즈 모드에서 강제로 나가도록 한다.
  • 그러나 이 경우 더 많은 자원을 사용하게 된다.

 

요약

  • 그동안 구글은 백그라운드 작업을 어렵게 만들었다. 그래서 개발자들은 새로운 해결책을 찾아야만 하게 되었다.
  • 안드로이드 입장에서 보면 구글의 이러한 정책은 사용자가 원하지 않는 상황에 백그라운드에서 어떤 작업이 실행되어 기기의 자원을 사용하는 상황으로부터 사용자를 보호하기 위함이다.
  • Foreground 서비스를 통한 방법을 제외하면 사실상 꼼수라고 할 수 있다.
  • 동기화가 필요한 앱들을 보면 Foreground 서비스를 통해 어떤 작업을 하는 것을 알 수 있는데, 이 방법이 가장 최선이 아닐까하는 생각이 든다. 
  • 그러나 잘못하면 상태바에서 알림이 사라지지 않는 불상사가 일어날 수 있으니 조심해야 한다.

 

참고

댓글