firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.72k stars 3.97k forks source link

[firebase_database]: OnDisconnect not called when app is killed on Android (iOS works fine) #13406

Closed Tom3652 closed 1 month ago

Tom3652 commented 1 month ago

Is there an existing issue for this?

Which plugins are affected?

Database

Which platforms are affected?

Android

Description

I made an issue 2 years ago to describe a specific behaviour of Firebase RTDB that is actually normal

Today, the behaviour upon internet loss is the same as before unfortunately but the real issue is that the OnDisconnect is never called on Android when the app is force killed by the user as is it the case on iOS

-> The problem with that is that we can wait undefinetely to see our OnDisconnect update, not "only" 10 mins for an internet loss

Reproducing the issue

  1. Run the sample code below
  2. See in your firebase console the test: 1 value
  3. force kill the app
  4. Wait as long as you want but see that test: 1 remains
  5. Do the same on iOS and see the expected behaviour
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const TestApp());
}

class TestApp extends StatefulWidget {
  const TestApp({super.key});

  @override
  State<TestApp> createState() => _TestAppState();
}

class _TestAppState extends State<TestApp> {

  void init() {
    final DatabaseReference reference = FirebaseDatabase.instance.ref();
    // if (kReleaseMode) {
    final connectedRef =
    FirebaseDatabase.instance.ref().child(".info/connected");
    connectedRef.onValue.listen((event) {
      final connected = event.snapshot.value as bool? ?? false;
      if (connected) {
        print("Connected.");
        reference
            .onDisconnect()
            .update({"test": ServerValue.increment(-1)});
        reference.update({"test": ServerValue.increment(1)});
      }
    });
  }

  @override
  void initState() {
    init();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

Firebase Core version

3.5.0

Flutter Version

3.24.3

Relevant Log Output

No response

Flutter dependencies

Expand Flutter dependencies snippet
```yaml Dart SDK 3.5.3 Flutter SDK 3.24.3 test_database 1.0.0+1 dependencies: - firebase_core 3.5.0 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_database 11.1.3 [firebase_core firebase_core_platform_interface firebase_database_platform_interface firebase_database_web flutter] - flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine] dev dependencies: - flutter_lints 4.0.0 [lints] - flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service] transitive dependencies: - _flutterfire_internals 1.3.43 [collection firebase_core firebase_core_platform_interface flutter meta] - async 2.11.0 [collection meta] - boolean_selector 2.1.1 [source_span string_scanner] - characters 1.3.0 - clock 1.1.1 - collection 1.18.0 - fake_async 1.3.1 [clock collection] - firebase_core_platform_interface 5.3.0 [collection flutter flutter_test meta plugin_platform_interface] - firebase_core_web 2.18.1 [firebase_core_platform_interface flutter flutter_web_plugins meta web] - firebase_database_platform_interface 0.2.5+43 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_database_web 0.2.6+1 [collection firebase_core firebase_core_web firebase_database_platform_interface flutter flutter_web_plugins] - flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math] - leak_tracker 10.0.5 [clock collection meta path vm_service] - leak_tracker_flutter_testing 3.0.5 [flutter leak_tracker leak_tracker_testing matcher meta] - leak_tracker_testing 3.0.1 [leak_tracker matcher meta] - lints 4.0.0 - matcher 0.12.16+1 [async meta stack_trace term_glyph test_api] - material_color_utilities 0.11.1 [collection] - meta 1.15.0 - path 1.9.0 - plugin_platform_interface 2.1.8 [meta] - sky_engine 0.0.99 - source_span 1.10.0 [collection path term_glyph] - stack_trace 1.11.1 [path] - stream_channel 2.1.2 [async] - string_scanner 1.2.0 [source_span] - term_glyph 1.2.1 - test_api 0.7.2 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph] - vector_math 2.1.4 - vm_service 14.2.5 - web 1.1.0 ```

Additional context and comments

No response

SelaseKay commented 1 month ago

Hi @Tom3652 , thanks for submitting this report. I tested with your sample code but was unable to reproduce your experience. It seems to work as expected on my end. What version of firebase_database are you using?

Tom3652 commented 1 month ago

Hi @SelaseKay thanks for your feedback.

I am using the 11.1.3 version of firebase_database.

May i ask you which version of Android are you using ? And which device ?

SelaseKay commented 1 month ago

I tested on Pixel 3a(Android 14) emulator.

Tom3652 commented 1 month ago

Alright i have more info on what is happening. I could reproduce it only on 1 device and not 100% of the time (my Motorola under Android 11), but it works fine on my 8 other Android physical devices.

However, in my real project the OnDisconnect is never called on Android because of another issue : The video_player package has a bug currently that if you play / pause a video and then call the native OnPause() Android method (App State = inactive), you will see the ExoPlayer crashing natively with a StackTrace but this has no impact on the Flutter app (no real crash for the user).

Since i have this crash in native code, it seems the OnDisconnect trigger is called after that "crash" because If i don't have any videos played / paused in my app or don't have any other native "crash" in the code, the OnDisconnect is triggered normally and i have my -1 counter on Android every devices.

Besides, the OnDisconnect should be something triggered server-side so i don't understand why a native crash on app state inactive would make the OnDisconnect not working : it should not be affected by anything in the local code and especially should be triggered when a crash occur to set the disconnection of the user in my opinion

To sum up, this issue could become : OnDisconnect seems to not be set server side and / or not called when a native error occur before destroying the app (terminated state). As an external viewer (without seeing the code), it seems the OnDisconnect is called at the very end of the App OnDestroy on the Android side, which may not be called properly if some other package have crashes in their OnPause or OnDestroy as well.

I will try to make a new reproductible sample code with the above information this week 🙏🏼

Tom3652 commented 1 month ago

Alright this is working now i am not able to reproduce the bad behaviour anymore despite the crash of video_player, i have tested also in my real project and it's working fine.

Thanks for your time anyway !