Don't flush touch events during first launch frame pre-warm (#13990)
* Lock events during runApp's warm up frame * move to scheduler binding * let the one scheduleWarmUpFrame api always lock * tweak test
This commit is contained in:
parent
8a6e973739
commit
c5c63dfd47
@ -161,21 +161,25 @@ abstract class BindingBase {
|
||||
/// callback's future completes.
|
||||
///
|
||||
/// This causes input lag and should therefore be avoided when possible. It is
|
||||
/// primarily intended for development features, in particular to allow
|
||||
/// [reassembleApplication] to block input while it walks the tree (which it
|
||||
/// partially does asynchronously).
|
||||
/// primarily intended for use during non-user-interactive time such as to
|
||||
/// allow [reassembleApplication] to block input while it walks the tree
|
||||
/// (which it partially does asynchronously).
|
||||
///
|
||||
/// The [Future] returned by the `callback` argument is returned by [lockEvents].
|
||||
@protected
|
||||
Future<Null> lockEvents(Future<Null> callback()) {
|
||||
developer.Timeline.startSync('Lock events');
|
||||
|
||||
assert(callback != null);
|
||||
_lockCount += 1;
|
||||
final Future<Null> future = callback();
|
||||
assert(future != null, 'The lockEvents() callback returned null; it should return a Future<Null> that completes when the lock is to expire.');
|
||||
future.whenComplete(() {
|
||||
_lockCount -= 1;
|
||||
if (!locked)
|
||||
if (!locked) {
|
||||
developer.Timeline.finishSync();
|
||||
unlocked();
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
@ -666,6 +666,8 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
|
||||
/// This is used during application startup so that the first frame (which is
|
||||
/// likely to be quite expensive) gets a few extra milliseconds to run.
|
||||
///
|
||||
/// Locks events dispatching until the scheduled frame has completed.
|
||||
///
|
||||
/// If a frame has already been scheduled with [scheduleFrame] or
|
||||
/// [scheduleForcedFrame], this call may delay that frame.
|
||||
///
|
||||
@ -677,8 +679,9 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
|
||||
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
|
||||
return;
|
||||
|
||||
final bool hadScheduledFrame = _hasScheduledFrame;
|
||||
_warmUpFrame = true;
|
||||
Timeline.startSync('Warm-up frame');
|
||||
final bool hadScheduledFrame = _hasScheduledFrame;
|
||||
// We use timers here to ensure that microtasks flush in between.
|
||||
Timer.run(() {
|
||||
assert(_warmUpFrame);
|
||||
@ -700,6 +703,13 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
|
||||
if (hadScheduledFrame)
|
||||
scheduleFrame();
|
||||
});
|
||||
|
||||
// Lock events so touch events etc don't insert themselves until the
|
||||
// scheduled frame has finished.
|
||||
lockEvents(() async {
|
||||
await endOfFrame;
|
||||
Timeline.finishSync();
|
||||
});
|
||||
}
|
||||
|
||||
Duration _firstRawTimeStampInEpoch;
|
||||
|
@ -92,11 +92,13 @@ void main() {
|
||||
|
||||
test('2 calls to scheduleWarmUpFrame just schedules it once', () {
|
||||
final List<VoidCallback> timerQueueTasks = <VoidCallback>[];
|
||||
bool taskExecuted = false;
|
||||
runZoned(
|
||||
() {
|
||||
// Run it twice without processing the queued tasks.
|
||||
scheduler.scheduleWarmUpFrame();
|
||||
scheduler.scheduleWarmUpFrame();
|
||||
scheduler.scheduleTask(() { taskExecuted = true; }, Priority.touch);
|
||||
},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()) {
|
||||
@ -107,7 +109,9 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
// A single call to scheduleWarmUpFrame queues up 2 Timer tasks.
|
||||
// scheduleWarmUpFrame scheduled 2 Timers, scheduleTask scheduled 0 because
|
||||
// events are locked.
|
||||
expect(timerQueueTasks.length, 2);
|
||||
expect(taskExecuted, false);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user