ブロガー関連記事一覧
Flutterアンドロイドプラットフォームのソースコードの解析
Flutterページ更新プロセスの解剖学
更新プロセス
ページを更新する必要がある場合、ここでは突破口としてsetStateメソッドを呼び出し、ページ更新のプロセスを勉強します。
flutter\lib\src\widgets\framework.dart
/// [State]
void setState(VoidCallback fn) {
final dynamic result = fn() as dynamic;
_element.markNeedsBuild();
}
最初の行は入力されたクロージャを実行し、続いてmarkNeedsBuild()メソッドを呼び出します。
/// [Element]
void markNeedsBuild() {
if (!_active)
return;
if (dirty)
return;
// 現在の要素を_dirty,再構築の必要性を示す
_dirty = true;
// 現在のエレメントをダーティエレメントリストに追加する。[WidgetsBinding.drawFrame] [buildScope]をクリックすると再構築される。
owner.scheduleBuildFor(this);
}
BuildOwnerのscheduleBuildForメソッドはここで呼び出されます。
/// [BuildOwner]
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
// _handleBuildScheduled
onBuildScheduled();
}
// ダーティエレメントリストに追加する
_dirtyElements.add(element);
element._inDirtyList = true;
}
WidgetsBindingの初期化については、「フレームワーク・スタートアップのソースコード分解」の記事で解説しています。
/// [WidgetsBinding]
void initInstances() {
super.initInstances();
_instance = this;
_buildOwner = BuildOwner();
buildOwner.onBuildScheduled = _handleBuildScheduled;
/// ......省略......
}
void _handleBuildScheduled() {
// ダーティな要素がビルドされている場合、変更は新しいフレームをトリガーしてはならない。
ensureVisualUpdate();
}
// オブジェクトが現在フレームを生成していない場合は、scheduleFrameを使って新しいフレームをスケジュールする。
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
/// [SchedulerBinding]
void scheduleFrame() {
// アプリケーションがフォアグラウンドで表示されているとき,_framesEnabledもし
// ここで、アプリがバックグラウンドに切り替わると、直接
if (_hasScheduledFrame || !framesEnabled)
return;
ensureFrameCallbacksRegistered();
// 実行エンジンのスケジューリングメソッド
window.scheduleFrame();
_hasScheduledFrame = true;
}
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
ここでのscheduleFrameメソッドは、JavaのJNIメソッドと同様に、C++で実装されたエンジンのネイティブメソッドです。このメソッドの公式な説明は
onBeginFrameコールバックとonDrawFrameコールバックが次の適切な機会に呼び出されるようにリクエストします!
void scheduleFrame() native 'Window_scheduleFrame';
C++の実装を追跡する代わりに、onBeginFrameとonDrawFrameコールバックの説明に従ってください。
/// [SchedulerBinding]
void _handleBeginFrame(Duration rawTimeStamp) {
if (_warmUpFrame) {
_ignoreNextEngineDrawFrame = true;
return;
}
handleBeginFrame(rawTimeStamp);
}
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
return;
}
handleDrawFrame();
}
ここでは、"Framework Startup Source Code Analysis "のレンダリングセクションで分析されたロジックに対応しています。 handleDrawFrameへの後続の呼び出しについては、 スキップして、主にbuildOwnerのbuildScopeメソッドで、ここで懸念されるロジックを直接見てください。
/// [WidgetsBinding]
void drawFrame() {
// ......省略......
try {
if (renderViewElement != null)
// ダーティとマークされた要素をRebuild()する。
buildOwner.buildScope(renderViewElement);
// 親クラスのdrawFrameメソッドを呼び出す。これは実際にはRendererBindingのdrawFrame()メソッドである。
super.drawFrame();
// アクティブでなくなったエレメントをアンロードして、エレメントのビルドプロセスを完了する。
buildOwner.finalizeTree();
}
// ......省略......
}
/// [BuildOwner]
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
Timeline.startSync('Build', arguments: timelineWhitelistArguments);
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
Element debugPreviousBuildTarget;
_dirtyElementsNeedsResorting = false;
try {
callback();
} finally {
}
}
// ダーティエレメントのリストをソートする
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
try {
// 要素にリビルドを呼び出す
_dirtyElements[index].rebuild();
} catch (e, stack) {
}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
index -= 1;
}
}
}
} finally {
for (final Element element in _dirtyElements) {
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
}
}
/// [Element]
void rebuild() {
if (!_active || !_dirty)
return;
// リビルドを実行する
performRebuild();
}
ここでは、performRebuild() はそのサブクラス ComponentElement で実装されています。
/// [ComponentElement]
void performRebuild() {
if (!kReleaseMode && debugProfileBuildsEnabled)
Timeline.startSync('${widget.runtimeType}', arguments: timelineWhitelistArguments);
Widget built;
try {
// 要素に関連するウィジェットのビルドメソッドを呼び出して、コントロールツリーを再構築する。
built = build();
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
} finally {
// ビルド呼び出し中にmarkNeedsBuild()の試みが無視されるまで、要素をクリーンとしてマークするのを遅らせる。
_dirty = false;
}
try {
// エレメントツリーを更新する
_child = updateChild(_child, built, slot);
} catch (e, stack) {
}
if (!kReleaseMode && debugProfileBuildsEnabled)
Timeline.finishSync();
}
ここで要素ツリーを更新するためのロジックは、すべてupdateChildメソッドにあります。
また、build() メソッドの具体的な実装も見てみましょう。
/// StatelessWidgetエレメント
class StatelessElement extends ComponentElement {
StatelessElement(StatelessWidget widget) : super(widget);
@override
StatelessWidget get widget => super.widget as StatelessWidget;
// buildcontextパラメータは、実際にはウィジェットのElementオブジェクトだ。
@override
Widget build() => widget.build(this);
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
}
/// StatefulWidgetエレメント
class StatefulElement extends ComponentElement {
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
_state._element = this;
_state._widget = widget;
}
@override
Widget build() => _state.build(this);
State<StatefulWidget> get state => _state;
State<StatefulWidget> _state;
// ......省略......
}
レンダリングプロセス
ページを更新する必要がある場合、アプリケーションの上位レイヤーはEngineに通知し、Engineは次のVsync信号が到着するまで待ち、次にFrameworkの上位レイヤーに通知します。Engineはレイヤーを合成し、テクスチャを生成し、最後にOpenGlインターフェイスを通してGPUにデータを送信し、GPUはデータを処理してモニタに表示します。
ビデオコース
Flutterフルスタック開発コースの完全版については、以下のアドレスをご覧ください。





