Flutter의 다양한 바인딩에 대한 자세한 설명

머리말

Flutter의 모든 작업은 다양한 Bindings에서 예정되어 있으며 플랫폼 측에서 Widget, Element 및 RenderObject의 종속성을 완전히 분리하는 것은 이러한 바인더의 존재입니다.이 기사를 읽으려면 다음과 같은 Flutter 기반이 필요합니다. Flutter의 기본, Flutter와 Platform 간의 통신 원리, 그리고 세 가지 Flutter 트리의 각각의 책임

위젯플러터바인딩

WidgetsFlutterBinding은 GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding을 상속하고 위젯과 엔진 간의 결합을 담당하는 싱글톤 스토리지입니다.

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance!;
  }
}

모든 바인딩은 다음 그림과 같이 상위 위젯, 요소 및 RenderObject에 대해 호출됩니다.

이벤트 디스패치 바인더 — GestureBinding

GestureBinding은 Flutter의 이벤트 배포 관리입니다.자세한 내용은 Talking About Flutter's Core Mechanisms - Event Distribution 을 참조하세요.

  1. Engine 레이어의 이벤트 리스너 등록, onPointerDataPacket은 Engine 레이어 콜백 메소드
  void initInstances() {
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;
  }
  1. RenderObject dispatchEvent에 이벤트 전달
  void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
 ...
    for (final HitTestEntry entry in hitTestResult.path) {
      try {
        entry.target.handleEvent(event.transformed(entry.transform), entry);
      } catch (exception, stack) {
      }
    }
  }
  1. 이벤트 메서드인 handleEvent를 처리하고 RenderObject에 등록된 모든 제스처 인식기를 처리하고 경쟁합니다.
  @override // from HitTestTarget
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    pointerRouter.route(event);
    if (event is PointerDownEvent) {
      gestureArena.close(event.pointer);
    } else if (event is PointerUpEvent) {
      gestureArena.sweep(event.pointer);
    } else if (event is PointerSignalEvent) {
      pointerSignalResolver.resolve(event);
    }
  }

작업 스케줄러 바인더 — SchedulerBinding

SchedulerBinding은 다양한 유형의 작업을 스케줄링하는 타이밍 처리, UI 구성 전/UI 구성 후 일부 작업 실행을 담당하는 작업 스케줄러이며 작업의 우선 순위를 지정할 수도 있습니다.

  1. handleBeginFrame은 scheduleFrameCallback 내부에 등록된 콜백을 실행하기 위한 것입니다.
  void handleBeginFrame(Duration? rawTimeStamp) {
    Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
    _firstRawTimeStampInEpoch ??= rawTimeStamp;
    _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
    if (rawTimeStamp != null)
      _lastRawTimeStamp = rawTimeStamp;

    _hasScheduledFrame = false;
    try {
      // TRANSIENT FRAME CALLBACKS
      Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
      _schedulerPhase = SchedulerPhase.transientCallbacks;
      final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
      _transientCallbacks = <int, _FrameCallbackEntry>{};
      callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
        if (!_removedIds.contains(id))
          _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
      });
      _removedIds.clear();
    } finally {
      _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
    }
  }
  1. handleDrawFrame은 addPersistentFrameCallback/addPostFrameCallback에 등록된 콜백을 실행하기 위한 것으로, UI 파이프라인에서 빌드한 콜백도 내부에서 실행됩니다.
  void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    Timeline.finishSync(); // end the "Animate" phase
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (final FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);

      // POST-FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (final FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
    } finally {
      _schedulerPhase = SchedulerPhase.idle;
      Timeline.finishSync(); // end the Frame
      assert(() {
        if (debugPrintEndFrameBanner)
          debugPrint('▀' * _debugBanner!.length);
        _debugBanner = null;
        return true;
      }());
      _currentFrameTimeStamp = null;
    }
  }
  1. GPU 래스터화 시간 소모적인 콜백 – GPU 시간 소모적인 감지에 사용할 수 있는 addTimingsCallback
  void addTimingsCallback(TimingsCallback callback) {
    _timingsCallbacks.add(callback);
    if (_timingsCallbacks.length == 1) {
      assert(window.onReportTimings == null);
      window.onReportTimings = _executeTimingsCallbacks;
    }
    assert(window.onReportTimings == _executeTimingsCallbacks);
  }
  1. 비동기 작업의 우선 순위 실행 – scheduleTask
  Future<T> scheduleTask<T>(TaskCallback<T> task,Priority priority, {String? debugLabel, Flow? flow,}) {
    final bool isFirstTask = _taskQueue.isEmpty;
    final _TaskEntry<T> entry = _TaskEntry<T>(task, priority.value, debugLabel, flow,);
    _taskQueue.add(entry);
    if (isFirstTask && !locked)
      _ensureEventLoopCallback();
    return entry.completer.future;
  }
  1. 다음 프레임이 작업을 빌드하기 전에 작업 - handleBeginFrame에서 호출되는 scheduleFrameCallback(1 참조), 예: 애니메이션에서 데이터 값 업데이트(ps: cancelFrameCallbackWithId 메서드로 취소 가능)
  int scheduleFrameCallback(FrameCallback callback, { bool rescheduling = false }) {
    scheduleFrame();
    _nextFrameCallbackId += 1;
    _transientCallbacks[_nextFrameCallbackId] = _FrameCallbackEntry(callback, rescheduling: rescheduling);
    return _nextFrameCallbackId;
  }
  1. 영구 콜백 태스크인 addPersistentFrameCallback, drawFrame의 태스크가 이 메소드에 등록됩니다. ps: 콜백이 등록되어 다음 프레임 이전에 매번 실행됩니다.
  void addPersistentFrameCallback(FrameCallback callback) {
    _persistentCallbacks.add(callback);
  }
  1. addPostFrameCallback은 handleDrawFrame에서 addPersistentFrameCallback 이후에 한 번만 호출됩니다.
  void addPostFrameCallback(FrameCallback callback) {
    _postFrameCallbacks.add(callback);
  }
  1. scheduleFrame()은 다시 호출해야 하는 UI 업데이트가 있음을 엔진에 알립니다.
  void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled)
      return;
    ensureFrameCallbacksRegistered();
    window.scheduleFrame();
    _hasScheduledFrame = true;
  }

서비스 바인더 — ServicesBinding

ServicesBinding 등록은 메시징 메신저 _defaultBinaryMessenger, 플랫폼의 다양한 라이프 사이클 등과 같은 일부 플랫폼 서비스를 관리합니다.

  void initInstances() {
    super.initInstances();
    _instance = this;
    //与platform通信的信使
    _defaultBinaryMessenger = createBinaryMessenger();
    //状态恢复回调
    _restorationManager = createRestorationManager();
    window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    initLicenses();
    //系统消息处理,如内存低 。。
    SystemChannels.system.setMessageHandler((dynamic message) => handleSystemMessage(message as Object));
    //生命周期
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
    readInitialLifecycleStateFromNativeWindow();
  }
  1. _defaultBinaryMessenger는 플랫폼과의 통신을 담당합니다.자세한 내용 은 Flutter의 MethodChannel/EventChannel 원칙을 참조하십시오. 두 가지 새로운 방법은 다음과 같습니다.
//发送二进制消息发给platform
  Future<ByteData?> _sendPlatformMessage(String channel, ByteData? message) {
    final Completer<ByteData?> completer = Completer<ByteData?>();
    ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
    });
    return completer.future;
  }

//处理platform发过来的二进制消息
  @override
  Future<void> handlePlatformMessage(
    String channel,
    ByteData? data,
    ui.PlatformMessageResponseCallback? callback,
  ) async {
    ByteData? response;
    try {
      final MessageHandler? handler = _handlers[channel];
      if (handler != null) {
        response = await handler(data);
      } else {
        ui.channelBuffers.push(channel, data, callback!);
        callback = null;
      }
    } 
  }
  1. handleSystemMessage는 메모리 부족, 글꼴 변경과 같은 시스템 메시지를 처리합니다.
  //该方法被子类重写了
  @protected
  @mustCallSuper
  Future<void> handleSystemMessage(Object systemMessage) async {
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
    switch (type) {
      case 'memoryPressure':
        handleMemoryPressure();
        break;
    }
    return;
  }
  1. _parseAppLifecycleMessage 수명 상태 콜백 처리
  static AppLifecycleState? _parseAppLifecycleMessage(String message) {
    switch (message) {
      case 'AppLifecycleState.paused':
        return AppLifecycleState.paused;
      case 'AppLifecycleState.resumed':
        return AppLifecycleState.resumed;
      case 'AppLifecycleState.inactive':
        return AppLifecycleState.inactive;
      case 'AppLifecycleState.detached':
        return AppLifecycleState.detached;
    }
    return null;
  }
  1. RestorationManager 데이터 저장/복원 관리
@protected
RestorationManager createRestorationManager() {
  return RestorationManager();
}

이미지 바인더 — PaintingBinding

PaintingBinding은 비교적 간단하고 Flutter 이미지 캐시를 관리하고 이미지 코덱을 만들고 GPU 셰이더 프로그램을 예열합니다.

보조 서비스 바인더 — SemanticsBinding

SemanticsBinding은 플랫폼에서 보조 서비스 이벤트를 처리합니다.

렌더링 바인더 — RendererBinding

RendererBinding은 렌더링 파이프라인 PipelineOwner(RenderObject 관리)를 관리하고 밝기 변경, 글꼴 배율 변경 등과 같은 플랫폼 디스플레이 관련 모니터링을 등록하는 핵심 구성 요소입니다. 첫 번째 RenderObject ---- RenderView를 생성합니다.

  1. initInstances 초기화
  void initInstances() {
    super.initInstances();
    _instance = this;
    //渲染管线,管理需要刷新的RenderObject
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
      //创建第一个 RenderObject  ---- RenderView
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    //注册绘制流水线回调,可参考SchedulerBinding中第6条
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }

드로잉 핵심 메소드 drawFrame

  @protected
  void drawFrame() {
    assert(renderView != null);
    //刷新需要布局
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    //刷新绘制
    pipelineOwner.flushPaint();
    if (sendFramesToEngine) {
    	//生成Scene发送到Engine
      renderView.compositeFrame(); // this sends the bits to the GPU
      pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
      _firstFrameSent = true;
    }
  }

위젯 바인더 — WidgetsBinding

WidgetsBinding은 세 가지 위젯 트리의 입구이며 위젯 레이어의 수명 주기 모니터링 등록, 밝기 변경 등과 같은 위젯과 요소 간의 일부 비즈니스를 처리합니다.

  1. runApp 위젯 입력 방법
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}
  1. initInstances 초기화
  void initInstances() {
    super.initInstances();
    _instance = this;
		//管理Element标脏,回收
    _buildOwner = BuildOwner();
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
    //语言,时区
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
  }
  1. drawFrame 핵심 메서드
  void drawFrame() {
		...
    try {
      if (renderViewElement != null)
        // 对标脏的Element进行重建,从而生成新的三棵树
        buildOwner!.buildScope(renderViewElement!);
        
        //见RendererBinding#drawFrame
      super.drawFrame();
      //卸载不再使用的Element
      buildOwner!.finalizeTree();
    } finally {
    }

   
  }

요약하다

위의 분석을 통해 Flutter의 Binding 구성 요소 간의 협업 작업이 Flutter UI 상호 작용 시스템을 구축하고 상대적으로 독립적이며 비즈니스 간의 결합을 분리하는 한 가지 유형의 책임만 담당한다는 것을 알 수 있습니다.

추천

출처blog.csdn.net/jdsjlzx/article/details/123545142