Flutter/Flutter

Flutter - flutter_foreground_task (1)

hyeonseong 2024. 5. 14. 10:19

Flutter앱에서 포그라운드 서비스로 어떤 주기적인 작업이 필요했고, 서비스가 실행중일 때 앱이 종료 되는 현상을 막을 필요가 있었다.

https://pub.dev/packages/flutter_foreground_task 라이브러리가 그런 역할을 해준다.

 

Foreground와 Main Thread 와 통신은 다음에서 https://seong9566.tistory.com/394

Main 메모리와 Foreground에서 사용하는 메모리는 다르기 때문에 
Main 메모리에서 사용한 데이터를 Foreground에서 사용하는 메모리로 전달하기 위해선 별도의 방식이 필요하다 
isolate방식과 유사하다.
전송 데이터는 기본 문자열(int,String,bool),컬렉션 (List,Set..) 등의 방식을 추천하고
사용자가 만든 객체는 Json으로 전달하는 방식 사용하길 권장한다.

사용 방법

"AndroidManifest.xml" 의  <application> </application> 안에 코드 추가 

  <application
       ...(기타 셋팅)
       
        <service
            android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
            android:foregroundServiceType="connectedDevice" //Android 14에서 추가됨.
            android:stopWithTask="true"/>
            
 </application>

 

Handler클래스를 따로 생성해 사용 함수들을 작성해주었다. ( init, start 등등 )

import 'dart:isolate';

import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';

class ForegroundTaskHandler extends TaskHandler {
  SendPort? _sendPort;
  
  void initForegroundTask() {
    print("ForegroundTaskHandler initForegroundTask");
    FlutterForegroundTask.init(
      androidNotificationOptions: AndroidNotificationOptions(
        foregroundServiceType: AndroidForegroundServiceType.NONE,
        /**
         * VISIBILITY_PUBLIC : 알림의 제목/내용이 잠금 화면에서도 보임
         * VISIBILITY_PRIVATE : 알림이 잠금화면에서 제목만 보임
         * VISIBILITY_SECRET : 알림이 잠금 화면에서는 보이지 않음
         */
        visibility: NotificationVisibility.VISIBILITY_SECRET,
        showWhen: false, // Time Stamp
        channelId: 'Foreground Notification',
        channelName: 'Foreground Notification',
        channelDescription:
            'This notification appears when the foreground service is running.',

        // OS의 noti bar에 지워지지 않는 알림이 생기는데 안보이게 하기 위해서 NONE설정
        channelImportance: NotificationChannelImportance.NONE,
        priority: NotificationPriority.LOW,

        /// 서비스가 중단 되었을 때 재시작 여부 설정
        isSticky: false,
      ),
      iosNotificationOptions: const IOSNotificationOptions(
        showNotification: false,
        playSound: false,
      ),
      foregroundTaskOptions: const ForegroundTaskOptions(
      // 10분
        interval: 600000, // 포그라운드 호출 주기 설정 , 앱이 백그라운드 상태 일 때도 호출 하기 위함.
        autoRunOnBoot: false, // 앱 실행시 자동 포그라운드 작업 자동 실행 여부
        allowWakeLock: false, // CPU를 계속 켜둘지 여부(절전모드)
        allowWifiLock: false, // true
      ),
    );
  }

  /// background작업
  @override
  void onStart(DateTime timestamp, SendPort? sendPort) async {}

  // Called every [interval] milliseconds in [ForegroundTaskOptions].
  @override
  void onRepeatEvent(DateTime timestamp, SendPort? sendPort) async {
    print('onRepeatEvent >> $devs');
  }

  @override
  void onDestroy(DateTime timestamp, SendPort? sendPort) async {}

  @override
  void onNotificationButtonPressed(String id) {
    print('onNotificationButtonPressed >> $id');
  }

  // "android.permission.SYSTEM_ALERT_WINDOW" permission must be granted for
  // this function to be called.
  /**
   * 알람 눌렀을때 호출 되는 함수
   * 앱 상태를 resume로 변경
   */
  @override
  void onNotificationPressed() {
    FlutterForegroundTask.launchApp("/resume-route");
    _sendPort?.send('onNotificationPressed');
  }
}

 

background에서 동작을 위해 함수 선언 및 Handler초기화 

- main함수 가장 위에 선언 해주면 된다.

// 함수가 우선 동작을 해야함을 알림
@pragma('vm:entry-point')
void startForegroundCallback() {
  FlutterForegroundTask.setTaskHandler(ForegroundTaskHandler());
}

 

앱이 paused상태에서 start, resumed상태에선 stop

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    switch (state) {
      case AppLifecycleState.resumed:
        FlutterForegroundTask.stopService();
        break;
      case AppLifecycleState.inactive:
        break;
      case AppLifecycleState.paused:
        try {
          print("foreground task start");
          FlutterForegroundTask.startService(
              notificationTitle: '',
              notificationText: '',
              callback: startForegroundCallback);
        } catch (e) {
          print(e);
        }
        break;
      case AppLifecycleState.detached:
        break;
      case AppLifecycleState.hidden:
        break;
    }
  }