Skip to content

Commit 256df44

Browse files
feat: Debouncing of SentryWidgetsBindingObserver.didChangeMetrics. #400 (#2232)
* feat: add debouncer for SentryWidgetsBindingObserver.didChangeMetrics * adapt tests for debouncing * add changelog entry for debouncer * Update flutter/lib/src/utils/debouncer.dart Co-authored-by: Giancarlo Buenaflor <[email protected]> * Update flutter/test/widgets_binding_observer_test.dart Co-authored-by: Giancarlo Buenaflor <[email protected]> * add internal to debouncer and add whitespaces to comments --------- Co-authored-by: Giancarlo Buenaflor <[email protected]>
1 parent 73d70bf commit 256df44

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
1212
```
1313

14+
### Improvements
15+
16+
- Debouncing of SentryWidgetsBindingObserver.didChangeMetrics with delay of 100ms. ([#2232](https://github.com/getsentry/sentry-dart/pull/2232))
17+
1418
### Dependencies
1519

1620
- Bump Android SDK from v7.13.0 to v7.14.0 ([#2228](https://github.com/getsentry/sentry-dart/pull/2228))

flutter/lib/src/utils/debouncer.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'dart:async';
2+
import 'package:flutter/foundation.dart';
3+
import 'package:meta/meta.dart';
4+
5+
@internal
6+
class Debouncer {
7+
final int milliseconds;
8+
Timer? _timer;
9+
10+
Debouncer({required this.milliseconds});
11+
12+
void run(VoidCallback action) {
13+
_timer?.cancel();
14+
_timer = Timer(Duration(milliseconds: milliseconds), action);
15+
}
16+
17+
void dispose() {
18+
_timer?.cancel();
19+
}
20+
}

flutter/lib/src/widgets_binding_observer.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:ui';
44
import 'package:flutter/foundation.dart';
55
import 'package:flutter/material.dart';
66
import '../sentry_flutter.dart';
7+
import 'utils/debouncer.dart';
78

89
/// This is a `WidgetsBindingObserver` which can observe some events of a
910
/// Flutter application.
@@ -50,6 +51,8 @@ class SentryWidgetsBindingObserver with WidgetsBindingObserver {
5051
// ignore: deprecated_member_use
5152
final StreamController<SingletonFlutterWindow?> _screenSizeStreamController;
5253

54+
final _didChangeMetricsDebouncer = Debouncer(milliseconds: 100);
55+
5356
/// This method records lifecycle events.
5457
/// It tries to mimic the behavior of ActivityBreadcrumbsIntegration of Sentry
5558
/// Android for lifecycle events.
@@ -88,9 +91,12 @@ class SentryWidgetsBindingObserver with WidgetsBindingObserver {
8891
if (!_options.enableWindowMetricBreadcrumbs) {
8992
return;
9093
}
91-
// ignore: deprecated_member_use
92-
final window = _options.bindingUtils.instance?.window;
93-
_screenSizeStreamController.add(window);
94+
95+
_didChangeMetricsDebouncer.run(() {
96+
// ignore: deprecated_member_use
97+
final window = _options.bindingUtils.instance?.window;
98+
_screenSizeStreamController.add(window);
99+
});
94100
}
95101

96102
void _onScreenSizeChanged(Map<String, dynamic> data) {

flutter/test/widgets_binding_observer_test.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ void main() {
193193
// ignore: deprecated_member_use
194194
window.physicalSizeTestValue = Size(newWidth, newHeight);
195195

196+
// waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400
197+
await tester.pump(Duration(milliseconds: 150));
198+
196199
final breadcrumb =
197200
verify(hub.addBreadcrumb(captureAny)).captured.single as Breadcrumb;
198201

@@ -230,6 +233,9 @@ void main() {
230233
// ignore: deprecated_member_use
231234
window.devicePixelRatioTestValue = newPixelRatio;
232235

236+
// waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400
237+
await tester.pump(Duration(milliseconds: 150));
238+
233239
final breadcrumb =
234240
verify(hub.addBreadcrumb(captureAny)).captured.single as Breadcrumb;
235241

@@ -265,6 +271,9 @@ void main() {
265271
// ignore: deprecated_member_use
266272
window.viewInsetsTestValue = WindowPadding.zero;
267273

274+
// waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400
275+
await tester.pump(Duration(milliseconds: 150));
276+
268277
verifyNever(hub.addBreadcrumb(captureAny));
269278

270279
instance.removeObserver(observer);
@@ -286,6 +295,9 @@ void main() {
286295

287296
window.onMetricsChanged!();
288297

298+
// waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400
299+
await tester.pump(Duration(milliseconds: 150));
300+
289301
verifyNever(hub.addBreadcrumb(captureAny));
290302

291303
instance.removeObserver(observer);
@@ -400,5 +412,70 @@ void main() {
400412

401413
instance.removeObserver(observer);
402414
});
415+
416+
testWidgets('debouncing didChangeMetrics with 100ms delay',
417+
(WidgetTester tester) async {
418+
final hub = MockHub();
419+
420+
final observer = SentryWidgetsBindingObserver(
421+
hub: hub,
422+
options: flutterTrackingEnabledOptions,
423+
);
424+
final instance = tester.binding;
425+
instance.addObserver(observer);
426+
427+
// ignore: deprecated_member_use
428+
final window = instance.window;
429+
430+
// ignore: deprecated_member_use
431+
window.physicalSizeTestValue = window.physicalSize;
432+
433+
const newPixelRatio = 1.7;
434+
// ignore: deprecated_member_use
435+
window.devicePixelRatioTestValue = newPixelRatio;
436+
437+
verifyNever(hub.addBreadcrumb(captureAny));
438+
439+
// waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400
440+
await tester.pump(Duration(milliseconds: 150));
441+
442+
verify(hub.addBreadcrumb(captureAny));
443+
444+
instance.removeObserver(observer);
445+
});
446+
447+
testWidgets('debouncing: didChangeMetrics is called only once in 100ms',
448+
(WidgetTester tester) async {
449+
final hub = MockHub();
450+
451+
final observer = SentryWidgetsBindingObserver(
452+
hub: hub,
453+
options: flutterTrackingEnabledOptions,
454+
);
455+
final instance = tester.binding;
456+
instance.addObserver(observer);
457+
458+
// ignore: deprecated_member_use
459+
final window = instance.window;
460+
461+
// ignore: deprecated_member_use
462+
window.physicalSizeTestValue = window.physicalSize;
463+
464+
// ignore: deprecated_member_use
465+
window.devicePixelRatioTestValue = 2.1;
466+
// ignore: deprecated_member_use
467+
window.devicePixelRatioTestValue = 2.2;
468+
// ignore: deprecated_member_use
469+
window.devicePixelRatioTestValue = 2.3;
470+
471+
verifyNever(hub.addBreadcrumb(captureAny));
472+
473+
// waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400
474+
await tester.pump(Duration(milliseconds: 150));
475+
476+
verify(hub.addBreadcrumb(captureAny)).called(1);
477+
478+
instance.removeObserver(observer);
479+
});
403480
});
404481
}

0 commit comments

Comments
 (0)