Skip to content

Commit c5c63df

Browse files
authored
Don't flush touch events during first launch frame pre-warm (flutter#13990)
* Lock events during runApp's warm up frame * move to scheduler binding * let the one scheduleWarmUpFrame api always lock * tweak test
1 parent 8a6e973 commit c5c63df

File tree

3 files changed

+24
-6
lines changed

3 files changed

+24
-6
lines changed

packages/flutter/lib/src/foundation/binding.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,21 +161,25 @@ abstract class BindingBase {
161161
/// callback's future completes.
162162
///
163163
/// This causes input lag and should therefore be avoided when possible. It is
164-
/// primarily intended for development features, in particular to allow
165-
/// [reassembleApplication] to block input while it walks the tree (which it
166-
/// partially does asynchronously).
164+
/// primarily intended for use during non-user-interactive time such as to
165+
/// allow [reassembleApplication] to block input while it walks the tree
166+
/// (which it partially does asynchronously).
167167
///
168168
/// The [Future] returned by the `callback` argument is returned by [lockEvents].
169169
@protected
170170
Future<Null> lockEvents(Future<Null> callback()) {
171+
developer.Timeline.startSync('Lock events');
172+
171173
assert(callback != null);
172174
_lockCount += 1;
173175
final Future<Null> future = callback();
174176
assert(future != null, 'The lockEvents() callback returned null; it should return a Future<Null> that completes when the lock is to expire.');
175177
future.whenComplete(() {
176178
_lockCount -= 1;
177-
if (!locked)
179+
if (!locked) {
180+
developer.Timeline.finishSync();
178181
unlocked();
182+
}
179183
});
180184
return future;
181185
}

packages/flutter/lib/src/scheduler/binding.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,8 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
666666
/// This is used during application startup so that the first frame (which is
667667
/// likely to be quite expensive) gets a few extra milliseconds to run.
668668
///
669+
/// Locks events dispatching until the scheduled frame has completed.
670+
///
669671
/// If a frame has already been scheduled with [scheduleFrame] or
670672
/// [scheduleForcedFrame], this call may delay that frame.
671673
///
@@ -677,8 +679,9 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
677679
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
678680
return;
679681

680-
final bool hadScheduledFrame = _hasScheduledFrame;
681682
_warmUpFrame = true;
683+
Timeline.startSync('Warm-up frame');
684+
final bool hadScheduledFrame = _hasScheduledFrame;
682685
// We use timers here to ensure that microtasks flush in between.
683686
Timer.run(() {
684687
assert(_warmUpFrame);
@@ -700,6 +703,13 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
700703
if (hadScheduledFrame)
701704
scheduleFrame();
702705
});
706+
707+
// Lock events so touch events etc don't insert themselves until the
708+
// scheduled frame has finished.
709+
lockEvents(() async {
710+
await endOfFrame;
711+
Timeline.finishSync();
712+
});
703713
}
704714

705715
Duration _firstRawTimeStampInEpoch;

packages/flutter/test/scheduler/scheduler_test.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,13 @@ void main() {
9292

9393
test('2 calls to scheduleWarmUpFrame just schedules it once', () {
9494
final List<VoidCallback> timerQueueTasks = <VoidCallback>[];
95+
bool taskExecuted = false;
9596
runZoned(
9697
() {
9798
// Run it twice without processing the queued tasks.
9899
scheduler.scheduleWarmUpFrame();
99100
scheduler.scheduleWarmUpFrame();
101+
scheduler.scheduleTask(() { taskExecuted = true; }, Priority.touch);
100102
},
101103
zoneSpecification: new ZoneSpecification(
102104
createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()) {
@@ -107,7 +109,9 @@ void main() {
107109
),
108110
);
109111

110-
// A single call to scheduleWarmUpFrame queues up 2 Timer tasks.
112+
// scheduleWarmUpFrame scheduled 2 Timers, scheduleTask scheduled 0 because
113+
// events are locked.
111114
expect(timerQueueTasks.length, 2);
115+
expect(taskExecuted, false);
112116
});
113117
}

0 commit comments

Comments
 (0)