[Flutter] Firebase_messaging 백그라운드 메세지

zunu·2021년 3월 26일
0

앱이 백그라운드에 있을 때 FCM을 통해 메세지가 제대로 받아지지 않는 현상을 발견했다.

앱을 켜놓은 상태에서는 onMessage를 통해 정상적으로 메세지를 받아왔지만 백그라운드에서 돌아가고 있을 때는 onMessage를 통해 메세지가 받아지지 않았다.

확인해보니 IOS의 경우 onResume을 통해 백그라운드에서의 메세지를 받아오는데, 유독 안드로이드만 백그라운드 메세지를 받아오지 못했다.

그리고 찾아보니 onBackgroundMessage라는 것을 통해 핸들러를 지정해두면 핸들러가 백그라운드에서 메세지를 받아온다고 한다.

그래서 기쁜 마음에 아래와 같이 간단히 핸들러를 만들어 주어 실행을 해보았다.

Future<void> onBackgroundHandler(Map<String, dynamic> message) async {
	logger.d("onBackgroundMessage: ${message["data"]}");
}

핸들러의 경우 TOP-LEVEL 또는 STATIC으로 선언하여 사용하라고 해서 나의 경우는 TOP-LEVEL로 선언하여 사용했다.

당연하지만 정상작동 하지 않았다.

하지만 자세히 찾아보니 android 폴더 안의 manifest 파일과 java 파일을 만들어주어야 한다는 것을 알 수 있었다.

출처 - Dart packages: firebase_messaging

모두 해주고 나니 안드로이드 기기에서도 백그라운드 작동시 메세지를 잘 받아왔고 이제 데이터만 잘 처리하면 되는 상황이었다. 정확하지는 않지만 다른 함수를 불러와 데이터를 처리했더니 아래와 같은 에러 로그가 발생했다.

I/flutter (20210): Unable to handle incoming background message.
I/flutter (20210): NoSuchMethodError: The method '함수이름' was called on null.
I/flutter (20210): Receiver: null
I/flutter (20210): Tried calling: 함수이름(데이터)

어떤 방법을 써도 이 핸들러 안에서 다른 함수를 콜백하는 것은 불가능했고 그 결과 나는 또다시 구글에서 답을 찾을 수 있었다.

방법은 생각보다 간단하다. 다른 함수를 콜백하여 사용하는 것이 안되니 로컬에 데이터를 저장하는 SharedPreferences를 사용하여 데이터를 저장해놓았다가 백그라운드에서 작동하고 있던 앱을 다시 켰을 때, 그 데이터를 불러와서 처리를 하는 방식이었다.

final prefs = await SharedPreferences.getInstance();
// SharedPreferences로 패스워드를 저장하여 앱을 다시 켤 때 체크한다.
await prefs.reload();
prefs.setString("temp", message['data']]);

이런 식으로 temp라는 키로 저장을 해두었다가

final prefs = await SharedPreferences.getInstance();
await prefs.reload();
String temp = prefs.getString("temp");
if(temp != null) {
	//데이터 처리
}
prefs.clear();

이런 식으로 데이터를 가져와서 처리 후, 데이터를 지워주었다.
이 때, 중요한 것이 있는데 앱이 백그라운드에서 포그라운드로 이동한 것을 확인할 수 있으려면 클래스에 WidgetsBindingObserver를 추가해주어 lifecycle을 확인해야한다.

class _pageState extends State<Page> with WidgetsBindingObserver{
  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);	// 처음 페이지를 만들 때 옵저버를 생성한다.
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);	// State가 dispose 될 시 옵저버도 없애준다.
    super.dispose();
  }

  @override
  Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
    final prefs = await SharedPreferences.getInstance();
    if(state == AppLifecycleState.resumed) {
      // 여기서 메세지를 받아왔는지 체크를 해봐야 됨.
      await prefs.reload();
      String temp = prefs.getString("temp");
      logger.d("DATA is ${temp}");
      if(temp != null) {
      	// 데이터 처리
        ...
      }
      prefs.clear();
    }
    super.didChangeAppLifecycleState(state);
  }


  @override
  Widget build(BuildContext context) {
  	...
    return Scaffold(
      ...
    );
  }
}

이런 식으로 하면 안드로이드에서 사용했던 생명주기를 거의 비슷하게 사용할 수 있다.
WidgetsBindingObserver에 대한 더 자세한 정보는 아래의 링크를 확인하면 된다.

출처 - Flutter SDK: WidgetsBindingObserver class

profile
개발새발

0개의 댓글