继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Flutter完整开发实战详解(五、 深入探索)

慕标5832272
关注TA
已关注
手记 1222
粉丝 229
获赞 1001

一、WidgetsFlutterBinding

这是一个胶水类。

1、Mixins

混入其中( ̄. ̄)!

是的,Flutter 使用的是 Dart 支持 Mixin ,而 Mixin 能够更好的解决多继承中容易出现的问题,如:方法优先顺序混乱、参数冲突、类结构变得复杂化等等。

Mixin 的定义解释起来会比较绕,我们直接代码从中出吧。如下代码所示,在 Dart 中 with 就是用于 mixins。可以看出,class G extends B with A, A2 ,在执行 G 的 a、b、c 方法后,输出了 A2.a()、A.b() 、B.c() 。所以结论上简单来说,就是相同方法被覆盖了,并且 with 后面的会覆盖前面的

class A {
  a() {    print("A.a()");
  }

  b() {    print("A.b()");
  }
}class A2 {
  a() {    print("A2.a()");
  }
}class B {
  a() {    print("B.a()");
  }

  b() {    print("B.b()");
  }

  c() {    print("B.c()");
  }
}class G extends B with A, A2 {

}


testMixins() {
  G t = new G();
  t.a();
  t.b();
  t.c();
}/// ***********************输出***********************///I/flutter (13627): A2.a()///I/flutter (13627): A.b()///I/flutter (13627): B.c()

接下来我们继续修改下代码。如下所示,我们定义了一个 Base 的抽象类,而A、A2、B 都继承它,同时再 print 之后执行 super() 操作。

从最后的输入我们可以看出,A、A2、B中的所有方法都被执行了,且只执行了一次,同时执行的顺序也是和 with 的顺序有关。如果你把下方代码中 class A.a() 方法的 super 去掉,那么你将看不到 B.a()base a() 的输出。

abstract class Base {
  a() {    print("base a()");
  }

  b() {    print("base b()");
  }

  c() {    print("base c()");
  }
}class A extends Base {
  a() {    print("A.a()");
    super.a();
  }

  b() {    print("A.b()");
    super.b();
  }
}class A2 extends Base {
  a() {    print("A2.a()");
    super.a();
  }
}class B extends Base {
  a() {    print("B.a()");
    super.a();
  }

  b() {    print("B.b()");
    super.b();
  }

  c() {    print("B.c()");
    super.c();
  }
}class G extends B with A, A2 {

}

testMixins() {
  G t = new G();
  t.a();
  t.b();
  t.c();
}///I/flutter (13627): A2.a()///I/flutter (13627): A.a()///I/flutter (13627): B.a()///I/flutter (13627): base a()///I/flutter (13627): A.b()///I/flutter (13627): B.b()///I/flutter (13627): base b()///I/flutter (13627): B.c()///I/flutter (13627): base c()

2、WidgetsFlutterBinding

说了那么多,那 Mixins 在 Flutter 中到底有什么用呢?这时候我们就要看 Flutter 中的“胶水类”: WidgetsFlutterBinding

WidgetsFlutterBinding 在 Flutter启动时runApp会被调用,作为App的入口,它肯定需要承担各类的初始化以及功能配置,这种情况下,Mixins 的作用就体现出来了。

webp

webp

从上图我们可以看出, WidgetsFlutterBinding 本身是并没有什么代码,主要是继承了 BindingBase,而后通过 with 黏上去的各类 Binding,这些 Binding 也都继承了 BindingBase

看出来了没,这里每个 Binding 都可以被单独使用,也可以被“黏”到 WidgetsFlutterBinding 中使用,这样做的效果,是不是比起一级一级继承的结构更加清晰了?

最后我们打印下执行顺序,如下图所以,不出所料ヽ( ̄ ̄)ノ。

webp

二、InheritedWidget

InheritedWidget 是一个抽象类,在 Flutter 中扮演者十分重要的角色,或者你并未直接使用过它,但是你肯定使用过和它相关的封装。

webp

如上图所示,InheritedWidget 主要实现两个方法:

  • 创建了 InheritedElement ,该 Element 属于特殊 Element,  主要增加了将自身也添加到映射关系表 _inheritedWidgets【注1】,方便子孙 element 获取;同时通过 notifyClients 方法来更新依赖。

  • 增加了 updateShouldNotify 方法,当方法返回 true 时,那么依赖该 Widget 的实例就会更新。

所以我们可以简单理解:InheritedWidget 通过  InheritedElement  实现了由下往上查找的支持(因为自身添加到 _inheritedWidgets),同时具备更新其子孙的功能。

注1:每个 Element 都有一个 _inheritedWidgets ,它是一个 HashMap<Type, InheritedElement>,它保存了上层节点中出现的 InheritedWidget 与其对应 element 的映射关系。

webp

接着我们看 BuildContext,如上图,BuildContext 其实只是接口, Element 实现了它。InheritedElementElement 的子类,所以每一个 InheritedElement 实例是一个 BuildContext 实例。同时我们日常使用中传递的 BuildContext 也都是一个 Element 。

所以当我们遇到需要共享 State 时,如果逐层传递 state 去实现共享会显示过于麻烦,那么了解了上面的 InheritedWidget 之后呢?

是否将需要共享的 State,都放在一个 InheritedWidget 中,然后在使用的 widget 中直接取用就可以呢?答案是肯定的!所以如下方这类代码:通常如 焦点、主题色、多语言、用户信息 等都属于 App 内的全局共享数据,他们都会通过 BuildContext(InheritedElement) 获取。

///收起键盘FocusScope.of(context).requestFocus(new FocusNode());/// 主题色Theme.of(context).primaryColor/// 多语言Localizations.of(context, GSYLocalizations) 
/// 通过 Redux 获取用户信息StoreProvider.of(context).userInfo/// 通过 Redux 获取用户信息StoreProvider.of(context).userInfo/// 通过 Scope Model 获取用户信息ScopedModel.of<UserInfo>(context).userInfo

综上所述,我们从先 Theme 入手。

如下方代码所示,通过给 MaterialApp 设置主题数据,通过 Theme.of(context) 就可以获取到主题数据并绑定使用。当 MaterialApp 的主题数据变化时,对应的 Widget 颜色也会发生变化,这是为什么呢(キ`゚Д゚´)!!?

  ///添加主题
  new MaterialApp(
      theme: ThemeData.dark()
  );  
  ///使用主题色
  new Container( color: Theme.of(context).primaryColor,

通过源码一层层查找,可以发现这样的嵌套: MaterialApp -> AnimatedTheme -> Theme -> _InheritedTheme extends InheritedWidget ,所以通过 MaterialApp 作为入口,其实就是嵌套在 InheritedWidget 下。

webp

如上图所示,通过 Theme.of(context) 获取到的主题数据,其实是通过 context.inheritFromWidgetOfExactType(_InheritedTheme) 去获取的,而 Element 中实现了 BuildContextinheritFromWidgetOfExactType 方法,如下所示:

webp

那么,还记得上面说的  _inheritedWidgets  吗?既然 InheritedElement 已经存在于 _inheritedWidgets 中,拿出来用就对了。

前文:InheritedWidget 内的 InheritedElement ,该 Element 属于特殊 Element,  主要增加了将自身也添加到映射关系表 _inheritedWidgets

最后,如下图所示,在 InheritedElement 中,notifyClients 通过 InheritedWidgetupdateShouldNotify 方法判断是否更新,比如在 Theme的  _InheritedTheme  是:

bool updateShouldNotify(_InheritedTheme old) => theme.data != old.theme.data;

webp

所以本质上 Theme、Redux 、 Scope Model、Localizations 的核心都是 InheritedWidget

三、内存

最近闲鱼技术发布了 《Flutter之禅 内存优化篇》 ,文中对于 Flutter 的内存做了深度的探索,其中有一个很有趣的发现是:

  • Flutter 中 ImageCache 缓存的是 ImageStream 对象,也就是缓存的是一个异步加载的图片的对象。

  • 在图片加载解码完成之前,无法知道到底将要消耗多少内存。

  • 所以容易产生大量的IO操作,导致内存峰值过高。

webp

图片来自闲鱼技术

如上图所示,是图片缓存相关的流程,而目前的拮据处理是通过:

  • 在页面不可见的时候没必要发出多余的图片

  • 限制缓存图片的数量

  • 在适当的时候CG

更详细的内容可以阅读文章本体,这里为什么讲到这个呢?是因为 限制缓存图片的数量 这一项。

还记得 WidgetsFlutterBinding 这个胶水类吗?其中Mixins 了 PaintingBinding 如下图所示,被"黏“上去的这个 binding 就是负责图片缓存

webp

在  PaintingBinding 内有一个 ImageCache 对象,该对象全局一个单例的,同时再图片加载时的 ImageProvider 所使用,所以设置图片缓存大小如下:

//缓存个数 100PaintingBinding.instance.imageCache.maximumSize=100;//缓存大小 50mPaintingBinding.instance.imageCache.maximumSizeBytes= 50 << 20;

四、线程

在闲鱼技术的 深入理解Flutter Platform Channel 中有讲到:Flutter中有四大线程,Platform Task Runner 、UI Task Runner、GPU Task Runner 和 IO Task Runner。

其中 Platform Task Runner 也就是 Android 和 iOS 的主线程,而 UI Task Runner 就是Flutter的 UI 线程。

如下图,如果做过 Flutter 中 Dart 和原生端通信的应该知道,通过 Platform Channel 通信的两端就是 Platform Task Runner  和 UI Task Runner,这里主要总结起来是:

  • 因为 Platform Task Runner 本来就是原生的主线程,所以尽量不要在 Platform 端执行耗时操作。

  • 因为Platform Channel并非是线程安全的,所以消息处理结果回传到Flutter端时,需要确保回调函数是在Platform Thread(也就是Android和iOS的主线程)中执行的。

webp

图片来自闲鱼技术



作者:恋猫月亮
链接:https://www.jianshu.com/p/42b59b07323d


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP