Flutter

Flutter - 特性

2019-03-13  本文已影响259人  前行的乌龟

在我们深入学习 Flutter 之前,对 Flutter 特性有个清晰的认识我认为是非常有必要的,这会让我们后面的学习更加透彻

尤其是这几天我被拉去了一个卖 Flutter 学习课程的公开课里,前面的内容很不错,在开始 Flutter 课程之前,先把 Flutter 的特性说上这么一说,我觉得这样学习的话效果会很不错。不能一上来就直接学,得先对要学习的内容有个深刻的认识,这样才好在学习的时候有更深入,更正确的认识


Flutter 最显著的特性就 2 个:

跨平台 - 是指我们可以通过同样的 Flutter 代码在 android,ios 2 端实现同样 UI 效果的 app ,这里就要说明了 Flutter 实质上实现的 UI 跨平台,涉及到不同平台的巨大差异,是不可能使用同一套代码在多端实现相同的效果的,未来随着 Fuchsia 系统的上线,Flutter 的运行环境还会加上 pc,物联网,web 端,多端之间的差异更是会拉大,想着一套代码运行那是没戏的,只能是通过 Flutter 代码实现 UI 层级的多端运行, Flutter 代码取调用各端本地实现的功能 lib

Fuchsia 已经可以在 PC 运行了

我们知道之前 FaceBook 的 RN 也是号称:"一处编写,处处使用",后来不像宣城的这么好事,然后改成了 "一处学习,处处使用",再然后就没然后啦,18年 FaceBook 停更 RN 了,也就是放弃了。RN 的是使用 JS 语言编写代码,然后由 RN 解释器在本地映射生成 native 的 view 已实现显示的,android 吧本身 UI 性能就不怎么样,要不网上怎么会有一大票铺天盖地的 UI 性能优化文章呢,RN 在 android 之上再次加上了 view 解释器和生成器,无形这又是增加了不少任务,所以 RN 的性能优化了好久,最后还是不理想,无奈放弃,这也是有原因的,其实最大的原因就是 Flutter 的出现,Flutter 的性能相比 RN 有太大的优势,FaceBook 也就不在 RN 身上在费事了,注定是打不过 Flutter 的,再说 RN 的坑太多,大伙都不用了,没见阿里爸爸都写了一篇 RN 从入门到放弃吗

哈哈这不正巧 FaceBook 正式回应了 Flutter 了吗,在专访中面对直白的问题,FaceBook 没有做正面回答就是最好的答案,呵呵,FaceBook 自己都信心不足,说什么 RN 和 Flutter 不是一个类型的,没有可比性,这不是纯扯淡嘛,详见:

都说 Flutter 性能好,为啥呢,这还得从 Flutter 的架构看,其实很简单

Flutter 的架构图

Engine C++ 内嵌层包括: Skia | Dart | Text

重点看红圈部分,Flutter 是直接使用 Skia 2D 图形引擎来绘制的,不再是 RN 的那套映射 native 本地组件了,Flutter 做的比 native 更彻底,理论性能上甚至比 native 上还好

Flutter 怎么干的,Flutter 没有 xml 了,UI 组件直接写在代码里面,Flutter 生成的 android 项目只有 MainActivity 这一个页面, MainActivity 页面里面只有 FlutterView 这一个 view ,所有的 UI 组件全部由 FlutterView 直接在 canvas 上绘制,比 android 原生还利落

android 原生得经过 xml(Dom 解析) -> layoutInflater 反射出 view 视图树来 -> rootView 的 measu,layout,draw -> 系统驱动 Skia 2D 绘图引擎来绘制图像

Flutter 没有了 xml ,也就不用 Dom 解析了,直接通过 canvas 驱动 Skia 2D 绘图引擎来绘制,FlutterView 渲染 UI 也比 android 强很多,不再是 android 那样从 rootView 一层一层的反复计算,定位再绘制。Flutter 使用统一的 UI 模型:widget,在更新 widget 的时候,采用类似于 recycleview DiffUtils 那样的 view 复用机制复用 widget 对象,无用的 widget 能复用就复用,更更新数据就更新数据,这样比实现更高效率的 UI 渲染。当然一切看优化,Flutter 要是优化到位,性能肯定比原生要好这是一定的了,可能还好很多

图不是很清晰,大家看个意思


Flutter 对比 android 绘制流程

通过对比,我想说 Flutter 真是太棒了,Google 通过 android 的锻炼已经对移动端足够了解,坑都趟的差不多了,所以作为下一代产品预热的 Flutter 天然的在技术上对 android NB 多了,以 android 现在的市场占有率,Google 的能力,Fuchsia 一出来包括硬件和软件在内的生态很快都能做起来,取代 android 也就是 2-3年的事

为了做在到多端的 UI 统一,Flutter 提供的 UI 组件在 android 和 ios 上分别了做了自行绘制,以保证式样相同


Flutter 在 android / ios 上 UI 对比_1 Flutter 在 android / ios 上 UI 对比_2

Flutter 在 Android 系统上使用 Skia 绘图引擎绘制图形,在 ios 上使用 JPEG 绘图引擎绘制图形,这些都是各端原生的,将来还会有 pc 端的,web 端的实现。可以看到 Flutter 思路的巧妙,简便,当然也是任务量巨大,所以 Flutter 才能在众多跨平台方案中脱颖而出,赢得宝座,这里我得再次膜拜 Google 大神

热重载 - 这个不要理解为热修复啊,不是这个,热重载指的是在开发阶段我们只要 Control + s 保存就能直接在设备上更新代码了

Flutter 在 deBug 阶段不需要编译,直接发送 Dart 文件差异包给链接的设备更新代码,因为没有 编译,所以也不用 打包,安装,这2个过程,可以做到秒级更新的水平,这比传统的 android 编译,打包,安装的过程快了不知道多少,真是一个天上一个地下,这个时候我想大家都会深深体验到:科技改变生活这句话含义的,有了这个优势,我们一天至少多了 20-30 分钟的工作时间


Flutter 渲染机制

Flutter 界面渲染过程分为三个阶段:布局、绘制、合成

布局和绘制在Flutter框架中完成,合成则交由引擎负责,至于采用合成图层的原因:由于直接交付给 GPU 多图层视图数据是低效率的,可能会重复绘制,所以还需要进行一步图层的合成,最后才交由引擎负责光栅化视图

这样 Flutter 就节约了类似 android 这样 layout xml DOM 解析操作的性能损耗与频繁进行局部 DOM 操作的矛盾

以前 android DOM 会在每一次元素更新到来之时渲染一次DOM,Flutter 会先汇总各个元素的更新情况,通过diff算法计算出与原来 DOM 树的差异,最后通过一次 DOM 更新解决

还有要注意一点,每一个 Flutter 实例会启动三个线程,分别是 UI 线程、GPU 线程和 IO 线程,所以复用一个 Flutter 实例会减少资源消耗

刚详细的 Flutter 渲染机制请看:纷争再起:Flutter-UI绘制解析


从源码看看 Flutter 怎么显示的

目前我这只能运行 Flutter 编译的 android app,这也是 android coder 关心的,其实从这里也能看出来 Flutter 如何跨平台的

1. Flutter main 入口

Flutter 项目的入口是 main 函数,也是整个 Flutter 项目执行的开始,这里看出来 Dart 受 java 影响还是很深的


Flutter main 函数

Flutter 在 android 项目里会把 Flutter 的 main 入口编译成 MainActivity


MainActivity 位置

而这个 MainActivity 则继承自 FlutterActivity

class MainActivity(): FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
  }
}

看出来了吧,Flutter 就是这么在 android 中开始的,Flutter 编译的 android 项目只有这么一个 Activity 的,想不想 Google 的风格,16 年可以兴起一阵学习 Google Mail+ 单 Activity + 多 Fragment 的页面组合方式,当年我还追来着,后来发现 Fragment 栈管理真是难啊,这不单 Activity + 多轻量级 page 的思路在 Flutter 上又复活了,接着看,这个思路妥妥的

2. FlutterActivity

FlutterActivity 内部没有啥逻辑,不看也没事,是声明了 Provider, PluginRegistry, ViewFactory 3个接口,然后自己实现

/// activity实现了三个接口
public class FlutterActivity extends Activity implements Provider, PluginRegistry, ViewFactory {

/// 视图提供类,一个方法getFlutterView
public interface Provider {
    FlutterView getFlutterView();
}

/// 插件注册
public interface PluginRegistry {
    /// 注册一个插件
    PluginRegistry.Registrar registrarFor(String var1);
    /// 是否有这个插件
    boolean hasPlugin(String var1);
    /// 插件发布值
    <T> T valuePublishedByPlugin(String var1);
    /// 下面开始为插件生命周期回调
    public interface PluginRegistrantCallback {
        void registerWith(PluginRegistry var1);
    }

    /// 显然是视图销毁的回调
    public interface ViewDestroyListener {
        boolean onViewDestroy(FlutterNativeView var1);
    }
        
    //// 不知道干啥,先留着
    public interface UserLeaveHintListener {
        void onUserLeaveHint();
    }
   
    //// activity生命周期回调之onNewIntent
    public interface NewIntentListener {
        boolean onNewIntent(Intent var1);
    }
    
  //// activity生命周期回调之onActivityResult
    public interface ActivityResultListener {
        boolean onActivityResult(int var1, int var2, Intent var3);
    }

//// activity生命周期回调之onRequestPermissionsResult
    public interface RequestPermissionsResultListener {
        boolean onRequestPermissionsResult(int var1, String[] var2, int[] var3);
    }
    /// 插件的注册者,相当于插件的宿主。
    public interface Registrar {
        Activity activity();

        Context context();

        Context activeContext();
        /// 这个BinaryMessager还不知道干啥
        BinaryMessenger messenger();
        /// TextureRegistry不清楚
        TextureRegistry textures();
        /// 获取视图
        FlutterView view();
        /// 寻找资源
        String lookupKeyForAsset(String var1);
        /// 寻找资源
        String lookupKeyForAsset(String var1, String var2);
        /// 发布值,与上面的valuePublishedByPlugin对应
        PluginRegistry.Registrar publish(Object var1);
        
        /// 增加生命周期回调
        PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener var1);

        /// 增加生命周期回调
        PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener var1);

        PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener var1);

        /// 增加生命周期回调
        PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener var1);
        
        /// 增加生命周期回调
        PluginRegistry.Registrar addViewDestroyListener(PluginRegistry.ViewDestroyListener var1);
    }
}

/// 视图工厂
  public interface ViewFactory {
        /// 创建flutterView
        FlutterView createFlutterView(Context var1);
        //// 创建nativeview
        FlutterNativeView createFlutterNativeView();
        ///  暂时搞不清楚干啥的,先留着
        boolean retainFlutterNativeView();
    }

    private final FlutterActivityEvents eventDelegate = delegate;
    private final FlutterView.Provider viewProvider = delegate;
    private final PluginRegistry pluginRegistry = delegate;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        eventDelegate.onCreate(savedInstanceState);
    }


3. FlutterActivityDelegate

在 2 中我们能看到 FlutterActivity 把包括 onCreate 在内的所有生命周期函数都交给 eventDelegate 这个代理了, eventDelegate 的类型是 FlutterActivityEvents ,那么重点是我们看看 FlutterActivityEvents 的 onCreate 干啥了

public final class FlutterActivityDelegate implements FlutterActivityEvents, FlutterView.Provider, PluginRegistry {

    ........

    @Override
    public void onCreate(Bundle savedInstanceState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(0x40000000);
            window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
        }

        String[] args = getArgsFromIntent(activity.getIntent());
        FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);

        flutterView = viewFactory.createFlutterView(activity);
        if (flutterView == null) {
            FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
            flutterView = new FlutterView(activity, null, nativeView);
            flutterView.setLayoutParams(matchParent);
            activity.setContentView(flutterView);
            launchView = createLaunchView();
            if (launchView != null) {
                addLaunchView();
            }
        }

        if (loadIntent(activity.getIntent())) {
            return;
        }

        String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
        if (appBundlePath != null) {
            runBundle(appBundlePath);
        }
    }
}

核心就是创建出 FlutterView 这个 view 对象,然后把 FlutterView setContentView 给 activity,这样就完成了从 Flitter 到 android 平台的显示,MainActivity 这个AC 可是只有 FlutterView 这一个 view 啊,Flutter 所有的 UI 组件都是由 FlutterView 使用 canvas 直接绘制的。Flutter 就是这样和 android 联系的,脱离了 android 传统的 xml 布局方式,节省了 xml dom 解析反射生成 viewTree 的操作

4. FlutterView

FlutterView 可以解答我们很多问题,而且是核心问题,从 FlutterView 我们可以看到 Flutter 的优越性

public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {

    public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
        super(context, attrs);

        Activity activity = (Activity) getContext();
        if (nativeView == null) {
            mNativeView = new FlutterNativeView(activity.getApplicationContext());
        } else {
            mNativeView = nativeView;
        }

        mSurfaceCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                assertAttached();
                mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                assertAttached();
                mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                assertAttached();
                mNativeView.getFlutterJNI().onSurfaceDestroyed();
            }
        };
    }

FlutterView 继承自 SurfaceView ,那么也就是说明 UI 绘制不是由 android UI 线程完成,而是完全完全由 Flutter 自己单干,这好啊,android UI 线程事太多了,把任务分解给别人干会让系统快很多的,而且 SurfaceView 是双缓存的,会缓存上一帧和当前帧,android 同步页面信号时若是 UI 线程还没把当前帧算出来,那么就会放弃当前帧的渲染,这就造成了丢帧,丢 3帧 用户就会感觉到明显的卡顿了,尤其是在动画时有有味明显。SurfaceView 不然,SurfaceView 有自己独立的 surface 显存区域,自己绘制自己, 双缓存,页面同步信号来是我只要把计算好的当前帧发给系统就行了,而不会管我现在是不是在计算下一帧

代码里 FlutterView 的绘制实际交给了 FlutterNativeView.getFlutterJNI() - FlutterJNI,我们去看看 FlutterJNI

5. FlutterJNI

public class FlutterJNI {

  @UiThread
  public static native boolean nativeGetIsSoftwareRenderingEnabled();

  @UiThread
  public static native String nativeGetObservatoryUri();
  
  private Long nativePlatformViewId;
  private FlutterRenderer.RenderSurface renderSurface;
  private AccessibilityDelegate accessibilityDelegate;
  private PlatformMessageHandler platformMessageHandler;

  @UiThread
  public void onSurfaceCreated(@NonNull Surface surface) {
    ensureAttachedToNative();
    nativeSurfaceCreated(nativePlatformViewId, surface);
  }

  private native void nativeSurfaceCreated(long nativePlatformViewId, Surface surface);

  @UiThread
  public void onSurfaceChanged(int width, int height) {
    ensureAttachedToNative();
    nativeSurfaceChanged(nativePlatformViewId, width, height);
  }

  private native void nativeSurfaceChanged(long nativePlatformViewId, int width, int height);

  @UiThread
  public void onSurfaceDestroyed() {
    ensureAttachedToNative();
    nativeSurfaceDestroyed(nativePlatformViewId);
  }

  private native void nativeSurfaceDestroyed(long nativePlatformViewId);
}

FlutterJNI 里面有大量的 native 方法,都是和底层打交道的,期中 FlutterRenderer.RenderSurface renderSurface 就是负责显示的,FlutterRenderer 是 Flutter 负责渲染的组件,剩下的大家自己取看吧,基本上这就解释清楚了 Flutter 为啥可以跨平台而 UI 样式相同,因为纯是自己画的,脱离原生平台组件


Flutter 内存/ GC 管理

以下内容来源于:闲鱼技术团队,参考文章: Flutter 内存机制梳理

Flutter 使用 Dart 语言开发,所以和 java JVM 虚拟机一样需要 Dart 的运行环境 VM。和Android ART 一样,Flutter 也对Dart 源码做了 AOT 编译,直接将 Dart 源码编译成了本地字节码,没有了解释执行的过程,提升执行性能

这里我们关注的是 Dart VM 内存分配和 GC 的相关部分。和 Java 不同,Dart 的线程 Isolate 是不共享内存的,拥有各自独立的堆(Heap) 和栈 (Stack) ,并且是各自独立 GC 的,彼此之间通过消息通道来通信。Dart天然不存在数据竞争和变量状态同步的问题,整个 Flutter Framework Widget 的渲染过程都运行在一个 isolate 中

Dart 内存管理

GC 部分,Dart 和 java 是相同的,同样在堆内存中分为:新生代 / 老年代,老年代里分 from / to 2快区域,进行整块内存地址的来回复制,删除

可以看到,Dart VM 借鉴了很多 JVM 的思路,Dart 中产生内存泄露的方式也和 Java 类似,Java 中很多排查内存泄露的思路和防止内存泄露的编程方法应该也可以借鉴过来

Android 将内存分为 JVM 虚拟机内存 和 Native 内存,系统对 JVM 虚拟机内存有一个上限,到达上限就会触发 OOM 异常,而对 Native 内存的使用没有太严格的限制,现在的手机内存都很大,一般有较大的Native内存富余,这里我们来看下 Android 和 Flutter 对 bitmap 存储的处理


Flutter 编译模式

Flutter 有 2种编译模式:JIT | AOT

Flutter 有 3种开发模式:debug | release | profile ,他们之间的差异比 android 中不同编译模式的差距大

事实上 flutter 下的 iOS / Android 工程本质上依然是一个标准的 iOS / Android 工程,flutter只是通过在 BuildPhase 中添加 shell 来生成和嵌入 App.framework 和 Flutter.framework(iOS) , 通过 gradle 来添加 flutter.jar 和vm / isolate_snapshot_data / instr(Android) 来将 Flutter 相关代码编译和嵌入原生App而已

更具体的内容详见:深入理解flutter的编译原理与优化


Flutter 项目结构


最后

最后了,我们来梳理下 Flutter 的优点,摘自:浅谈flutter的优点与缺点

Flutter 优点:
Flutter 缺点:
上一篇 下一篇

猜你喜欢

热点阅读