flutter

247 阅读5分钟

一、介绍

2017年5月Google I/O大会正式对外公布Flutter,到2018年12月发布Flutter1.0,引发全球大量的开发者和企业开始研究Flutter
Flutter是彻底的跨平台方案,既没有采用webView,也没有采用JS桥接原生控件,而是自行实现一套UI框架

  • 在引擎底层通过Skia渲染到屏幕。

image.png

  • 对于UI之外所需要使用的移动设备自身提供的服务,比如相机、定位、屏幕触摸等,则采用Platform Channels跟原生系统通信的方式来实现。

二、架构

image.png

  • Flutter Framework层:用Dart编写,封装整个Flutter架构的核心功能,包括Widget、动画、绘制、手势等功能,有Material(Android风格UI)和Cupertino(iOS风格)的UI界面, 可构建Widget控件以及实现UI布局。
  • Flutter Engine层:用C++编写,用于高质量移动应用的轻量级运行时环境,实现了Flutter的核心库,包括Dart虚拟机、动画和图形、文字渲染、通信通道、事件通知、插件架构等。引擎渲染采用的是2D图形渲染库Skia,虚拟机采用的是面向对象语言Dart VM,并将它们托管到Flutter的嵌入层。shell实现了平台相关的代码,比如跟屏幕键盘IME和系统应用生命周期事件的交互。不同平台有不同的shell,比如Android和iOS的shell。

Flutter引擎启动

image.png

  • FlutterApplication.java的onCreate过程主要完成初始化配置、加载引擎libflutter.so、注册JNI方法;
  • FlutterActivity.java的onCreate过程,通过FlutterJNI的AttachJNI()方法来初始化引擎Engine、Dart虚拟机、Isolate、taskRunner等对象。再经过层层处理最终调用main.dart中main()方法,执行runApp(Widget app)来处理整个Dart业务代码。

Flutter引擎启动中会创建有4个TaskRunner以及创建虚拟机

TaskRunner

Flutter引擎启动过程,会创建UI/GPU/IO这3个线程,会为这些线程依次创建MessageLoop对象,启动后处于epoll_wait等待状态。
对于Flutter的消息机制跟Android原生的消息机制有很多相似之处,都有消息(或者任务)、消息队列(或任务队列)以及Looper;有一点不同的是Android有一个Handler类,用于发送消息以及执行回调方法,相对应Flutter中有着相近功能的便是TaskRunner。

image.png

这4个Task Runner的具体工作内容。

  • Platform Task Runner:运行在Android或者iOS的主线程,尽管阻塞该线程并不会影响Flutter渲染管道,平台线程建议不要执行耗时操作;否则可能触发watchdog来结束该应用。比如Android、iOS都是使用平台线程来传递用户输入事件,一旦平台线程被阻塞则会引起手势事件丢失。
  • UI Task Runner: 运行在ui线程,比如1.ui,用于引擎执行root isolate中的所有Dart代码,执行渲染与处理Vsync信号,将widget转换生成Layer Tree。除了渲染之外,还有处理Native Plugins消息、Timers、Microtasks等工作;
  • GPU Task Runner:运行在gpu线程,比如1.gpu,用于将Layer Tree转换为具体GPU指令,执行设备GPU相关的skia调用,转换相应平台的绘制方式,比如OpenGL, vulkan, metal等。每一帧的绘制需要UI Runner和GPU Runner配合完成,任何一个环节延迟都可能导致掉帧;
  • IO Task Runner:运行在io线程,比如1.io,前3个Task Runner都不允许执行耗时操作,该Runner用于将图片从磁盘读取出来,解压转换为GPU可识别的格式后,再上传给GPU线程。为了能访问GPU,IO Runner跟GPU Runner的Context在同一个ShareGroup。比如ui.image通过异步调用让IO Runner来异步加载图片,该线程不能执行其他耗时操作,否则可能会影响图片加载的性能。

渲染原理

image.png

渲染过程,UI线程完成布局、绘制操作,生成Layer Tree;GPU线程执行合成并光栅化后交给GPU来处理,其中几个关键步骤:

  • Animate: 遍历_transientCallbacks,执行动画回调方法;
  • Build: 对于dirty的元素会执行build构造,没有dirty元素则不会执行,对应于buildScope()
  • Layout: 计算渲染对象的大小和位置,对应于flushLayout(),这个过程可能会嵌套再调用build操作;
  • Compositing bits: 更新具有脏合成位的任何渲染对象, 对应于flushCompositingBits();
  • Paint: 将绘制命令记录到Layer, 对应于flushPaint();
  • Compositing: 将Compositing bits发送给GPU, 对应于compositeFrame();

GPU线程通过skia向GPU硬件绘制一帧的数据,GPU将帧信息保存到FrameBuffer里面,然后视频控制器会根据VSync信号从FrameBuffer取帧数据传递给显示器,从而显示出最终的画面。

Platform Channels

Flutter框架提供了UI的控件支持,对于APP除了UI还有其他依赖于Native平台的支持,比如调用Camera的功能,该怎么办呢?为此,Flutter通过提供Platform Channel的功能,使得Dart代码具备与Native交互的能力。

image.png

Platform Channel用于Flutter与Native之间的消息传递,整个过程的消息与响应是异步执行,不会阻塞用户界面。Flutter引擎框架已完成桥接的通道,这样开发者只需在Native层编写定制的Android/iOS代码,即可在Dart代码中直接调用,这也就是Flutter Plugin插件的一种形式

核心原理:

  • Flutter应用通过Platform Channel将传递的数据编码成消息的形式,跨线程发送到该应用所在的宿主(Android或iOS);
  • 宿主接收到Platform Channel的消息后,调用相应平台的API,也就是原生编程语言来执行相应方法;
  • 执行完成后将结果数据通过同样方式原路返回给应用程序的Flutter部分

原文: gityuan.com/flutter/

三、底层实现

  • 单页面:大部分移动框架都是单页面应用,整个UI默认运行在一个Activity/viewControl上面,框架路由与原生路由没关系;

  • 画布:原生平台提供一个类似Surface的画布,之后由Flutter来渲染

    FlutterApplication

    FlutterMain.startInitialization(this)
    

    FlutterActivityDelegate

    flutterView = new FlutterView(activity,null,nativeView)
    
  • android使用skia Api:将OnDraw()方法中获得的画布对象传递给本机代码,再使用c++和skia Api进行绘制 推荐文章

  • 给Android和iOS开发flutter基础

  • flutter实战第二版

欢迎关注我的前端自检清单,我和你一起成长