前言

阅读此文的彦祖,亦菲们,附送一枚Provider模板代码生成插件!

我为啥要写这个插件呢?

此事说来话短,我这不准备写解析Provider源码的文章,肯定要写这框架的使用样例啊,然后再哔哔源码呀!在写demo样例的时候,新建那俩三个文件、文件夹和必写的模板代码,这让我感到很方啊,这不耽误我时间嘛!然后就撸了这个插件,相对而言,多花了几百倍的时间。。。

希望这个插件,能减轻使用Provider小伙们的一点工作量;插件里面的模板代码是经过我深思熟虑过的,如果各位靓仔有更好的模板代码,请在评论里贴出来,我觉得合理的话,会加入到插件里。

关于Provider的源码,如果对设计模式或面向接口编程不熟悉的话,看起来是相当懵逼的,基本就是:懵逼树上懵逼果,懵逼树下你和我;Provider源码使用了大量的抽象类,调用父类构造函数,继承实现断言,很多关键的函数调用,点进去都是抽象类,必须返回好几层去看看这个抽象类的实现类是什么,看的十分头大!这里面有很多设计模式的痕迹:观察者模式、策略模式、外观模式、命令模式、访问者模式、模板模式、迭代器模式、、、

我会竭尽所能的将总体流程说清楚,相关晦涩流程会结合图文,并给出相应小demo演示

ε=(´ο`*)))唉,这篇文章写完,我感觉整个人都被掏空了。。。

不管你用或不用Provider,我相信在你读完本文的刷新机制栏目,大概率会对该框架中闪耀的智慧,感到由衷的赞叹!

使用

老规矩,说原理之前,先来看下使用

Provider的使用,和我前俩篇写的Handler和ThreadLocal使用有一些区别

Provider是一个状态管理框架,写它的使用可能会占较多篇幅,所以文章整体篇幅也会较长,请见谅。。。

我实在不想分篇幅水赞啊,而且也是为了方便大家可以在一篇文章里面查阅相关知识(请结合掘金旁边的大纲食用),也方便我随时修改优化文章内容。。。

插件

  • 插件github:provider_template

    • 使用中碰见什么bug,希望大家能及时给我提issue
  • 插件可以进入Android Studio的Setting里面,选择Plugins,然后搜索flutter provider,第一个,看图上红框标定的就是了,点击install安装即可

  • 来下看使用效果图

  • 如果你不喜欢这种命名方式,这里提供修改入口;也支持了持久化

    • 大家按需修改吧

  • Alt + Enter : 可以选择包裹Widget,有三种可选(Consumer、Selector、ChangeNotifierProvider),一键生成麻烦,重复且使用频率很高的Widget!

  • 快捷代码片段提示:我自己写了三个,如果老哥们还有其它的骚操作,需要各位提PR啊

    • 在这个文件里面添加就行了,大家可以参照写法:provider_template
    • 输入 provider 前缀便有提示

写法

ChangeNotifierProvider中为什么用builder?而不用child?

  • Provider.of(context, listen: false)中的context,必须是ChangeNotifierProvider或其子Widget的
  • 使用ProEasyCounterPage的context,会发现无法找到ProEasyCounterProvider的情况,导致无法触发increment()方法
  • 原理是什么?看完文章,你就造了
class ProEasyCounterPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (BuildContext context) => ProEasyCounterProvider(),builder: (context, child) => _buildPage(context),);}Widget _buildPage(BuildContext context) {final provider = context.read<ProEasyCounterProvider>();return Scaffold(appBar: AppBar(title: Text('Provider-Easy范例')),body: Center(child: Consumer<ProEasyCounterProvider>(builder: (context, provider, child) {return Text('点击了 ${provider.count} 次',style: TextStyle(fontSize: 30.0),);},),),floatingActionButton: FloatingActionButton(onPressed: () => provider.increment(),child: Icon(Icons.add),),);}
}class ProEasyCounterProvider extends ChangeNotifier {int count = 0;void increment() {count++;notifyListeners();}
}

获取注入的实例的方法有点长;所以我在 _buildPage(…) 这个方法中,将其提出来,单独赋值给了一个变量,方便后续使用

插件生成代码

插件生成代码分为俩个模式:Default和High

默认模式有俩个文件(Default):view、provider

高级模式有三个文件(High):view、provider、state

大家都是用Flutter的老手,对这种结构应该非常了解,state层是把数据层独立出来维护

在非常复杂的提交界面,state层我甚至还会分出:跳转(jump)、提交(submit)、展示(show)这三种结构;没办法,一个模块搞了上百个变量,不这样分,太难维护了

default:默认模式下的模板代码

  • view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';import 'provider.dart';class CounterPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (BuildContext context) => CounterProvider(),builder: (context, child) => _buildPage(context),);}Widget _buildPage(BuildContext context) {final provider = context.read<CounterProvider>();return Container();}
}
  • provider
import 'package:flutter/material.dart';class CounterProvider extends ChangeNotifier {}

High:高级模式下的模板代码

  • view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';import 'provider.dart';class CounterPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (BuildContext context) => CounterProvider(),builder: (context, child) => _buildPage(context),);}Widget _buildPage(BuildContext context) {final provider = context.read<CounterProvider>();return Container();}
}
  • provider
import 'package:flutter/material.dart';import 'state.dart';class CounterProvider extends ChangeNotifier {final state = CounterState();
}
  • state
class CounterState {CounterState() {// init some variables}
}

前置知识

下面就是Provider的源码分析内容了,如果大家赶时间,可以点个赞(方便日后查阅,滑稽.jpg),回头等有时间,再静下心来慢慢看;我怕你快餐式阅读,读到刷新机制那块,会直接骂街,这写的啥玩意???

Provider的刷新机制,相关流程相当之绕,我已经竭尽全力,精简了无数我们不需要关注的代码,然后一步步带着你的思路去走一遍正确的流程,相关类还给了很多说明,但是架不住源码流程山路十八弯,绕的一比啊!你如果不用心去看,去体会,会相当烦躁。。。

我已经帮大家熬过最蛋筒的部分,相关绕的流程画了详细的图示,我已经努力了;如果你想知道Provider内部运转机制,现在就需要你努力了!

ChangeNotifier的单独使用

ValueListenableBuilder和ValueNotifier可以配套使用,ValueListenableBuilder内部也是一个StatefulWidget,代码很简单,感兴趣的可以自己查看

这个暂且不表,这边就搞最原始的ChangeNotifier的使用

大家肯定在Provider都写过继承ChangeNotifier的代码,而且写的非常多,但是大家知道怎么单独使用ChangeNotifier,以达到控制界面变化的效果吗?

我搜了很多怎么单独使用ChangeNotifier的文章,但是基本都是写配合ChangeNotifierProvider在Provider中使用的,我佛了呀,搜到寥寥无几的文章,也没说清楚,怎么单独使用;我想这玩意是不是有个单独XxxWidgetBuild配合使用?但是!我怎么都找不到,气抖冷!

我突然想到,TextField控件中的TextEditingController用到了ChangeNotifier,总不可能TextField还用Provider吧!我在源码里面一通翻,各种super,abstract,私有变量,看的头皮发麻,最后终于找到了关键代码,搞清楚TextField是怎么使用ChangeNotifier的了,为什么每次改变TextEditingController的text值,然后在TextField数据框里的数据也及时改变了,其实最后还是用到setState

TextField中的流程代码不贴了,如果贴出来,会相当占篇幅:我下面会写一个颗粒度最小ChangeNotifier的单独使用demo

  • TextEditingController实际是继承了ValueNotifier,来看下ValueNotifier
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {ValueNotifier(this._value);@overrideT get value => _value;T _value;set value(T newValue) {if (_value == newValue)return;_value = newValue;notifyListeners();}@overrideString toString() => '${describeIdentity(this)}($value)';
}

ValueNotifier实际是对ChangeNotifier的封装

这里影响不大,我们还是使用ChangeNotifier,来写一个类似TextField中的控制器效果,每当控制器中的数值改变,其控件内容就自动更新

  • 先使用ChangeNotifier搞一个控制器
class TestNotifierController extends ChangeNotifier {String _value = '0';String get value => _value;set value(String newValue) {if (_value == newValue) return;_value = newValue;notifyListeners();}
}
  • 搭配这个控制器的Widget

    • OK,这样就搞定了,改变控制器的数据,Widget也会自动刷新
    • 我把功能颗粒度压缩的非常小,希望大家阅读会比较轻松
class TestNotifierWidget extends StatefulWidget {const TestNotifierWidget({Key? key,this.controller,}) : super(key: key);final TestNotifierController? controller;@override_TestNotifierState createState() => _TestNotifierState();
}class _TestNotifierState extends State<TestNotifierWidget> {@overridevoid initState() {///添加回调 value改变时,自动触发回调内容widget.controller?.addListener(_change);super.initState();}@overrideWidget build(BuildContext context) {return Text(widget.controller?.value ?? '初始值为空',style: TextStyle(fontSize: 30.0),);}///被触发的回调void _change() {setState(() {});}
}
  • 来看下怎么使用这个控件

    • 使用代码已经非常简单了:onPressed改变了控制器数值内容,TestNotifierWidget控件会自动刷新
class TestNotifierPage extends StatelessWidget {@overrideWidget build(BuildContext context) {final controller = TestNotifierController();var count = 0;return Scaffold(appBar: AppBar(title: Text('ChangeNotifier使用演示')),body: Center(child: TestNotifierWidget(controller: controller),),floatingActionButton: FloatingActionButton(onPressed: () {controller.value = '数值变化:${(++count).toString()}';},child: Icon(Icons.add),),);}
}
  • 来看下效果图

Function Call()

这里说个小知识点,源码里面大量使用了这个技巧,网上搜了下,很少提到这个的,这边记一笔

每个Function都有个Call()方法

  • 下面俩种方式调用是等同的,都能调用test方法
void main(){test();test.call();
}void test(){print('test');
}

你可能想,这有什么用,我还多写一个 .call ?

来看下一个小范例,就知道这个东西能帮我们简化很多代码

  • 平时封装带有CallBack回调Widget

    • 这边写了俩个自定义的点击回调判断操作
    • 如果不做判空操作,外部未实现这个Function,点击事件会报空异常
class TestWidget extends StatelessWidget {const TestWidget({Key? key,this.onTap,this.onBack,}) : super(key: key);final VoidCallback? onTap;final VoidCallback? onBack;@overrideWidget build(BuildContext context) {return GestureDetector(onTap: () {if (onTap != null) {onTap!();}if (onBack != null) {onBack!();}},child: Container(),);}
}
  • 使用 .call() 后,可以怎么写呢?

    • 可以干掉麻烦的if判空操作了!
class TestWidget extends StatelessWidget {const TestWidget({Key? key,this.onTap,this.onBack,}) : super(key: key);final VoidCallback? onTap;final VoidCallback? onBack;@overrideWidget build(BuildContext context) {return GestureDetector(onTap: () {onTap?.call();onBack?.call();},child: Container(),);}
}

刷新机制

Provider的刷新机制是非常重要的,只要把Provider的刷新机制搞清楚,这个框架在你面前,将不在神秘!

实际上,大家只要看到ChangeNotifier的应用,那肯定知道,这就是个观察者模式,但是问题是:它的监听在何处添加?添加的监听逻辑是否有完整的初始化链路?监听逻辑是什么?为什么触发监听逻辑,能导致相应控件刷新?

  • 上面初始化的完整链路看的真是有点蛋痛

    • 源码东一榔锤西一棒的,而且还用了大量了抽象类,想直接定位逻辑,那是不可能的,你必须找到实现类赋值的地方,才能明白内部运转
    • 不搞清楚完整初始化链路,内心就相当于膈应,明知道他肯定初始化了,却不知道他在哪初始化的,就很难受
    • 我下面将相关流程理了一遍,希望对大家有所帮助
  • 要读懂Provider,必须要有个前提,明白什么观察者模式:观察者模式其实很简单,简单描述下
    • 定义个List类型,泛型为一个抽象类,初始化这个List
    • 然后给这个List,add这个抽象类的实现类实例
    • 某个合适时候,遍历这个List所有实例,触发所有实例的某个方法
    • 如果将这个思想和反射注解结合在一起,就能大大拓宽它的使用面,例如android里的EventBus。。。

总流程

继承ChangeNotifier的类,是通过ChangeNotifierProvider传入到Provider内部,很明显ChangeNotifierProvider这个类很重要,基本可以算是框架的主入口

这边梳理下ChangeNotifierProvider 回溯的总流程,其它的旁枝末节,暂时不贴代码,这个往上回溯的过程,实例了一个很重要的上下文类,很多关键的类初始化都和这个上下文类有关系,先来回溯下这个重要的流程!

  • ChangeNotifierProvider

    • 这地方有个_dispose回调,是定义好的,内部逻辑是回收ChangeNotifier实例
    • 这里将该方法赋值给了他的父类ListenableProvider,然后一层层往上回溯
class ChangeNotifierProvider<T extends ChangeNotifier?> extends ListenableProvider<T> {ChangeNotifierProvider({Key? key,required Create<T> create,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,create: create,dispose: _dispose,lazy: lazy,builder: builder,child: child,);...static void _dispose(BuildContext context, ChangeNotifier? notifier) {notifier?.dispose();}
}
  • ListenableProvider

    • 这地方有个_startListening回调,这个方法极其重要
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {ListenableProvider({Key? key,required Create<T> create,Dispose<T>? dispose,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,startListening: _startListening,create: create,dispose: dispose,lazy: lazy,builder: builder,child: child,);  ...static VoidCallback _startListening(InheritedContext e, Listenable? value,) {value?.addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);}
}
  • InheritedProvider

    • 这个类就是逻辑的纠缠点了:我省略了大量和主流程无关的代码,不然会十分影响你的关注点,会很难受
    • 这里就不需要看他的父类了,他的父类是SingleChildStatelessWidget,这个类是对StatelessWidget类的一个封装,能稍微优化下嵌套问题,无关紧要
    • 需要看下buildWithChild(看成StatelessWidget的build方法就行了)方法里面的_InheritedProviderScope类,来看下他的源码
class InheritedProvider<T> extends SingleChildStatelessWidget {InheritedProvider({Key? key,Create<T>? create,T Function(BuildContext context, T? value)? update,UpdateShouldNotify<T>? updateShouldNotify,void Function(T value)? debugCheckInvalidValueType,StartListening<T>? startListening,Dispose<T>? dispose,this.builder,bool? lazy,Widget? child,})  : _lazy = lazy,_delegate = _CreateInheritedProvider(create: create,update: update,updateShouldNotify: updateShouldNotify,debugCheckInvalidValueType: debugCheckInvalidValueType,startListening: startListening,dispose: dispose,),super(key: key, child: child);...final _Delegate<T> _delegate;final bool? _lazy;final TransitionBuilder? builder;...@overrideWidget buildWithChild(BuildContext context, Widget? child) {...return _InheritedProviderScope<T>(owner: this,debugType: kDebugMode ? '$runtimeType' : '',child: builder != null? Builder(builder: (context) => builder!(context, child),): child!,);}
}
  • _InheritedProviderScope

    • 这里是继承了InheritedWidget,里面重写createElement方法,在构建Widget的时候,这个方法是肯定会被调用的!
    • 马上就要到最重要的类了,就是createElement中实例化的_InheritedProviderScopeElement类!
class _InheritedProviderScope<T> extends InheritedWidget {const _InheritedProviderScope({required this.owner,required this.debugType,required Widget child,}) : super(child: child);final InheritedProvider<T> owner;final String debugType;@overridebool updateShouldNotify(InheritedWidget oldWidget) {return false;}@override_InheritedProviderScopeElement<T> createElement() {return _InheritedProviderScopeElement<T>(this);}
}
  • _InheritedProviderScopeElement:实现方法里面的逻辑全省略了,逻辑太多,看着头晕

    • 先说明下,这个类是极其极其重要的!大家可以看下他实现了一个什么抽象类:InheritedContext!
    • InheritedContext继承了BuildContext,也就是说,这里作者实现了BuildContext所有抽象方法
      • 是的,BuildContext也是个抽象类,我们可以去实现多个不同实现类
      • 内部系统只需要特定的周期去触发相应方法,就可以了
      • 你可以在相应的方法里面实现自己的逻辑,大大的扩展了逻辑,怎么说呢?有点策略模式味道,可以动态替换实现类
    • _InheritedProviderScopeElement算是实现了:InheritedContext和BuildContext;BuildContext中有很多方法是和控件生命周期挂钩的,例如热重载触发(reassemble),setState触发(build、performRebuild)、以及很有意思的强制依赖项组件刷新(markNeedsNotifyDependents:这是Provider作者在InheritedContext中抽象的方法)。。。
abstract class InheritedContext<T> extends BuildContext {T get value;void markNeedsNotifyDependents();bool get hasValue;
}class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);...@overridevoid mount(Element? parent, dynamic newSlot) {...}@override_InheritedProviderScope<T> get widget => super.widget as _InheritedProviderScope<T>;@overridevoid reassemble() {...}@overridevoid updateDependencies(Element dependent, Object? aspect) {...}@overridevoid notifyDependent(InheritedWidget oldWidget, Element dependent) {...}@overridevoid performRebuild() {...}@overridevoid update(_InheritedProviderScope<T> newWidget) {...}@overridevoid updated(InheritedWidget oldWidget) {...}@overridevoid didChangeDependencies() {...}@overrideWidget build() {...}@overridevoid unmount() {...}@overridebool get hasValue => _delegateState.hasValue;@overridevoid markNeedsNotifyDependents() {...}bool _debugSetInheritedLock(bool value) {...}@overrideT get value => _delegateState.value;@overrideInheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect,}) {...}@overridevoid debugFillProperties(DiagnosticPropertiesBuilder properties) {...}
}

上面进行了五步的回溯流程,如果不仔细看清楚相关类里面的逻辑,很可能就迷失在super方法里。。。

通过上面的五步回溯,我们可以断定一个事实:_InheritedProviderScopeElement(实现BuildContext) 被实例化了,而且他在初始化的时候被调用了,对应的,其内部相应的周期也能被正常触发!这样之前看源码困扰我的很多问题,就迎刃而解了!

  • 图示

    • 上面回溯的层级过多,还有很多的继承和实现
    • 看了后,脑中可能没啥印象,所以此处画了流程图,可以参照对比

添加监听

整个刷新机制里面有个相当重要的一环,我们从Create中传入的类,它内部是怎么处理的?

class ProEasyCounterPage extends StatelessWidget {final provider = ProEasyCounterProvider();@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (BuildContext context) => provider,child: Container(),);}
}

就算没看源码,我也能断定传入的XxxProvider实例,肯定使用了其本身的addListener方法!

但是找这个addListener方法,实在让我找自闭了,之前因为没梳理总流程,对其初始化链路不明晰,找到了addListener方法,我都十分怀疑,是不是找对了、其它地方是不是还有addListener方法;后来没办法,就把Provider源码下载下来(之前直接项目里面点Provider插件源码看的),全局搜索addListener方法,排除所有的测试类中使用的,然后断定我找对了,整个添加监听的链路是通顺的!

下面来整体的带大家过一遍源码

靓仔们,我要开始绕了!!!

流转

  • ChangeNotifierProvider

    • 明确下Create是一个Function,返回继承ChangeNotifier类的实例
    • 这里一定要记住create这个变量的走向,其中的T就是继承ChangeNotifier类的关键类
    • 增加了_dispose方法,传给了父类
    • create这里super给其父类,回溯下父类
typedef Create<T> = T Function(BuildContext context);class ChangeNotifierProvider<T extends ChangeNotifier?> extends ListenableProvider<T> {ChangeNotifierProvider({Key? key,required Create<T> create,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,create: create,dispose: _dispose,lazy: lazy,builder: builder,child: child,);...static void _dispose(BuildContext context, ChangeNotifier? notifier) {notifier?.dispose();}
}
  • ListenableProvider

    • 此处将create实例super给了父类
    • 还增加一个_startListening方法,也同样给了父类
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {ListenableProvider({Key? key,required Create<T> create,Dispose<T>? dispose,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,startListening: _startListening,create: create,dispose: dispose,lazy: lazy,builder: builder,child: child,);...static VoidCallback _startListening(InheritedContext e, Listenable? value,) {value?.addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);}
}
  • InheritedProvider

    • 这地方和上面总流程不太一样了
    • create、dispose、startListening传给了_CreateInheritedProvider
    • 需要看下_CreateInheritedProvider
class InheritedProvider<T> extends SingleChildStatelessWidget {InheritedProvider({Key? key,Create<T>? create,T Function(BuildContext context, T? value)? update,UpdateShouldNotify<T>? updateShouldNotify,void Function(T value)? debugCheckInvalidValueType,StartListening<T>? startListening,Dispose<T>? dispose,this.builder,bool? lazy,Widget? child,})  : _lazy = lazy,_delegate = _CreateInheritedProvider(create: create,update: update,updateShouldNotify: updateShouldNotify,debugCheckInvalidValueType: debugCheckInvalidValueType,startListening: startListening,dispose: dispose,),super(key: key, child: child);...
}
  • 流程图示

_CreateInheritedProvider

这地方会进行一个很重要的回溯流程,回溯到_InheritedProviderScopeElement

下次再有需要用到这个类,就直接拿这个类来讲了

  • _CreateInheritedProvider说明

    • _CreateInheritedProvider继承了抽象类 _Delegate,实现了其createState抽象方法
    • 按理说,主要逻辑肯定在createState方法中 _CreateInheritedProviderState 实例中
    • 必须要看下_CreateInheritedProvider实例,在何处调用 createState方法,然后才能继续看 _CreateInheritedProviderState的逻辑
@immutable
abstract class _Delegate<T> {_DelegateState<T, _Delegate<T>> createState();void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
}class _CreateInheritedProvider<T> extends _Delegate<T> {_CreateInheritedProvider({this.create,this.update,UpdateShouldNotify<T>? updateShouldNotify,this.debugCheckInvalidValueType,this.startListening,this.dispose,})  : assert(create != null || update != null),_updateShouldNotify = updateShouldNotify;final Create<T>? create;final T Function(BuildContext context, T? value)? update;final UpdateShouldNotify<T>? _updateShouldNotify;final void Function(T value)? debugCheckInvalidValueType;final StartListening<T>? startListening;final Dispose<T>? dispose;@override_CreateInheritedProviderState<T> createState() =>_CreateInheritedProviderState();
}
  • 这里需要重新回顾下InheritedProvider类

    • 这地方做了一个很重要的操作,将_CreateInheritedProvider实例赋值给 _delegate
    • buildWithChild方法中_InheritedProviderScope的owner接受了InheritedProvider本身的实例
    • 结合这俩个就有戏了,再来看下_InheritedProviderScope类
class InheritedProvider<T> extends SingleChildStatelessWidget {InheritedProvider({Key? key,Create<T>? create,T Function(BuildContext context, T? value)? update,UpdateShouldNotify<T>? updateShouldNotify,void Function(T value)? debugCheckInvalidValueType,StartListening<T>? startListening,Dispose<T>? dispose,this.builder,bool? lazy,Widget? child,})  : _lazy = lazy,_delegate = _CreateInheritedProvider(create: create,update: update,updateShouldNotify: updateShouldNotify,debugCheckInvalidValueType: debugCheckInvalidValueType,startListening: startListening,dispose: dispose,),super(key: key, child: child);final _Delegate<T> _delegate;final bool? _lazy;...@overrideWidget buildWithChild(BuildContext context, Widget? child) {,,,return _InheritedProviderScope<T>(owner: this,debugType: kDebugMode ? '$runtimeType' : '',child: builder != null? Builder(builder: (context) => builder!(context, child),): child!,);}
}
  • _InheritedProviderScope

    • createElement方法传入_InheritedProviderScope本身的实例
    • 关键的在_InheritedProviderScopeElement类中
class _InheritedProviderScope<T> extends InheritedWidget {const _InheritedProviderScope({required this.owner,required this.debugType,required Widget child,}) : super(child: child);final InheritedProvider<T> owner;final String debugType;@overridebool updateShouldNotify(InheritedWidget oldWidget) {return false;}@override_InheritedProviderScopeElement<T> createElement() {return _InheritedProviderScopeElement<T>(this);}
}
  • _InheritedProviderScopeElement类,我就直接精简到关键代码了

    • 有没有感觉InheritedWidget很像StatefulWidget,实际他俩最终都是继承Widget,未对Widget的建造者模式那层封装,所以有俩层结构;而StatelessWidget将建造者模式那层进行了封装,所以只有一层结构
    • 下面的关键代码看到没! widget.owner._delegate.createState() … 这地方调用了_CreateInheritedProvider类的createState() 方法,安心了
    • performRebuild:该回调会在setState或者build的时候会触发;此处做了一个判断,只会在第一次build的时候触发
    • 这里可以确定_CreateInheritedProvider类中的createState方法一定会被调用;接下来看看其方法里面调用的 _CreateInheritedProviderState类
class _InheritedProviderScopeElement<T> extends InheritedElementimplements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);...@overridevoid performRebuild() {if (_firstBuild) {_firstBuild = false;_delegateState = widget.owner._delegate.createState()..element = this;}super.performRebuild();}...
}
  • 流程图示

_InheritedProviderScopeElement

  • _CreateInheritedProviderState:这个类做了很多事情,很多的主体逻辑的都在此处理

    • 该类代码很多,此处只留下我们需要关注的代码,因为省略了很多代码,从下面的主体代码来看,流程就清楚了:create、startListening、dispose 都有
    • 但是这些变量是依附在delegate上的,这个delegate是个啥?需要看下继承的抽象类 _DelegateState
class _CreateInheritedProviderState<T> extends _DelegateState<T, _CreateInheritedProvider<T>> {VoidCallback? _removeListener;bool _didInitValue = false;T? _value;_CreateInheritedProvider<T>? _previousWidget;@overrideT get value {...if (!_didInitValue) {_didInitValue = true;if (delegate.create != null) {assert(debugSetInheritedLock(true));try {..._value = delegate.create!(element!);} finally {...}...}...}element!._isNotifyDependentsEnabled = false;_removeListener ??= delegate.startListening?.call(element!, _value as T);element!._isNotifyDependentsEnabled = true;assert(delegate.startListening == null || _removeListener != null);return _value as T;}@overridevoid dispose() {super.dispose();_removeListener?.call();if (_didInitValue) {delegate.dispose?.call(element!, _value as T);}}...
}
  • _DelegateState

    • delegate是通过 _InheritedProviderScopeElement的实例获取到了owner然后获取到了 _delegate变量
    • _delegate这个变量是在InheritedProvider类中的实例化 _CreateInheritedProvider赋值给他的,不信的话,可以返回去看看
    • 好吉尔绕!!!
abstract class _DelegateState<T, D extends _Delegate<T>> {_InheritedProviderScopeElement<T>? element;T get value;D get delegate => element!.widget.owner._delegate as D;bool get hasValue;bool debugSetInheritedLock(bool value) {return element!._debugSetInheritedLock(value);}bool willUpdateDelegate(D newDelegate) => false;void dispose() {}void debugFillProperties(DiagnosticPropertiesBuilder properties) {}void build({required bool isBuildFromExternalSources}) {}
}
  • element

    • 现在还有个问题,element这个变量在哪实例化的?怎么大家这么随便用它!就不怕它为空吗?
    • 直接带大家来_InheritedProviderScopeElement里面看了,上面已经回顾了到这个必定实例化这个上下文类的流程
    • performRebuild回调中,在调用createState()方法的时候,给element赋值了,element = this
    • 所以在_CreateInheritedProviderState类中,可以随便使用element 这个变量,他的值肯定不为空!
class _InheritedProviderScopeElement<T> extends InheritedElementimplements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);...@overridevoid performRebuild() {if (_firstBuild) {_firstBuild = false;_delegateState = widget.owner._delegate.createState()..element = this;}super.performRebuild();}...
}

不知道大家对这流程有没有个清晰的印象

  • 来看看这山路十八弯的初始化链路图

_CreateInheritedProviderState

有了上面分析出的element和_delegate不为空的,且 _delegate能直接访问 _CreateInheritedProvider这个实例基础,再来看下 _CreateInheritedProviderState代码

  1. get 流程

    1. 我们传入的create会直接赋值给 _value,现在这个 _value,就是我们在外面传进来的那个XxxProvider实例了!
    2. 底下也调用了 startListening,说明从外面传进来的这个回调也调用了,将 上下文实例传进来的XxxProvider实例 作为入参传进了这个回调中,此处传进来的回调也通过 .call 被调用了!
  2. dispose 流程
    1. 调用startListening方法时,该方法会返回一个移除监听Function
    2. 移除监听的Function在dispose时被调用,移除给XxxProvider添加的监听
    3. 从外部传入的dispose方法,也在此处被执行
    4. OK!回收资源的操作在此处都搞定了!
class _CreateInheritedProviderState<T> extends _DelegateState<T, _CreateInheritedProvider<T>> {VoidCallback? _removeListener;bool _didInitValue = false;T? _value;_CreateInheritedProvider<T>? _previousWidget;@overrideT get value {...if (!_didInitValue) {_didInitValue = true;if (delegate.create != null) {assert(debugSetInheritedLock(true));try {..._value = delegate.create!(element!);} finally {...}...}...}element!._isNotifyDependentsEnabled = false;_removeListener ??= delegate.startListening?.call(element!, _value as T);element!._isNotifyDependentsEnabled = true;assert(delegate.startListening == null || _removeListener != null);return _value as T;}@overridevoid dispose() {super.dispose();_removeListener?.call();if (_didInitValue) {delegate.dispose?.call(element!, _value as T);}}...
}
  • 关键的就是startListening回调了,来看下他的逻辑

    • _startListening在此处 addListener 了!ChangeNotifier 是 Listenable 实现类,姑且把它当成访问者模式也可,所以这个value就是我们从外面传进来的 XxxProvider
    • 返回了一个VoidCallback的Function,里面是移除监听逻辑
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {ListenableProvider({Key? key,required Create<T> create,Dispose<T>? dispose,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,startListening: _startListening,create: create,dispose: dispose,lazy: lazy,builder: builder,child: child,);...static VoidCallback _startListening(InheritedContext e, Listenable? value,) {value?.addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);}
}

还有最后一个问题!!!

需要调用_startListening方法,必须调用 _CreateInheritedProviderState类里面的 get value

在哪个初始化入口,使用这个 get value 呢?

  • 这里直接给出结论了,还是在 _InheritedProviderScopeElement这个上下文类里面

    • reassemble:全局状态的初始化逻辑或热重载的时候被调用
    • _delegateState首先在performRebuild回调中会赋初值
    • 在reassemble回调中,_delegateState调用了value( _delegateState.value )
    • 所以 get value 肯定会在初始化的时候被调用,上面流程是通顺的
class _InheritedProviderScopeElement<T> extends InheritedElementimplements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);late _DelegateState<T, _Delegate<T>> _delegateState;...@overridevoid performRebuild() {if (_firstBuild) {_firstBuild = false;_delegateState = widget.owner._delegate.createState()..element = this;}super.performRebuild();}@overridevoid reassemble() {super.reassemble();final value = _delegateState.hasValue ? _delegateState.value : null;if (value is ReassembleHandler) {value.reassemble();}}...
}

总结

上面分析完了添加监听,以及相关的初始化链路和调用链路

  • 可以把流程图整全了,来看看

刷新逻辑

刷新逻辑也是相当之绕啊;本菜比,各种debug,在framework里面各种打断点,终于把流程理通了!我突然感觉自己打通了任督二脉!

作者为了实现这个刷新逻辑,和系统api做了大量的交互,相当的精彩!

我会尽力将这个精彩纷呈的操作,展现给大家!

触发

  • ListenableProvider

    • 这地方逻辑很简单,添加了InheritedContext这个上下文类中的markNeedsNotifyDependents方法
    • 说明,我们在外部使用notifyListeners() 的时候,一定会触发InheritedContext实现类中的markNeedsNotifyDependents方法
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {ListenableProvider({Key? key,required Create<T> create,Dispose<T>? dispose,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,startListening: _startListening,create: create,dispose: dispose,lazy: lazy,builder: builder,child: child,);...static VoidCallback _startListening(InheritedContext e, Listenable? value,) {value?.addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);}
}
  • _InheritedProviderScopeElement: _InheritedProviderScopeElement是InheritedContext的实现类

    • 还是要来这个类看看,只保留了和markNeedsNotifyDependents有关的代码
    • markNeedsNotifyDependents回调作用,总的来说:会将强制依赖于T窗口小部件进行重建
    • 说的这么笼统没啥用,下面会全面分析,他是怎么做到让依赖于T窗口小部件进行重建的! 我想了下,还是观察者模式的应用。。。
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);...@overridevoid markNeedsNotifyDependents() {if (!_isNotifyDependentsEnabled) {return;}markNeedsBuild();_shouldNotifyDependents = true;}...
}

刷新流程

咱们现在来理一下刷新的流程!

  • markNeedsNotifyDependents

    • 当我们使用 notifyListeners(),就会触发,这个回调
    • 此处调用了 markNeedsBuild(),然后给 _shouldNotifyDependents 设置为true
    • 必备操作,来看下 markNeedsBuild() 作用
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);bool _shouldNotifyDependents = false;...@overridevoid markNeedsNotifyDependents() {if (!_isNotifyDependentsEnabled) {return;}markNeedsBuild();_shouldNotifyDependents = true;}...
}
  • markNeedsBuild

    • _InheritedProviderScopeElement最终继承的还是Element抽象类,markNeedsBuild()方法是Element中的
    • Element类是一个实现了BuildContext抽象类中抽象方法的抽象类,该类十分重要
    • 这个方法花里胡哨的代码写了一大堆,他最主要的功能:就是会调用Element的performRebuild()方法,然后触发ComponentElement的build()方法,最终触发_InheritedProviderScopeElement的build方法
    • _InheritedProviderScopeElement extends InheritedElement extends ProxyElement extends ComponentElement extends Element
abstract class Element extends DiagnosticableTree implements BuildContext {...void markNeedsBuild() {assert(_lifecycleState != _ElementLifecycle.defunct);if (_lifecycleState != _ElementLifecycle.active)return;assert(owner != null);assert(_lifecycleState == _ElementLifecycle.active);assert(() {if (owner!._debugBuilding) {assert(owner!._debugCurrentBuildTarget != null);assert(owner!._debugStateLocked);if (_debugIsInScope(owner!._debugCurrentBuildTarget!))return true;if (!_debugAllowIgnoredCallsToMarkNeedsBuild) {final List<DiagnosticsNode> information = <DiagnosticsNode>[ErrorSummary('setState() or markNeedsBuild() called during build.'),ErrorDescription('This ${widget.runtimeType} widget cannot be marked as needing to build because the framework ''is already in the process of building widgets.  A widget can be marked as ''needing to be built during the build phase only if one of its ancestors ''is currently building. This exception is allowed because the framework ''builds parent widgets before children, which means a dirty descendant ''will always be built. Otherwise, the framework might not visit this ''widget during this build phase.',),describeElement('The widget on which setState() or markNeedsBuild() was called was',),];if (owner!._debugCurrentBuildTarget != null)information.add(owner!._debugCurrentBuildTarget!.describeWidget('The widget which was currently being built when the offending call was made was'));throw FlutterError.fromParts(information);}assert(dirty); // can only get here if we're not in scope, but ignored calls are allowed, and our call would somehow be ignored (since we're already dirty)} else if (owner!._debugStateLocked) {assert(!_debugAllowIgnoredCallsToMarkNeedsBuild);throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.'),ErrorDescription('This ${widget.runtimeType} widget cannot be marked as needing to build ''because the framework is locked.',),describeElement('The widget on which setState() or markNeedsBuild() was called was'),]);}return true;}());if (dirty)return;_dirty = true;owner!.scheduleBuildFor(this);}...
}
  • build

    • 这里说明下,这个子类调用父类方法,然后父类调用自身方法,是先触发这个子类的重写方法,然后可以通过 super. 的方式去执行父类逻辑
    • 上面给_shouldNotifyDependents设置为true,所以build内部逻辑会执行notifyClients(widget)方法
    • 接下来看下notifyClients(widget)方法
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);bool _shouldNotifyDependents = false;...@overrideWidget build() {if (widget.owner._lazy == false) {value; // this will force the value to be computed.}_delegateState.build(isBuildFromExternalSources: _isBuildFromExternalSources,);_isBuildFromExternalSources = false;if (_shouldNotifyDependents) {_shouldNotifyDependents = false;notifyClients(widget);}return super.build();}...
}
  • notifyClients:notifyClients()是InheritedElement类中的,notifyClients()方法是ProxyElement类中的一个抽象方法,InheritedElement在此处做了一个实现

    1. notifyClients()是一个非常非常重要的方法,它内部有个for循环,遍历了_dependents这个HashMap类型的所有key值, _dependents的key是Element类型

      1. 什么是Element?它可以表示为Widget在树中特定位置的实例,一个Element可以形成一棵树(想想每个Container都有Element,然后其child再套其它的widget,这样就形成了一颗树)
      2. Element在此处将其理解为:本身Widget和其子节点形成的树,Element是这棵树的头结点,这特定位置的节点是实例化的,对这个特定位置的实例节点操作,会影响到他的子节点
      3. Widget的createElement()方法会实例化Element
    2. 这地方遍历_dependents的key取Element,可以猜测:他肯定是想取某个元素或者说某个Widget
    3. 取到相关Element实例后,她会传入notifyDependent(oldWidget, dependent)方法中
    4. 接下来,需要看看notifyDependent(oldWidget, dependent)方法逻辑了
class InheritedElement extends ProxyElement {final Map<Element, Object?> _dependents = HashMap<Element, Object?>();...@overridevoid notifyClients(InheritedWidget oldWidget) {assert(_debugCheckOwnerBuildTargetExists('notifyClients'));for (final Element dependent in _dependents.keys) {assert(() {// check that it really is our descendantElement? ancestor = dependent._parent;while (ancestor != this && ancestor != null)ancestor = ancestor._parent;return ancestor == this;}());// check that it really depends on usassert(dependent._dependencies!.contains(this));notifyDependent(oldWidget, dependent);}}
}
  • notifyDependent

    • if (dependencies is _Dependency) 这判断的逻辑题里面还有很多逻辑,是作者在BuildContext上面搞了一个select扩展方法(判断是否需要刷新),但和现在讲了刷新流程无关,我在里面绕了好久,凎!
    • 去掉上面的逻辑就简单了,shouldNotify赋值为true,最后调用dependent.didChangeDependencies()
    • dependent还记得是啥吗?是父类里面循环取得的Element实例
    • 这地方直接去掉super操作,这也是系统建议的,我们可以重写notifyDependent方法,自定义相关逻辑;因为有时我们需要可选择性的调用dependent.didChangeDependencies()!
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {_InheritedProviderScopeElement(_InheritedProviderScope<T> widget): super(widget);...@overridevoid notifyDependent(InheritedWidget oldWidget, Element dependent) {final dependencies = getDependencies(dependent);if (kDebugMode) {ProviderBinding.debugInstance.providerDidChange(_debugId);}var shouldNotify = false;if (dependencies != null) {if (dependencies is _Dependency<T>) {...} else {shouldNotify = true;}}if (shouldNotify) {dependent.didChangeDependencies();}}...
}
  • didChangeDependencies

    • didChangeDependencies逻辑就很简单了,会调用markNeedsBuild()
    • 可以理解为:最终会调用该Widget的build方法
    • markNeedsBuild()就不讲了,内部涉及逻辑太多了,还涉及bind类,还会涉及到绘制流程,我嘞个去。。。
abstract class Element extends DiagnosticableTree implements BuildContext {...@mustCallSupervoid didChangeDependencies() {assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-opassert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));markNeedsBuild();}...
}

现在有个超纠结的事情,这个点关乎整个刷新流程的枢纽!

InheritedElement中的_dependents这个map的key是Element,这个Element是什么?上面所有流程都是为了调用 _dependents这个Map中key(Element)的markNeedsBuild()方法,最终是为了调用这个Element的Widget的build方法!

大家明白了吗?我们就算大胆去蒙,去猜,去赌,这个Widget十有八九就是Consumer这类刷新Widget啊!

但是!但是!他到底是怎么将这类刷新Widget添加到InheritedElement的 _dependents变量中的呢 !?

  • 上述流程图示

BuildContext

插播一个小知识点,这个知识和下述内容相关,这边先介绍一下

BuildContext是什么?

  • BuildContext

    • 每个抽象方法上面注释超级多,我删掉了(占篇幅),有兴趣的可以自己去源码里看看
    • BuildContext就是抽象类,是约定好的一个抽象类,相关方法的功能已经被约定,你如果想实现这个抽象类类,相关方法功能实现可以有出入,但不应该偏离抽象方法注释所描述的功能范围
abstract class BuildContext {Widget get widget;BuildOwner? get owner;bool get debugDoingBuild;RenderObject? findRenderObject();Size? get size;InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();T? findAncestorWidgetOfExactType<T extends Widget>();T? findAncestorStateOfType<T extends State>();T? findRootAncestorStateOfType<T extends State>();T? findAncestorRenderObjectOfType<T extends RenderObject>();void visitAncestorElements(bool Function(Element element) visitor);void visitChildElements(ElementVisitor visitor);DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});List<DiagnosticsNode> describeMissingAncestor({ required Type expectedAncestorType });DiagnosticsNode describeOwnershipChain(String name);
}
  • StatelessWidget:看下StatelessWidget对BuildContext的实现(StatefulWidget同理,不贴了)

    • 代码超级简单,StatelessWidget抽象了build方法,入参为BuildContext
    • createElement()方法实例了StatelessElement类,并将StatelessWidget本身实例传入
    • StatelessElement里面实现了ComponentElement的build方法:该方法调用了widget里面的build方法,并将本身的实例传入,流程通了,此处调用StatelessWidget的build方法,并传入了BuildContext的实现类
    • ComponentElement的父类中肯定有实现BuildContext,往上看看
abstract class StatelessWidget extends Widget {const StatelessWidget({ Key? key }) : super(key: key);@overrideStatelessElement createElement() => StatelessElement(this);@protectedWidget build(BuildContext context);
}class StatelessElement extends ComponentElement {StatelessElement(StatelessWidget widget) : super(widget);@overrideStatelessWidget get widget => super.widget as StatelessWidget;@overrideWidget build() => widget.build(this);@overridevoid update(StatelessWidget newWidget) {super.update(newWidget);assert(widget == newWidget);_dirty = true;rebuild();}
}
  • ComponentElement

    • ComponentElement继承Element,它抽象了一个build方法,StatelessElement实现了这个方法,没毛病
    • 来看看Element
abstract class ComponentElement extends Element {...@protectedWidget build();...
}
  • Element

    • Element此处实现了BuildContext,所以继承他的子类,直接将本身实例传给BuildContext就OK了
    • 如果没做什么骚操作,BuildContext可以理解为:每个Widget都有对应的Element( 通过createElement()生成 ),Element是BuildContext实现类
abstract class Element extends DiagnosticableTree implements BuildContext {...
}
  • Widget

    • Widget抽象了一个createElement()方法
    • 每个Widget的子类,理应都有自己对应的Element
@immutable
abstract class Widget extends DiagnosticableTree {const Widget({ this.key });final Key? key;@protected@factoryElement createElement();...
}
  • 图示

  • 关于Widget和Element再多说俩句

知道为什么好多文章说Widget对Element是一对多吗?

首先Widget是Element的一个配置描述,我们通过类似StatelessElement createElement() => StatelessElement(this),将widget本身的配置信息实例传入XxxElemen(this)中,然后XxxElement可以通过传入的Widget配置信息去生成对应的Element实例

大家发现没?每一个Widget都有对应的Element实例!

假设写了下面这个Widget

Widget _myWidget({Widget child}){return Container(width:30, height:30, child:child);
}
  • 咱们这样用
_myWidget(child: Container(child: _myWidget(),)
)

这不就对了嘛,只有一份Widget配置信息,但是会生成俩个Element!

但是还是会有俩个Widget实例,但从配置信息层次上看,俩个Widget实例的配置信息都是一样的,所以是一份配置信息。。。

所以就有了Widget对Element是一对多的说法;反正我是这样理解的,仅供参考。。。

可能大佬们写文章,这些简单实例脑子自然生成,但是对这些没啥概念的靓仔,这或许就成了:一条定理或者既定概念

神奇的Provider.of()

为了将上面的流程连接起来,需要一位神奇的魔术师登场,下面就要请上我们的王炸:Provider.of() !

将刷新组件添加到了InheritedElement中的_dependents变量里,他到底是怎么做到的呢?

  • Provider.of() :下面就是该方法所有的逻辑,代码很少,实现的功能却很强!

    1. of方法中,会通过 _inheritedElementOf(context)方法获取到,和当前Widget距离最近的(往父节点遍历)继承InheritedElement的XxxElement
    2. 上面是通过 _inheritedElementOf(context)方法中的 context.getElementForInheritedWidgetOfExactType()方法去获取的;继承InheritedElement的Widget的子节点,是可以通过这个方法去拿到距离他最近的继承InheritedElement的Widget的XxxElement实例,同样的,也可以获取其中储存的数据
    3. 你可能想,我拿到 继承InheritedElement的XxxElement的实例有啥?咱好好想想:我们拿到这个XxxElement实例后,我们不就可以往它的父类InheritedElement里面的 _dependents的map变量塞值了吗?狂喜…
    4. 它是怎么做到的呢?就是通过这个:context.dependOnInheritedElement(inheritedElement)
static T of<T>(BuildContext context, {bool listen = true}) {...final inheritedElement = _inheritedElementOf<T>(context);if (listen) {context.dependOnInheritedElement(inheritedElement);}return inheritedElement.value;
}static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(BuildContext context) {..._InheritedProviderScopeElement<T>? inheritedElement;if (context.widget is _InheritedProviderScope<T>) {context.visitAncestorElements((parent) {inheritedElement = parent.getElementForInheritedWidgetOfExactType<_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>?;return false;});} else {inheritedElement = context.getElementForInheritedWidgetOfExactType<_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>?;}if (inheritedElement == null) {throw ProviderNotFoundException(T, context.widget.runtimeType);}return inheritedElement!;
}

context.getElementForInheritedWidgetOfExactType()

这个api是怎么拿到父节点的InheritedElement的呢?

  • Element:因为Element是BuildContext实现类,所以直接来Element看逻辑就行了

    • getElementForInheritedWidgetOfExactType返回是 _inheritedWidgets变量
    • _inheritedWidgets的key:是需要找的继承InheritedWidget的Widget
    • _updateInheritance:方法是将父节点 _inheritedWidgets 对象赋值给当前Element的 _inheritedWidgets变量
    • 现只需要看下,有什么地方给_inheritedWidgets这Map塞值就行了
abstract class Element extends DiagnosticableTree implements BuildContext {...@mustCallSupervoid mount(Element? parent, dynamic newSlot) {..._updateInheritance();}Map<Type, InheritedElement>? _inheritedWidgets;...@overrideInheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {assert(_debugCheckStateIsActiveForAncestorLookup());final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];return ancestor;}void _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);_inheritedWidgets = _parent?._inheritedWidgets;}...
}
  • InheritedElement

    • 齐活了: _inheritedWidgets![widget.runtimeType] = this
    • InheritedElement中给自己父类Element的 _inheritedWidgets变量塞值了(ProxyElement最终继承还是Element)
    • 父节点会将 _inheritedWidgets变量,一级一级的赋值给子节点Element的 _inheritedWidgets 变量
    • 可以发现:寻找父节点的InheritedElement,耗时极短,只需要从Map里面去拿值就行了
class InheritedElement extends ProxyElement {...@overridevoid _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;if (incomingWidgets != null)_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);else_inheritedWidgets = HashMap<Type, InheritedElement>();_inheritedWidgets![widget.runtimeType] = this;}...
}

dependOnInheritedElement

  • 关于BuildContext上面已经说过了,我们直接去Element类里面找dependOnInheritedElement方法,看看他的实现逻辑
  • 直接看最重要的代码 ancestor.updateDependencies(this, aspect):我们传入的继承了InheritedElement的XxxElement,被传入了updateDependencies方法,然后他还将当前Widget的Element实例传入了updateDependencies方法中
abstract class Element extends DiagnosticableTree implements BuildContext {...@overrideInheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {assert(ancestor != null);_dependencies ??= HashSet<InheritedElement>();_dependencies!.add(ancestor);ancestor.updateDependencies(this, aspect);return ancestor.widget;}...
}
  • updateDependencies:流程终于完整的跑通了!

    • updateDependencies方法调用了setDependencies方法
    • setDependencies方法,将子Widget的Element实例赋值给了继承InheritedElement的类的 _dependents 变量
class InheritedElement extends ProxyElement {...@protectedvoid setDependencies(Element dependent, Object? value) {_dependents[dependent] = value;}@protectedvoid updateDependencies(Element dependent, Object? aspect) {setDependencies(dependent, null);}...
}
  • 看下图示:这图调了好久,不规划下,线很容易交叉,吐血…

自定义Builder

通过上面的分析,Provider的widget定点刷新,已经不再神秘了…

学以致用,咱们来整一个自定义Builder!

  • 自定义的EasyBuilder控件能起到和Consumer一样的刷新作用
class EasyBuilder<T> extends StatelessWidget {const EasyBuilder(this.builder, {Key? key,}) : super(key: key);final Widget Function() builder;@overrideWidget build(BuildContext context) {Provider.of<T>(context);return builder();}
}

写下完整的使用

  • view
class CustomBuilderPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (BuildContext context) => CustomBuilderProvider(),builder: (context, child) => _buildPage(context),);}Widget _buildPage(BuildContext context) {final provider = context.read<CustomBuilderProvider>();return Scaffold(appBar: AppBar(title: Text('Provider-自定义Builder范例')),body: Center(child: EasyBuilder<CustomBuilderProvider>(() => Text('点击了 ${provider.count} 次',style: TextStyle(fontSize: 30.0),),),),floatingActionButton: FloatingActionButton(onPressed: () => provider.increment(),child: Icon(Icons.add),),);}
}///自定义Builder
class EasyBuilder<T> extends StatelessWidget {const EasyBuilder(this.builder, {Key? key,}) : super(key: key);final Widget Function() builder;@overrideWidget build(BuildContext context) {Provider.of<T>(context);return builder();}
}
  • provider
class CustomBuilderProvider extends ChangeNotifier {int count = 0;void increment() {count++;notifyListeners();}
}
  • 效果图

总结

以上,就将Provider的刷新机制完整的说完了~~

撒花 ✿✿ヽ(°▽°)ノ✿

如果那里写的欠妥,请各位大佬不吝赐教 ~ . ~

MultiProvider

在上面的刷新机制里面,我说了一个:ChangeNotifierProvider这个类很重要,基本可以算是框架的主入口

  • 在这里,你可能有疑问了???

    • 这不对吧!
    • 我们一般不是在main主入口的写全局Provider,要用到MultiProvider,按理说:主入口应该是MultiProvider!
void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(builder: (BuildContext context, Widget? child) {return MultiProvider(child: child, providers: [//此处通过MultiProvider创建的Provider是全局的ChangeNotifierProvider.value(value: ProSpanOneProvider()),]);});}
}
  • 这里来看下MultiProvider代码(无限嵌套那个Provider就不讲了,很少用)

    • 源码so easy,继承Nested类,Nested可以优化一些布局嵌套问题,感兴趣的可查看:nested(pub)
    • 看源码,可以发现MultiProvider肯定不是主入口,这地方只是将Provider的套在顶层Widget上
class MultiProvider extends Nested {MultiProvider({Key? key,required List<SingleChildWidget> providers,Widget? child,TransitionBuilder? builder,}) : super(key: key,children: providers,child: builder != null? Builder(builder: (context) => builder(context, child),): child,);
}
  • 上面的不是主入口,children里面用了ChangeNotifierProvider.value,来看看这个源码

    • ChangeNotifierProvider.value是ChangeNotifierProvider的命名构造函数,实际上ChangeNotifierProvider.value是对ChangeNotifierProvider使用的一个优化
class ChangeNotifierProvider<T extends ChangeNotifier?> extends ListenableProvider<T> {ChangeNotifierProvider({Key? key,required Create<T> create,bool? lazy,TransitionBuilder? builder,Widget? child,}) : super(key: key,create: create,dispose: _dispose,lazy: lazy,builder: builder,child: child,);ChangeNotifierProvider.value({Key? key,required T value,TransitionBuilder? builder,Widget? child,}) : super.value(key: key,builder: builder,value: value,child: child,);static void _dispose(BuildContext context, ChangeNotifier? notifier) {notifier?.dispose();}
}
  • 为什么说ChangeNotifierProvider.value是对ChangeNotifierProvider使用的一个优化呢?

    • 来看看下面这个俩种写法,实际上等同的
void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(builder: (BuildContext context, Widget? child) {return MultiProvider(child: child, providers: [//简化版ChangeNotifierProvider.value(value: ProSpanOneProvider()),//效果和上面等同ChangeNotifierProvider(create: (context) => ProSpanOneProvider()),]);});}
}
  • 总结

    • 所以 ChangeNotifierProvider类是非常重要的,基本是Provider的主入口,没毛病
    • 很多初始化的操作,都是借助从该类实例化的时候开始的

Consumer

Consumer应该是我们日常,非常非常常用的一个控件了,他的源码很简单,结构也很清晰

作者还写很多:Consumer2、Consumer3、Consumer4、Consumer5、Consumer6;把我直接看懵了。。。

鄙人拙见,大可不必,这样会让builder参数变得十分迷惑;能用Consumer2到Consumer6了,直接用Provider.of(context),或许能让后来者更加清晰的读懂代码;而且使用Consumer2之类的,必须要在Consumer上面写相应的泛型,builder方法里面写相应的参数,这和我直接写Provider.of(context)的工作量相差无几。。。

此处我们只需要看Consumer就行了,至于Consumer2到Consumer6,就只是多封了几个Provider.of(context)。。。

  • Consumer

    • 结构很清晰,继承了SingleChildStatelessWidget,重写了buildWithChild方法,在里面返回了builder函数
    • 请注意:这地方做了一个将child传到父类的操作;而且buildWithChild里面会传出一个child的,然后传到builder方法里
class Consumer<T> extends SingleChildStatelessWidget {Consumer({Key? key,required this.builder,Widget? child,}) : super(key: key, child: child);final Widget Function(BuildContext context, T value, Widget? child,) builder;@overrideWidget buildWithChild(BuildContext context, Widget? child) {return builder(context,Provider.of<T>(context),child,);}
}
  • SingleChildStatelessWidget

    • 此处在抽象了一个buildWithChild方法,然后在build方法中调用了buildWithChild方法
    • 此处,将context和我们在外部传入的child,都传给了buildWithChild方法
    • ok,Consumer逻辑比较简单,大致就这么多了!
abstract class SingleChildStatelessWidget extends StatelessWidget implements SingleChildWidget {const SingleChildStatelessWidget({Key? key, Widget? child}): _child = child,super(key: key);final Widget? _child;Widget buildWithChild(BuildContext context, Widget? child);@overrideWidget build(BuildContext context) => buildWithChild(context, _child);@overrideSingleChildStatelessElement createElement() {return SingleChildStatelessElement(this);}
}

Selector

Provider还有很重要的刷新组建,条件刷新组件Selector,来看看

  • 使用

    • 我这地方用了三层结构,将状态层解耦出去了
    • 对复杂模块能更好应对
class ProHighCounterPage extends StatelessWidget {final provider = ProHighCounterProvider();@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (BuildContext context) => provider,child: _buildSelector(),);}Widget _buildSelector() {return Scaffold(appBar: AppBar(title: Text('Provider-Extended范例')),body: Center(child: Selector(shouldRebuild: (previous, next) {return true;},selector: (context, provider) => provider,builder: (_, __, ___) {return Text('点击了 ${provider.state.count} 次',style: TextStyle(fontSize: 30.0));},),),floatingActionButton: FloatingActionButton(onPressed: () => provider.increment(),child: Icon(Icons.add),),);}
}class ProHighCounterProvider extends ChangeNotifier {final state = ProExtendedCounterState();void increment() {state.count++;notifyListeners();}
}class ProExtendedCounterState {late int count;ProExtendedCounterState() {count = 0;}
}
  • Selector

    • 看来Selector0才是重点,去看看Selector0
class Selector<A, S> extends Selector0<S> {Selector({Key? key,required ValueWidgetBuilder<S> builder,required S Function(BuildContext, A) selector,ShouldRebuild<S>? shouldRebuild,Widget? child,}) : super(key: key,shouldRebuild: shouldRebuild,builder: builder,selector: (context) => selector(context, Provider.of(context)),child: child,);
}

Selector0:主要逻辑在_Selector0State中,下面三个判定为true,都可以使builder方法执行刷新操作

  1. oldWidget != widget:如果selector的父节点刷新了,builder也会刷新
  2. widget. _shouldRebuild != null && widget. _shouldRebuild!(value as T, selected):shouldRebuild回调实现了,且返回为true
  3. selector:selector回调返回了XxxProvider,XxxProvider这个实例完全改变了(例:重新实例化赋值);且shouldRebuild回调未实现
class Selector0<T> extends SingleChildStatefulWidget {Selector0({Key? key,required this.builder,required this.selector,ShouldRebuild<T>? shouldRebuild,Widget? child,})  : _shouldRebuild = shouldRebuild,super(key: key, child: child);final ValueWidgetBuilder<T> builder;final T Function(BuildContext) selector;final ShouldRebuild<T>? _shouldRebuild;@override_Selector0State<T> createState() => _Selector0State<T>();
}class _Selector0State<T> extends SingleChildState<Selector0<T>> {T? value;Widget? cache;Widget? oldWidget;@overrideWidget buildWithChild(BuildContext context, Widget? child) {final selected = widget.selector(context);final shouldInvalidateCache = oldWidget != widget ||(widget._shouldRebuild != null &&widget._shouldRebuild!(value as T, selected)) ||(widget._shouldRebuild == null &&!const DeepCollectionEquality().equals(value, selected));if (shouldInvalidateCache) {value = selected;oldWidget = widget;cache = widget.builder(context,selected,child,);}return cache!;}@overridevoid debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);properties.add(DiagnosticsProperty<T>('value', value));}
}

手搓一个状态管理框架

看完Provider的原理后,大家是不是感觉胸中万千沟壑,腹中万千才华无法释放!咱们就来将自己想法统统释放出来吧!

学以致用,咱们就来按照Provider刷新机制,手搓一个状态管理框架。。。

手搓框架就叫:EasyP(后面应该还会接着写Bloc和GetX;依次叫EasyC,EasyX,省事…),取Provider的头字母

手搓状态框架

这个手搓框架做了很多简化,但是绝对保留了原汁原味的Provider刷新机制!

  • ChangeNotifierEasyP:类比Provider的ChangeNotifierProvider

    • 代码做了大量的精简,只保留了provider的刷新机制的精髓
    • 代码我就不解释了,上面的刷新机制如果看懂了,下面的代码很容易理解;如果没看懂,我解释下面代码也没用啊。。。
class ChangeNotifierEasyP<T extends ChangeNotifier> extends StatelessWidget {ChangeNotifierEasyP({Key? key,required this.create,this.builder,this.child,}) : super(key: key);final T Function(BuildContext context) create;final Widget Function(BuildContext context)? builder;final Widget? child;@overrideWidget build(BuildContext context) {assert(builder != null || child != null,'$runtimeType  must specify a child',);return EasyPInherited(create: create,child: builder != null? Builder(builder: (context) => builder!(context)): child!,);}
}class EasyPInherited<T extends ChangeNotifier> extends InheritedWidget {EasyPInherited({Key? key,required Widget child,required this.create,}) : super(key: key, child: child);final T Function(BuildContext context) create;@overridebool updateShouldNotify(InheritedWidget oldWidget) => false;@overrideInheritedElement createElement() => EasyPInheritedElement(this);
}class EasyPInheritedElement<T extends ChangeNotifier> extends InheritedElement {EasyPInheritedElement(EasyPInherited<T> widget) : super(widget);bool _firstBuild = true;bool _shouldNotify = false;late T _value;late void Function() _callBack;T get value => _value;@overridevoid performRebuild() {if (_firstBuild) {_firstBuild = false;_value = (widget as EasyPInherited<T>).create(this);_value.addListener(_callBack = () {// 处理刷新逻辑,此处无法直接调用notifyClients// 会导致owner!._debugCurrentBuildTarget为null,触发断言条件,无法向后执行_shouldNotify = true;markNeedsBuild();});}super.performRebuild();}@overrideWidget build() {if (_shouldNotify) {_shouldNotify = false;notifyClients(widget);}return super.build();}@overridevoid notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {//此处就直接刷新添加的监听子Element了,不各种super了dependent.markNeedsBuild();// super.notifyDependent(oldWidget, dependent);}@overridevoid unmount() {_value.removeListener(_callBack);_value.dispose();super.unmount();}
}
  • EasyP:类比Provider的Provider类
class EasyP {/// 获取EasyP实例/// 获取实例的时候,listener参数老是写错,这边直接用俩个方法区分了static T of<T extends ChangeNotifier>(BuildContext context) {return _getInheritedElement<T>(context).value;}/// 注册监听控件static T register<T extends ChangeNotifier>(BuildContext context) {var element = _getInheritedElement<T>(context);context.dependOnInheritedElement(element);return element.value;}/// 获取距离当前Element最近继承InheritedElement<T>的组件static EasyPInheritedElement<T>_getInheritedElement<T extends ChangeNotifier>(BuildContext context) {var inheritedElement = context.getElementForInheritedWidgetOfExactType<EasyPInherited<T>>()as EasyPInheritedElement<T>?;if (inheritedElement == null) {throw EasyPNotFoundException(T);}return inheritedElement;}
}class EasyPNotFoundException implements Exception {EasyPNotFoundException(this.valueType);final Type valueType;@overrideString toString() => 'Error: Could not find the EasyP<$valueType>';
}
  • build:最后整一个Build类就行了
class EasyPBuilder<T extends ChangeNotifier> extends StatelessWidget {const EasyPBuilder(this.builder, {Key? key,}) : super(key: key);final Widget Function() builder;@overrideWidget build(BuildContext context) {EasyP.register<T>(context);return builder();}
}

大功告成,上面这三个类,就能起到和Provider一样的局部刷新功能!

刷新机制一模一样,绝对没有吹牛皮!

下面来看看怎么使用吧!

使用

用法基本和Provider一摸一样…

  • view
class CounterEasyPPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierEasyP(create: (BuildContext context) => CounterEasyP(),builder: (context) => _buildPage(context),);}Widget _buildPage(BuildContext context) {final easyP = EasyP.of<CounterEasyP>(context);return Scaffold(appBar: AppBar(title: Text('自定义状态管理框架-EasyP范例')),body: Center(child: EasyPBuilder<CounterEasyP>(() {return Text('点击了 ${easyP.count} 次',style: TextStyle(fontSize: 30.0),);}),),floatingActionButton: FloatingActionButton(onPressed: () => easyP.increment(),child: Icon(Icons.add),),);}
}
  • easyP
class CounterEasyP extends ChangeNotifier {int count = 0;void increment() {count++;notifyListeners();}
}
  • 效果图:体验一下

    • 如果网页打不开,可能需要你清下浏览器缓存

全局EasyP

  • 全局也是可以的,直接把ChangeNotifierEasyP类套在主入口,代码就不贴了,给大家看下效果图

总结

如果有靓仔的公司,不想使用第三方状态管理框架,完全可以参照Provider的刷新机制,撸一个状态管理框架出来!我上面已经撸了一个极简版,画龙画虎难画骨,上面我大致把他的骨架整好了;如果有需要的话,发挥你的聪明才智,copy过去给他填充血肉吧。。。

如果大家看懂了Provider的刷新机制,就会发现Provider状态框架,对系统资源占用极低,它仅仅只使用了ChangeNotifier,这仅仅是最基础的Callback回调,这会占用多少资源?刷新逻辑全是调用Flutte的framework层自带的那些api(获取InheritedElement的内部操作很简单,有兴趣可以看看)。。。所以完全不用担心,他会占用多少资源,几乎忽略不计!

最后

一本秘籍

写完整篇文章,我突然感觉自己掌握一本武功秘籍!知道了怎么去写出高端大气上档次且深奥的项目!

我现在就来传授给大家…

  • 首先一定要善用面向接口编程的思想!

    • 如果要想非常深奥,深奥的自己都难以看懂,那直接滥用这种思想就稳了!
  • 多用各种设计模式,别和我扯什么简单易用,老夫写代码,就是设计模式一把梭,不管合适不合适,全怼上面

    • 一定要多用命令模式和访问者模式,就是要让自己的函数入参超高度可扩展,难以被别人和自己读懂
    • if else内部逻辑直接抛弃,全用策略模式往上怼
    • 不管内部状态闭不闭环,状态模式直接强行闭环
    • for要少用,多用List遍历,防止别人不懂你的良苦用心,一定在旁注释:迭代器模式
    • 外观模式,一般都是做一层外观吧,咱们直接搞俩层,三层外观类!代理模式五层代理类起步!
    • 对象或变量不管是不是只用一次,咱们全都缓存起来,将享元模式的思想贯彻到底
    • 变换莫测的就是桥接模式了,一般俩个维度桥接,咱们直接9个维度,俗话说的好,九九八十一难嘛,不是把你绕进去,就是把自己绕起来!头发和命,只有一个能活!
    • 所有的类与类绝不强耦合,一定要有中介类桥接,别人要喷你;你就自信的往后一仰,淡淡的说:“迪米特法则,了解一下。”
  • 最重要的,要多用Framework层的回调

    • 不管那个系统回调咱们懂不懂,都在里面整点代码,假装很懂
    • 最关键的时候,系统抽象类要继承,多写点自己的抽象方法,千万不能写注释,不然以后自己看懂了,咋办?

以上纯属调侃

切勿对号入座进Provider,Provider相关思想用的张弛有度,他所抽象的类,实际在多处实现了不同的实现类,大大的增加了扩展;而且他所继承的系统上下文类里,所抽象的方法,给了非常详尽的注释。

从Provider的源码上看,能看出Provider的作者绝对是个高手,必须对framework层有足够了解,才能写出那样精彩的刷新机制!

这是一个很优秀的框架!

我为啥写上面这些调侃?ε=(´ο`*)))唉,前人练手,后人抓头。。。

相关地址

  • 文章中Demo的Github地址:flutter_use
  • Web效果:https://cnad666.github.io/flutter_use/web/index.html
    • 如果provider相关功能按钮没看到,可能需要你清下浏览器缓存
  • Windows:Windows平台安装包
    • 密码:xdd666

系列文章

  • 源码篇:Handler那些事(万字图文)
  • 源码篇:ThreadLocal的奇思妙想(万字图文)

【源码篇】Flutter Provider的另一面(万字图文+插件)相关推荐

  1. Android Jetpack架构组件之 Room(使用、源码篇)

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发 ...

  2. 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...

  3. phpcmsV9 完整更新ckeditor编辑器到最新版 - 源码篇

    phpcmsV9 默认编辑器是ckeditor,但是版本低,使用效果低. 本文 重点解决问题: 更新ckeditor默认编辑器到最新版. phpcms当前版本 : V9.6.3 . 现在将我更换编辑器 ...

  4. create-react-app教程-源码篇

    原文链接:create-react-app教程-源码篇 之前介绍了create-react-app的基本使用, 为了便于理解一个脚手架脚本是如何运作的,现在来看一下 create-react-app ...

  5. shiro源码篇 - 疑问解答与系列总结,你值得拥有

    前言 开心一刻 小明的朋友骨折了,小明去他家里看他.他老婆很细心的为他换药,敷药,然后出去买菜.小明满脸羡慕地说:你特么真幸福啊,你老婆对你那么好!朋友哭得稀里哗啦的说:兄弟你别说了,我幸福个锤子,就 ...

  6. android 使用4大组件的源码,Android Jetpack架构组件之 Paging(使用、源码篇)

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  7. 第五篇:Spring源码篇-ApplicationContext

    Spring源码篇-ApplicationContext   前面通过手写IoC,DI.AOP和Bean的配置.到最后ApplicationContext的门面处理,对于Spring相关的核心概念应该 ...

  8. 手撸Spring系列12:MyBatis(源码篇)

    说在前头: 笔者本人为大三在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正. ...

  9. 手撸Spring系列2:IOC/DI 思想(源码篇-IOC)

    说在前头: 笔者本人为大三在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正. ...

最新文章

  1. jmap+MAT实战内存溢出
  2. 斜挎包长度到哪里合适_斜挎包带子多长合适 看个人身高
  3. C# 程序集 和 反射
  4. python真好玩 pdf_编程真好玩(青少年学Python一本通)
  5. ERROR: The minSdk version should not be declared in the android manifest file.
  6. 凯撒密码 (Python实现)
  7. 通达信公式-当天成交量不大于百日均成交量比例
  8. 寻迹pid算法 c语言,基于STC12C5A60S2单片机及PID控制算法的气味循迹车设计
  9. 大三,在软件工程学习上的感悟
  10. python中的二维列表(实例)_python实现两个一维列表合并成一个二维列表
  11. 利用Python,通过关键字获取漏洞平台最新漏洞信息
  12. 开发人员如何在面试中介绍自己的项目经历
  13. 解决hive报错FAILED: SemanticException Cartesian products are disabled for safety的问题
  14. 什么是机器人编程课程
  15. thinkphp6+swoole websocket使用教程自研路线不建议使用
  16. Java - 50以内所有质数 (带标签的continue)
  17. 云存储中不可不知的五个安全问题及应对措施
  18. C#与Halcon混合编程调用笔记本摄像头
  19. FRAM铁电存储器FM25W256编程实现存取数据
  20. Keil μVision4和Keil μVision5的比较

热门文章

  1. 梅州人创办、控股的品牌、集团或名企业及相关人物 [转]
  2. 常见哈希算法 对称加密算法与非对称加密算法
  3. docker-compose部署redis,配置redis密码
  4. 更换系统服务器地址,金蝶系统更换服务器地址
  5. Android画板开发(四) 添加背景和保存画板内容为图片
  6. 日期格式 FormatDateTime函数详解
  7. 2007年北京高考作文,惊为天人
  8. 有没有多微信自动批量加好友的软件?
  9. 看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了
  10. 指定固定ip地址提示被其他网络适配器占用,快速解决占用冲突问题