WidgetsBindingObserver工作原理学习

WidgetsBindingObserver

实际开发过程中常用来监听App的生命周期方法,因为Flutter的StatefulWidget的生命周期方法中没有像Android Activity类似的onResume、onPause等方法,所以一般要实现类似的业务场景,就会使用到WidgetsBindingObserver。先看一个实际开发过程中的例子:

class _VocabularyPageState extends State<VocabularyPage> with WidgetsBindingObserver {
  
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addObserver(this);
  }
  
  
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }
  
  
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
        
    }
  }
}

通过上述代码,就让State类实现了类似Android中Activity onResume生命周期方法类似的效果。

这个类具体的实现是什么样的呢,先打开文档阅读一下。

Interface for classes that register with the Widgets layer binding.

When used as a mixin, provides no-op method implementations.

See WidgetsBinding.addObserver and WidgetsBinding.removeObserver.

This class can be extended directly, to get default behaviors for all of the handlers, or can used with the implements keyword, in which case all the handlers must be implemented (and the analyzer will list those that have been omitted).

光看文档感觉十分模糊,没法判断出来这个类具体是做什么的。这样只能打开这个类的源码,研究下是否能弄明白它的实现。

WidgetsBindingObserver的代码实现十分简单,是一个抽象类,提供了一些方法定义。下面我们看其中的一部分。

abstract class WidgetsBindingObserver {
  /// 当系统通知app pop当前的route时被调用,例如在安卓上用户点击了返回键。
  Future<bool> didPopRoute() => Future<bool>.value(false);
  
  /// 当应用的尺寸变化时被调用,例如手机发生了旋转。
  void didChangeMetrics() { }
  
  /// 当系统把应用放置到前台或后台时被调用,这个方法暴露了[SystemChannels.lifecycle]的通知
  void didChangeAppLifecycleState(AppLifecycleState state) { }
}

看下来可以发现这个类时一个回调类,用来暴露一些系统相关的通知,具体是谁调用的目前还不知道。看到SystemChannels时,大致可以推断出,didChangeAppLifecycleState是App中Flutter容器(FlutterActivity)通过channel把自己的生命周期通知到WidgetsBindingObserver的。

继续看下WidgetsBindingObserver这个类所在源码文件的上下文,可以看到WidgetsBinding类,结合使用时调用的WidgetsBinding.instance?.addObserver(this),看起来WidgetsBinding应该是完成这些逻辑的类了。看下WidgetsBinding的文档说明。

The glue between the widgets layer and the Flutter engine.

这个说明还是可以看明白一点东西,这个类的主要目的是连接了Flutter引擎和Widget层,具体在做什么就继续看源码吧。这里想到一个问题,WidgetsBinding.instance这个instance是怎么来的,先看下这点。

/// The current [WidgetsBinding], if one has been created.
///
/// If you need the binding to be constructed before calling [runApp],
/// you can ensure a Widget binding has been constructed by calling the
/// `WidgetsFlutterBinding.ensureInitialized()` function.
static WidgetsBinding? get instance => _instance;

光看这个类的说明没有看出来instance是怎么创建出来的,不过解答另外一个疑惑,就是我们在runApp的时候调用WidgetsFlutterBinding.ensureInitialized()原来是确保这个类的初始化。

那是谁初始化了WidgetsBinding呢,翻了源码一时半会也没找到,倒是看到了一堆binding结尾的类,例如GestureBinding、RendererBinding、PaintingBinding,一下子也没完全弄明白是做什么的。上Google搜搜吧,搜到了一篇很好的文档,开篇就回答了我们的问题。

runApp kicks off binding initialization by invoking the WidgetsFlutterBinding/RenderingFlutterBinding.ensureInitialized static method. This calls each binding’s initInstances method, allowing each to initialize in turn.

看起来是runApp这个方法中初始化的,先去源码中验证一下。

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

果然这里在runApp方法中调用的,但是这个WidgetsFlutterBinding和我们之前用到的WidgetsBinding的关系是什么呢?WidgetsFlutterBinding初始化的同时WidgetsBinding是否也初始化了呢。继续看下WidgetsFlutterBinding.ensureInitialized()的源码吧。

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

WidgetsFlutterBinding的代码很简单,但是却更加让人不解了,就调用了一下默认的构造函数?还有with后面这一串东西又是要干嘛?按常规的思路,先看看基类BindingBase的构造方法吧。

/// Base class for mixins that provide singleton services (also known as
/// "bindings").
///
/// To use this class in an `on` clause of a mixin, inherit from it and implement
/// [initInstances()]. The mixin is guaranteed to only be constructed once in
/// the lifetime of the app (more precisely, it will assert if constructed twice
/// in debug mode).
///
/// The top-most layer used to write the application will have a concrete class
/// that inherits from [BindingBase] and uses all the various [BindingBase]
/// mixins (such as [ServicesBinding]). For example, the Widgets library in
/// Flutter introduces a binding called [WidgetsFlutterBinding]. The relevant
/// library defines how to create the binding. It could be implied (for example,
/// [WidgetsFlutterBinding] is automatically started from [runApp]), or the
/// application might be required to explicitly call the constructor.
abstract class BindingBase {
  BindingBase() {
    initInstances();
  }
  
  /// The initialization method. Subclasses override this method to hook into
  /// the platform and otherwise configure their services. Subclasses must call
  /// "super.initInstances()".
  ///
  /// By convention, if the service is to be provided as a singleton, it should
  /// be exposed as `MixinClassName.instance`, a static getter that returns
  /// `MixinClassName._instance`, a static field that is set by
  /// `initInstances()`.
  void initInstances() {
  
  }
}

这里我删掉一些不相关的代码,最终可以看到BindingBase的构造函数调用一个initInstances方法,initInstances的实现是空的,让人越看越迷糊。

回过头去仔细读一读源码的注释,多次提到了mixin这个关键词,感觉这个可能是一个关键所在,所以继续Google一下,先学一下这个mixin,找到了这篇文章什么是mixin,大致理解之后回过头来再看WidgetsBinding的实现,好像有点理解了,但是也不是完全明白,我尝试了另外一个手段,就是运行起来打上断点然后Debug一下,看看源码的调用是什么样子的。在BindingBase的initInstances和WidgetBinding的initInstances方法上加上断点,看看会发生什么。得到了下图所示的调用栈:
调用栈

发现BindingBase的initInstances的调用过程中,也会调用WidgetBinding的initInstances方法,也包括了之前看到过的with后面的那些xxxBinding的initInstances方法。看到调用栈里的这些类名结合刚才看过的mixin的原理,更加明白了一些,这些mixin在初始化的时候形成了一个调用链,会调用mix后的最后的一个类的的initInstances方法,然后又通过调用super.initInstances()继续调用上一层mix的initInstances方法。这里理解起来的确有点复杂,一时看不懂也不打紧,最好先弄明白mixin再来看这块的源码,这篇文章里就不展开了。

好了,大致明白了怎么初始化的,继续回到之前的问题,WidgetsBinding是怎么实现监听app的生命周期方法的。找到WidgetsBindingObserver.didChangeAppLifecycleState方法被调用的相关源码。


void handleAppLifecycleStateChanged(AppLifecycleState state) {
  super.handleAppLifecycleStateChanged(state);
  for (final WidgetsBindingObserver observer in _observers)
    observer.didChangeAppLifecycleState(state);
}

通过IDE的代码导航,搜一下这个方法被调用到的地方,找到了两处,都在ServicesBinding类中。

mixin ServicesBinding on BindingBase, SchedulerBinding {
  
  void initInstances() {
    super.initInstances();
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
    readInitialLifecycleStateFromNativeWindow();
  }
  
  
  void readInitialLifecycleStateFromNativeWindow() {
    if (lifecycleState != null) {
      return;
    }
    final AppLifecycleState? state = _parseAppLifecycleMessage(window.initialLifecycleState);
    if (state != null) {
      handleAppLifecycleStateChanged(state);
    }
  }
  
  Future<String?> _handleLifecycleMessage(String? message) async {
    handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message!)!);
    return null;
  }
}

这一块的代码还是比较好理解的,处理了SystemChannels里通知过来的App生命周期变化的通知,以及在初始化的时候获取一次当前的App生命周期。这样我们的Widget注册了WidgetsBindingObserver之后,就可以响应App的生命周期了。大部分逻辑其实是在ServicesBinding类中实现的,WidgetsBinding只是增加了监听的注册与销毁逻辑。

总结一下这次过程,我们从WidgetsBindingObserver开始,研究了WidgetsBinding是做什么的,怎么进行的初始化,中间岔开去学习了一些mixin是怎么工作的,最后了解了WidgetsBinding是如何把系统的监听传递给Widget的。

参考阅读

  • Framework,介绍了flutter的初始化、渲染等的实现过程
  • 什么是mixin,理解mixin可以帮我们更好的理解WidgetBinding的源码实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值