AndroidStudio(WearOS)로 Interval Timer 만들기 : 타이머 백그라운드 실행 구현

LeeYulhee·2024년 4월 15일

👉 문제 원인 및 해결 방안


  • 문제 1 : 타이머 실행 중 화면이 꺼지면 알림이 울리지 않음
    • 백그라운드 실행을 적용 해야 정상적으로 작동
    • ⇒ 백그라운드로 실행하기 위해서는 Foreground Service를 적용 해야 함

  • 문제 2 : 백그라운드 적용 후에 PAUSE와 RESTART 제대로 작동하지 않음
    • 기존에는 모든 로직을 Activity에 적용했는데 Service와 Activity로 분리
    • Activity의 RESTART 메서드에서 Service를 호출할 때, intervalTime을 전달해주지 않아 intervalTime이 0으로 전달됨
      • 위의 이유 때문에 RESTART 시, 남은 SET들의 intervalTime이 0으로 호출되어 정상적으로 작동되지 않음
    • Activity의 RESTART 메서드에서 Service를 호출할 때, currentSet에 executionSet(최초에 지정한 SET)이 대입되어 SET가 이어서 실행되지 않음
    • ⇒ Activity의 RESTART 메서드에서 Service를 호출할 때, currentSet과 intervalTime 전달해야 함



👉 수정한 코드


  • TimerActivity에서 onCreate 시, Service 호출
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_timer);

    textViewTimer = findViewById(R.id.textViewTimer);
    pauseButton = findViewById(R.id.pauseButton);

    selectedOption = getIntent().getIntExtra("selectedOption", 0);
    intervalTime = getIntent().getIntExtra("intervalTime", 0);
    executionSet = getIntent().getIntExtra("executionSet", 0);

    pauseButton.setOnClickListener(view -> {
        if (isPaused) {
            sendControlCommand("ACTION_RESTART");
        } else {
            sendControlCommand("ACTION_PAUSE");
        }
    });

    startTimerService();
}

  • TimerActivity의 sendControlCommand에서 intervalTime과 currentSet도 Service로 전달
private void sendControlCommand(String action) {
    Intent serviceIntent = new Intent(this, TimerService.class);
    serviceIntent.setAction(action);
    serviceIntent.putExtra("currentSet", currentSet);
    serviceIntent.putExtra("isPaused", isPaused);
    serviceIntent.putExtra("isSendControlCommandCall", true);
    serviceIntent.putExtra("intervalTime", intervalTime);
    startService(serviceIntent);
}

  • TimerService의 로직 일부
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    intervalTime = intent.getIntExtra("intervalTime", 0);
    selectedOption = intent.getIntExtra("selectedOption", 0);
    boolean isSendControlCommandCall = intent.getBooleanExtra("isSendControlCommandCall", false);

    if ( isSendControlCommandCall ) {
        currentSet = intent.getIntExtra("currentSet", executionSet);
    } else {
        executionSet = intent.getIntExtra("executionSet", 0);
        currentSet = executionSet;
    }

    startForeground(1, getNotification("타이머 실행 중"));

    String action = intent.getAction();
    if ("ACTION_PAUSE".equals(action)) {
        pauseTimer();
    } else if ("ACTION_RESTART".equals(action)) {
        restartTimer();
    } else {
        startTimer();
    }

    return START_STICKY;
}

private void startTimer() {
    timeLeftInMillis = intervalTime * 1000L;
    Log.d("onFinish", "startTimer의 timeLeftMillis : " + timeLeftInMillis);
    setupAndStartTimer();
}

private void pauseTimer() {
    if (countDownTimer != null) {
        countDownTimer.cancel();
        isPaused = true;
        broadcastTimerUpdate(timeLeftInMillis); // 일시 정지 상태 방송
    }
}

private void restartTimer() {
    if (isPaused) {
        isPaused = false;
        broadcastTimerUpdate(timeLeftInMillis);
        setupAndStartTimer();
    }
}

private void broadcastTimerUpdate(long millisUntilFinished) {
    Intent intent = new Intent(ACTION_UPDATE_BROADCAST);
    intent.putExtra("timeLeftInMillis", millisUntilFinished);
    intent.putExtra("currentSet", currentSet); // 현재 currentSet을 Activity에서 받아오기 위해 넘김
    intent.putExtra("isPaused", isPaused);
    sendBroadcast(intent);
}

private Notification getNotification(String contentText) {
    Intent notificationIntent = new Intent(this, TimerActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,
            0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);

    return new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("InTime")
            .setContentText(contentText)
            .setSmallIcon(R.drawable.in_logo)
            .setContentIntent(pendingIntent)
            .setOngoing(true)
            .build();
}
profile
끝없이 성장하고자 하는 백엔드 개발자입니다.

0개의 댓글