Flutter AnimationController回调原理,2024年最新春招面试视频

@protected

void ensureFrameCallbacksRegistered() {

window.onBeginFrame ??= _handleBeginFrame;

window.onDrawFrame ??= _handleDrawFrame;

}

赋给了window.onBeginFram方法

/// Called by the engine to prepare the framework to produce a new frame.

///

/// This function calls all the transient frame callbacks registered by

/// [scheduleFrameCallback]. It then returns, any scheduled microtasks are run

/// (e.g. handlers for any [Future]s resolved by transient frame callbacks),

/// and [handleDrawFrame] is called to continue the frame.

///

/// If the given time stamp is null, the time stamp from the last frame is

/// reused.

///

/// To have a banner shown at the start of every frame in debug mode, set

/// [debugPrintBeginFrameBanner] to true. The banner will be printed to the

/// console using [debugPrint] and will contain the frame number (which

/// increments by one for each frame), and the time stamp of the frame. If the

/// given time stamp was null, then the string “warm-up frame” is shown

/// instead of the time stamp. This allows frames eagerly pushed by the

/// framework to be distinguished from those requested by the engine in

/// response to the “Vsync” signal from the operating system.

///

/// You can also show a banner at the end of every frame by setting

/// [debugPrintEndFrameBanner] to true. This allows you to distinguish log

/// statements printed during a frame from those printed between frames (e.g.

/// in response to events or timers).

void handleBeginFrame(Duration rawTimeStamp) {

Timeline.startSync(‘Frame’, arguments: timelineWhitelistArguments);

_firstRawTimeStampInEpoch ??= rawTimeStamp;

_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);

if (rawTimeStamp != null)

_lastRawTimeStamp = rawTimeStamp;

_hasScheduledFrame = false;

try {

// TRANSIENT FRAME CALLBACKS

Timeline.startSync(‘Animate’, arguments: timelineWhitelistArguments);

_schedulerPhase = SchedulerPhase.transientCallbacks;

final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;

//调用callBack******

_transientCallbacks = <int, _FrameCallbackEntry>{};

callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {

if (!_removedIds.contains(id))

_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);

//**************************callback(timeStamp);

});

_removedIds.clear();

} finally {

_schedulerPhase = SchedulerPhase.midFrameMicrotasks;

}

}

  • 当框架准备构建一帧的时候由engine调用,可以猜想endgine调用的实际上是window的onBegainFrame

  • 它会回调[scheduleFrameCallback]中的所有方法(包括一些future的方法),当执行完成后,系统会调用[handleDrawFrame]

/// Signature for frame-related callbacks from the scheduler.

///

/// The timeStamp is the number of milliseconds since the beginning of the

/// scheduler’s epoch. Use timeStamp to determine how far to advance animation

/// timelines so that all the animations in the system are synchronized to a

/// common time base.

typedef FrameCallback = void Function(Duration timeStamp);

callback(timeStamp);

  • “时间戳”是自scheduler开始以来的毫秒数。使用时间戳来确定动画时间线要前进多远,以便系统中的所有动画都同步到一通用的时间线上。

**总结:**当每次系统绘制的之前,会回调到ui.windows的onBegainFrame,而这个onBegainFrame执行的是handleBeginFrame,将时间值回调给了每一个callback。这里注意的是,是在在绘制之前,因为我们一般在绘制之前去通过改变控件的属性值完成动画,而这个动作必须在绘制前完成。

Ticker

反向搜索谁调用了scheduleFrameCallback,发现是在Ticker中的scheduleTick,而scheduleTick有几个地方调用后面来看

/// Schedules a tick for the next frame.

///

/// This should only be called if [shouldScheduleTick] is true.

@protected

void scheduleTick({ bool rescheduling = false }) {

assert(!scheduled);

assert(shouldScheduleTick);

_animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);

}

这里发现ticker传入了一个_tick对象到scheduleFrameCallback中

void _tick(Duration timeStamp) {

assert(isTicking);

assert(scheduled);

_animationId = null;

_startTime ??= timeStamp;

_onTick(timeStamp - _startTime);

// The onTick callback may have scheduled another tick already, for

// example by calling stop then start again.

if (shouldScheduleTick)

scheduleTick(rescheduling: true);

}

final TickerCallback _onTick;

Ticker(this._onTick, { this.debugLabel }) {

assert(() {

_debugCreationStack = StackTrace.current;

return true;

}());

}

这里发现,_ticke方法,将时间戳,转换为一个相对时间,回调的到onTick中。onTicker是Ticker构造时候创建的。而Ticker的创建其实主要只在TickProvider中

@override

Ticker createTicker(TickerCallback onTick) {

_ticker = Ticker(onTick, debugLabel: kDebugMode ? ‘created by $this’ : null);

return _ticker;

}

那谁调用了这个createTicker呢,其实自然能联想到是AnimationController

AnimationController({

double value,

this.duration,

this.reverseDuration,

this.debugLabel,

this.lowerBound = 0.0,

this.upperBound = 1.0,

this.animationBehavior = AnimationBehavior.normal,

@required TickerProvider vsync,

}) : assert(lowerBound != null),

assert(upperBound != null),

assert(upperBound >= lowerBound),

assert(vsync != null),

_direction = _AnimationDirection.forward {

_ticker = vsync.createTicker(_tick);

_internalSetValue(value ?? lowerBound);

}

void _tick(Duration elapsed) {

_lastElapsedDuration = elapsed;

final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;

assert(elapsedInSeconds >= 0.0);

_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);

if (_simulation.isDone(elapsedInSeconds)) {

_status = (_direction == _AnimationDirection.forward) ?

AnimationStatus.completed :

AnimationStatus.dismissed;

stop(canceled: false);

}

///注意到了 notifyListeners()方法,果然AnimationController继承自Animation继承自Listenable。

notifyListeners();

_checkStatusChanged();

}

**总结:**某个方法调用了scheduleTick之后,使的Ticker里面的回调函数_tick(Duration elapsed)被添加到transientCallbacks中,之后每次帧绘制之前候通过handleBeginFrame回调到这个方法。

那谁调用了scheduleTick,通过源码搜索Ticker中的一个start()的地方调用了这个函数。

每次启动动画的时候我们一般使用animation.forward(),

TickerFuture forward({ double from }) {

_direction = _AnimationDirection.forward;

//如果不为null 表示动画有起始值

if (from != null)

value = from;

return _animateToInternal(upperBound);

}

这个方法就两个作用,一个是将动画标记为开始状态,另一个作用如果为动画设置从哪一个位置开始,默认从起始点,之后调用_animateToInternal

TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear }) {

//****************//

return _startSimulation(_InterpolationSimulation(_value, target, simulationDuration, curve, scale));

}

TickerFuture _startSimulation(Simulation simulation) {

_simulation = simulation;

_lastElapsedDuration = Duration.zero;

_value = simulation.x(0.0).clamp(lowerBound, upperBound);

// 此处调用ticker的start

final TickerFuture result = _ticker.start();

_status = (_direction == _AnimationDirection.forward) ?

AnimationStatus.forward :

AnimationStatus.reverse;

_checkStatusChanged();

return result;

}

**总结:**当调用animationController的forward的时候,最终会调到ticker的start方法,start方法调用scheduleTick开始了ticker与ScheduleBinding的绑定。整个过程是一个Windows- > ScheduleBinding -> Ticker ->AnimationController。

建立好绑定之后,每次帧绘制之前,都会回调到AnimationController的_tick方法中

void _tick(Duration elapsed) {

_lastElapsedDuration = elapsed;

final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;

assert(elapsedInSeconds >= 0.0);

_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);

if (_simulation.isDone(elapsedInSeconds)) {

_status = (_direction == _AnimationDirection.forward) ?

AnimationStatus.completed :

AnimationStatus.dismissed;

stop(canceled: false);

}

notifyListeners();

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

写在最后

本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!

最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:

对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android架构师之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
-1712785520724)]

对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android架构师之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

[外链图片转存中…(img-LrFSu0PA-1712785520725)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-jlP3K4SA-1712785520725)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值