Firebase unable to reconnect after exiting Doze Mode

Viewed 1130

Overview

I'm using Flutter with Firebase. After the phone/app enters Doze Mode, Firebase needs a few minutes (too long!) to reconnect. It took me weeks to find how to reproduce this issue.

Steps to reproduce:

  1. Open a Flutter app and select a screen where you can call Firebase with a click of a button

  2. Enter Androids' Doze Mode (Lock the screen, run adb shell dumpsys battery unplug, and then run adb shell dumpsys deviceidle force-idle)

  3. After a few seconds/minutes, under the "Run" tab, the following will appear:

    Stream closed with status: Status{code=UNAVAILABLE, description=Keepalive failed. The connection is likely gone, cause=null}. and [{0}] Failed to resolve name. status={1}

  4. Now, unlock your phone and try to make a Firebase query

For me, the following error appears 8 out of 10 times. The connection will re-establish after hitting the "Retry" button, but only after a few minutes which is, of course, too late.

W/XXX.XXXXXXX(14214): Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed) W/XXX.XXXXXXX(14214): Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed) W/Firestore(14214): (22.0.1) [WatchStream]: (72436f2) Stream closed with status: Status{code=UNAVAILABLE, description=Unable to resolve host firestore.googleapis.com, cause=java.lang.RuntimeException: java.net.UnknownHostException: Unable to resolve host "firestore.googleapis.com": No address associated with hostname W/Firestore(14214): at io.grpc.internal.DnsNameResolver.resolveAll(DnsNameResolver.java:436) W/Firestore(14214): at io.grpc.internal.DnsNameResolver$Resolve.resolveInternal(DnsNameResolver.java:272) W/Firestore(14214): at io.grpc.internal.DnsNameResolver$Resolve.run(DnsNameResolver.java:228) W/Firestore(14214): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) W/Firestore(14214): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) W/Firestore(14214): at java.lang.Thread.run(Thread.java:919) W/Firestore(14214): Caused by: java.net.UnknownHostException: Unable to resolve host "firestore.googleapis.com": No address associated with hostname W/Firestore(14214): at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:156) W/Firestore(14214): at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103) W/Firestore(14214): at java.net.InetAddress.getAllByName(InetAddress.java:1152) W/Firestore(14214): at io.grpc.internal.DnsNameResolver$JdkAddressResolver.resolveAddress(DnsNameResolver.java:646) W/Firestore(14214): at io.grpc.internal.DnsNameResolver.resolveAll(DnsNameResolver.java:404) W/Firestore(14214): ... 5 more W/Firestore(14214): Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname) W/Firestore(14214): at libcore.io.Linux.android_getaddrinfo(Native Method) W/Firestore(14214): at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:74) W/Firestore(14214): at libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:200) W/Firestore(14214): at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:74) W/Firestore(14214): at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:135) W/Firestore(14214): ... 9 more W/Firestore(14214): }. W/XXX.XXXXXXX(14214): Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed) I/flutter (14214): MyDatabase | getDatabaselData | [cloud_firestore/unavailable] The service is currently unavailable. This is a most likely a transient condition and may be corrected by retrying with a backoff.

The caught exception is: MyDatabase | getDatabaselData | [cloud_firestore/unavailable] The service is currently unavailable.

Pubspec.yaml

cloud_firestore: ^0.14.1+3
firebase_storage: ^4.0.1
firebase_core: ^0.5.0+1
firebase_auth: ^0.18.3
2 Answers

Edit 2: At the moment of writing this edit, there is a commit pending on FlutterFire git (but not yet published). So the solution will be to update to the latest Firebase_Core. It is also possible to fix it manually in Firebase_Core package > gradle.properties > change FirebaseSDKVersion=28.0.1 to FirebaseSDKVersion=28.1.0.

Edit: The issue still persists if the phone enters the state naturally (6 hours of complete inactivity). It seems this issue must be fixed inside FirebaseSDK and so far it hasn't been: https://github.com/FirebaseExtended/flutterfire/issues/4305

Old answer:

I see many people strugling with this, and I have finally found the solution.

It seems Firebase is mostly wasting time on the previous (dis)connection rather than the new connection. So what I did, is disconnecting Firebase whenever the app goes to the background, and reconnecting it upon resuming. In this case, a new connection is established within seconds.

Here is the code; create a life cycle manager class (observer) and wrap it around your MaterialApp. Try-Catch is probably not necessary, but I will be pushing to production tomorrow so it's just a precaution.

lifecycle_manager.dart

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class LifecycleManager extends StatefulWidget {
  final Widget child;
  LifecycleManager({Key key, this.child}) : super(key: key);
  @override
  _LifecycleManagerState createState() => _LifecycleManagerState();
}

class _LifecycleManagerState extends State<LifecycleManager> with WidgetsBindingObserver {
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    try {
      if (state != AppLifecycleState.resumed) FirebaseFirestore.instance.disableNetwork();
      else FirebaseFirestore.instance.enableNetwork();
    } catch (error) {
      print('LifecycleManager | didChangeAppLifecycleState | ' + error.toString());
    }
  }
}

main.dart

import 'lifecycle_manager.dart';
LifecycleManager(child: MaterialApp());

(I have also updated all my dependencies, but that didn't solve the problem)

  • Delete the application from the emulator.
  • Install the application.
  • Delete collection from firebase.
  • Run the code again.

This solved my problem without any changes to my versions.

Pubspec.yaml

  version: 1.0.0+1

  environment:
    sdk: ">=2.7.0 <3.0.0"

  dependencies:
    flutter:
      sdk: flutter

    cupertino_icons: ^1.0.2
    get_it: ^1.0.3+2
    provider: ^5.0.0
    cloud_firestore: ^0.12.9+4
    firebase_auth: 0.14.0+5
  

Usage

class MainPage extends StatefulWidget {
  const MainPage({Key key}) : super(key: key);

  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  final Firestore _firestore = Firestore.instance;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Page"),
      ),
      body: Center(
        child: Column(
          children: [
            RaisedButton(
              child: Text("Add Data"),
              color: Colors.deepPurple,
              onPressed: _addData,
            )
          ],
        ),
      ),
    );
  }
  void _addData() {
    Map<String, dynamic> addUser = Map();
    addUser["name"] = "name1";
    addUser["surname"] = "surname1";
    _firestore
        .collection("users")
        .document("nameandsurname")
        .setData(addUser)
        .then((value) => debugPrint("user added"));
  }
}
Related