flutter androidView实现原理

1,创建PlatformView

PlatformView是flutter中可以渲染原生界面的view,在android中的体现是AndroidView
flutter 创建AndroidView的时候,实际渲染的是_AndroidViewState build方法

_AndroidViewState
  @override
  Widget build(BuildContext context) {
    return Focus(
      focusNode: _focusNode,
      onFocusChange: _onFocusChange,
      child: _AndroidPlatformView(
        controller: _controller,
        hitTestBehavior: widget.hitTestBehavior,
        gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet,
      ),
    );
  }

_initializeOnce是在didChangeDependencies和didUpdateWidget的时候调用的而且除非viewtype改变了否则只会调用一次

_AndroidViewState
  void _initializeOnce() {
    if (_initialized) {
      return;
    }
    _initialized = true;
    _createNewAndroidView();
    _focusNode = FocusNode(debugLabel: 'AndroidView(id: $_id)');
  }

_AndroidViewState
void _createNewAndroidView() {
    _id = platformViewsRegistry.getNextPlatformViewId();
    _controller = PlatformViewsService.initAndroidView(
      id: _id,
      viewType: widget.viewType,
      layoutDirection: _layoutDirection,
      creationParams: widget.creationParams,
      creationParamsCodec: widget.creationParamsCodec,
      onFocus: () {
        _focusNode.requestFocus();
      },
    );
    if (widget.onPlatformViewCreated != null) {
      _controller.addOnPlatformViewCreatedListener(widget.onPlatformViewCreated);
    }
  }

关注 PlatformViewsService.initAndroidView ,内部创建了一个AndroidViewController,后面创建Texture就是使用这个类创建的

  static AndroidViewController initAndroidView({
    @required int id,
    @required String viewType,
    @required TextDirection layoutDirection,
    dynamic creationParams,
    MessageCodec<dynamic> creationParamsCodec,
    VoidCallback onFocus,
  }) {
    assert(id != null);
    assert(viewType != null);
    assert(layoutDirection != null);
    assert(creationParams == null || creationParamsCodec != null);
    final AndroidViewController controller = AndroidViewController._(
      id,
      viewType,
      creationParams,
      creationParamsCodec,
      layoutDirection,
    );
    _instance._focusCallbacks[id] = onFocus ?? () {};
    return controller;
  }

视线转移到AndroidView的Render

  RenderAndroidView
  void performResize() {
    size = constraints.biggest;
    _sizePlatformView();
  }

  Future<void> _sizePlatformView() async {
    ···
    do {
      targetSize = size;
      await _viewController.setSize(targetSize);
      _currentAndroidViewSize = targetSize;
      // We've resized the platform view to targetSize, but it is possible that
      // while we were resizing the render object's size was changed again.
      // In that case we will resize the platform view again.
    } while (size != targetSize);
    ...
  }

  Future<void> setSize(Size size) async {
    if (_state == _AndroidViewState.waitingForSize)
      return _create(size);
    await SystemChannels.platform_views.invokeMethod<void>('resize', <String, dynamic>{
      'id': id,
      'width': size.width,
      'height': size.height,
    });
  }

当AndroidViewState为waitingForSize时会调用_create方法

  Future<void> _create(Size size) async {
    ···
    _textureId = await SystemChannels.platform_views.invokeMethod('create', args);
    _state = _AndroidViewState.created;
    for (final PlatformViewCreatedCallback callback in _platformViewCreatedCallbacks) {
      callback(id);
    }
···
  }

此处会调用methodchannel event 为 create返回textureid
继续看android实现

PlatfromViewsChannel.java
        private void create(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
          Map<String, Object> createArgs = call.arguments();
          PlatformViewCreationRequest request =
              new PlatformViewCreationRequest(
                  (int) createArgs.get("id"),
                  (String) createArgs.get("viewType"),
                  (double) createArgs.get("width"),
                  (double) createArgs.get("height"),
                  (int) createArgs.get("direction"),
                  createArgs.containsKey("params")
                      ? ByteBuffer.wrap((byte[]) createArgs.get("params"))
                      : null);

          try {
            long textureId = handler.createPlatformView(request);
            result.success(textureId);
          } catch (IllegalStateException exception) {
            result.error("error", detailedExceptionString(exception), null);
          }
        }

public long createPlatformView(
            @NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
          PlatformViewFactory viewFactory = registry.getFactory(request.viewType);
          if (viewFactory == null) {
            throw new IllegalStateException(
                "Trying to create a platform view of unregistered type: " + request.viewType);
          }

          Object createParams = null;
          if (request.params != null) {
            createParams = viewFactory.getCreateArgsCodec().decodeMessage(request.params);
          }

          int physicalWidth = toPhysicalPixels(request.logicalWidth);
          int physicalHeight = toPhysicalPixels(request.logicalHeight);
          validateVirtualDisplayDimensions(physicalWidth, physicalHeight);

          TextureRegistry.SurfaceTextureEntry textureEntry = textureRegistry.createSurfaceTexture();
          VirtualDisplayController vdController =
              VirtualDisplayController.create(
                  context,
                  accessibilityEventsDelegate,
                  viewFactory,
                  textureEntry,
                  physicalWidth,
                  physicalHeight,
                  request.viewId,
                  createParams,
                  (view, hasFocus) -> {
                    if (hasFocus) {
                      platformViewsChannel.invokeViewFocused(request.viewId);
                    }
                  });

          if (flutterView != null) {
            vdController.onFlutterViewAttached(flutterView);
          }

          vdControllers.put(request.viewId, vdController);
          View platformView = vdController.getView();
          platformView.setLayoutDirection(request.direction);
          return textureEntry.id();
        }

上图列出关键步骤,textureRegistry创建了一个SurfaceTextureEntry 这就是flutter最终渲染的texture

     @Override
        public void resizePlatformView(
            @NonNull PlatformViewsChannel.PlatformViewResizeRequest request,
            @NonNull Runnable onComplete) {
          ensureValidAndroidVersion();

          final VirtualDisplayController vdController = vdControllers.get(request.viewId);
          if (vdController == null) {
            throw new IllegalStateException(
                "Trying to resize a platform view with unknown id: " + request.viewId);
          }

          int physicalWidth = toPhysicalPixels(request.newLogicalWidth);
          int physicalHeight = toPhysicalPixels(request.newLogicalHeight);
          validateVirtualDisplayDimensions(physicalWidth, physicalHeight);
          lockInputConnection(vdController);
          vdController.resize(
              physicalWidth,
              physicalHeight,
              new Runnable() {
                @Override
                public void run() {
                  unlockInputConnection(vdController);
                  onComplete.run();
                }
              });
        }

VirtualDisplayController 是控制展示的类

  public static VirtualDisplayController create(
      Context context,
      AccessibilityEventsDelegate accessibilityEventsDelegate,
      PlatformViewFactory viewFactory,
      TextureRegistry.SurfaceTextureEntry textureEntry,
      int width,
      int height,
      int viewId,
      Object createParams,
      OnFocusChangeListener focusChangeListener) {
    textureEntry.surfaceTexture().setDefaultBufferSize(width, height);
    Surface surface = new Surface(textureEntry.surfaceTexture());
    DisplayManager displayManager =
        (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);

    int densityDpi = context.getResources().getDisplayMetrics().densityDpi;
    VirtualDisplay virtualDisplay =
        displayManager.createVirtualDisplay("flutter-vd", width, height, densityDpi, surface, 0);

    if (virtualDisplay == null) {
      return null;
    }

    return new VirtualDisplayController(
        context,
        accessibilityEventsDelegate,
        virtualDisplay,
        viewFactory,
        surface,
        textureEntry,
        focusChangeListener,
        viewId,
        createParams);
  }

virturalDisplay需要渲染的显示抽象

create方法创建了VirtualDisplay,他负责渲染图像到Flutter提供的surface。
我们关注SingleViewPresentation

onCreate(){ 创建view,在attacEngine的时候调用
  ··· 
if (state.platformView == null) {
      state.platformView = viewFactory.create(context, viewId, createParams);
    }
  ···
}

然后每次factory创建的自定义view调用onDraw方法就会渲染到flutter提供的surface中,达到在flutter中渲染的目的

AndroidView事件分发

获取到手指事件
  MotionEventDispatcher.dart
  void handlePointerEvent(PointerEvent event) {
    if (event is PointerDownEvent) {
      if (nextPointerId == 0)
        downTimeMillis = event.timeStamp.inMilliseconds;
      pointerProperties[event.pointer] = propertiesFor(event, nextPointerId++);
    }
    pointerPositions[event.pointer] = coordsFor(event);

    dispatchPointerEvent(event);

    ···
  }
分发事件,最终调用了 touch channel回调原生
  _MotionEventsDispatcher.dart
  void dispatchPointerEvent(PointerEvent event) {
    ····
    final AndroidMotionEvent androidMotionEvent = AndroidMotionEvent(
        downTime: downTimeMillis,
        eventTime: event.timeStamp.inMilliseconds,
        action: action,
        pointerCount: pointerPositions.length,
        pointerProperties: pointers.map<AndroidPointerProperties>((int i) => pointerProperties[i]).toList(),
        pointerCoords: pointers.map<AndroidPointerCoords>((int i) => pointerPositions[i]).toList(),
        metaState: 0,
        buttonState: 0,
        xPrecision: 1.0,
        yPrecision: 1.0,
        deviceId: 0,
        edgeFlags: 0,
        source: 0,
        flags: 0,
    );
    viewController.sendMotionEvent(androidMotionEvent);
  ····
  }
  AndroidViewController.dart
  Future<void> sendMotionEvent(AndroidMotionEvent event) async {
    await SystemChannels.platform_views.invokeMethod<dynamic>(
        'touch',
        event._asList(id),
    );
  }

android原生代码接受channel事件处理

PlatformViewsController
       @Override
        public void onTouch(@NonNull PlatformViewsChannel.PlatformViewTouch touch) {
          ensureValidAndroidVersion();

          float density = context.getResources().getDisplayMetrics().density;
          PointerProperties[] pointerProperties =
              parsePointerPropertiesList(touch.rawPointerPropertiesList)
                  .toArray(new PointerProperties[touch.pointerCount]);
          PointerCoords[] pointerCoords =
              parsePointerCoordsList(touch.rawPointerCoords, density)
                  .toArray(new PointerCoords[touch.pointerCount]);

          if (!vdControllers.containsKey(touch.viewId)) {
            throw new IllegalStateException(
                "Sending touch to an unknown view with id: " + touch.viewId);
          }
          View view = vdControllers.get(touch.viewId).getView();

          MotionEvent event =
              MotionEvent.obtain(
                  touch.downTime.longValue(),
                  touch.eventTime.longValue(),
                  touch.action,
                  touch.pointerCount,
                  pointerProperties,
                  pointerCoords,
                  touch.metaState,
                  touch.buttonState,
                  touch.xPrecision,
                  touch.yPrecision,
                  touch.deviceId,
                  touch.edgeFlags,
                  touch.source,
                  touch.flags);

          view.dispatchTouchEvent(event);
        }

总结:flutter的AndroidView实现原理是将渲染内容渲染到flutter提供的SurfaceTexture中,然后事件传递是flutter事件传递到原生view实现的,双端的事件冲突在flutter端处理。

由于这种实现方式导致频繁的channel调用性能较差,另外如果嵌入NestedScrollView页面会频繁创建VirtualDisplay,性能会很差

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354