GetxController原理深度解析----万字图解!!!!
getx是flutter开发中使用最频繁也最受欢迎的开发框架,要使用它必须先掌握其原理,并利用其提供的工具,达到更好的利用框架的目的,在了解原理的同时在结合设计模式去分析,不仅能学到原理,还能加强设计模式的学习
一、GetxController的不同创建方式和使用场景
1.Get.lazyPut(() => XXXController());
2.Get.putAsync(() => XXXController());
3.Get.create(() => XXXController());
4.Get.put(() => XXXController());
原理解析及使用场景
//延迟初始化,只有在调用Get.find()的时候才会创建controller void lazyPut<S>( //函数方式创建,懒加载 InstanceBuilderCallback<S> builder, { //对builder内容进行标记 String? tag, //是否持久化保存controller bool? fenix, bool permanent = false, }) { _insert( isSingleton: true, name: tag, permanent: permanent, builder: builder, fenix: fenix ?? Get.smartManagement == SmartManagement.keepFactory, ); } void create<S>( //函数方式创建,懒加载 InstanceBuilderCallback<S> builder, { String? tag, bool permanent = true, }) { _insert( isSingleton: false, name: tag, builder: builder, permanent: permanent, ); } S put<S>( //直接创建 S dependency, { String? tag, bool permanent = false, @deprecated InstanceBuilderCallback<S>? builder, }) { _insert( isSingleton: true, name: tag, permanent: permanent, builder: builder ?? (() => dependency)); return find<S>(tag: tag); }
1.tag(可选):如果想要创建相同类型的controller(由于controller只会创建一次,就像单例,
如果想创建多个,则用tag做标记)
2.permanent(可选):默认情况下,get会在实例不再使用后销毁(例如,一个已经销毁的视图controller),如果需要实例
在整个生命周期中都存在,则设为true。
3.fenix(可选):下次使用时是否重建,当不使用时,实例会被丢弃,但当再次需要使用时,Get会重新创建实例
就像 bindings api 中的 "SmartManagement.keepFactory "一样。
4.isSingleton(不可选):是否是单例形式存储。如果为true则直接调用builder回调,false则先使用dependency直接创建的实例,如果不存在则调用回调函数创建实例。
1.lazyPut 懒加载,传回调函数,只有在调用Get.find()的时候才会调用回调函数创建实例(跟随widget生命周期变化)
2.putAsync 异步实例创建,比如sharePreference.(跟随widget生命周期变化)
3.create 传入的permanent为true,持久化保存实力,不随生命周期变化
4.put 普通创建实例(跟随widget生命周期变化)
二、GetxController的实例获取与使用
//如果是用create创建的controller则调用此方法每次都会重新创建实例 //如果注册的是controller,则会初始化它的生命周期 S find<S>({String? tag}) { //通过tag标记寻找已保存的controller(如果没有的话通过controller的类名寻找) final key = _getKey(S, tag); //判断是否注册过,注册过则直接获取controller实例的包装类_InstanceBuilderFactory if (isRegistered<S>(tag: tag)) { final dep = _singl[key]; if (dep == null) { if (tag == null) { throw 'Class "$S" is not registered'; } else { throw 'Class "$S" with tag "$tag" is not registered'; } } //关注此函数 final i = _initDependencies<S>(name: tag); return i ?? dep.getDependency() as S; } else { // ignore: lines_longer_than_80_chars throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"'; } }
上面的代码比较简单,就是从容器中获取controller实例,我们重点看看_initDependencies(name: tag);
S? _initDependencies<S>({String? name}) { final key = _getKey(S, name); final isInit = _singl[key]!.isInit; S? i; //第一次执行会走进来,因为isInit默认为false if (!isInit) { i = _startController<S>(tag: name); if (_singl[key]!.isSingleton!) { _singl[key]!.isInit = true; if (Get.smartManagement != SmartManagement.onlyBuilder) { RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name)); } } } return i; } /// 真正的初始化controller的逻辑 S _startController<S>({String? tag}) { final key = _getKey(S, tag); final i = _singl[key]!.getDependency() as S; if (i is GetLifeCycleBase) { //开始执行controller的生命周期 //oninit --- 并同时监听onReady执行(界面布局完成后) i.onStart(); if (tag == null) { Get.log('Instance "$S" has been initialized'); } else { Get.log('Instance "$S" with tag "$tag" has been initialized'); } if (!_singl[key]!.isSingleton!) { RouterReportManager.appendRouteByCreate(i); } } return i; } // 说明只执行一次onInit();初始化 void _onStart() { if (_initialized) return; onInit(); _initialized = true; }
上面的代码只有一个逻辑,就是执行controller的生命周期函数
abstract class DisposableInterface extends GetLifeCycle { //当调用Get.find()的时候执行此方法。 void onInit() { super.onInit(); //这里开始监听当前页面的vsync信号帧绘制,绘制完成后回调onReady()方法 Get.engine.addPostFrameCallback((_) => onReady()); } /// Called 1 frame after onInit(). It is the perfect place to enter /// navigation events, like snackbar, dialogs, or a new route, or /// async request. void onReady() { //这里需要客户端进行重写 super.onReady(); }
1.onInit只会执行一次,调用find的时候会首先调用生命周期onInit方法
2.根据vsync信号刷新回调onReady生命周期方法。即界面构建完成之后调用
总结:只有controller在当前页面使用Get.find()的时候。首先会执行onInit方法。如果在widget中调用,同时还会监听页面绘制完成并调用onReady方法。
注意:如果只是通过Get.find()的方式获取controller,并不会调用controller的delete()方法清空内存。需要手动调用Get.delete()方法从内存中清除controller。
三、GetxController的销毁时机
1.在widget的dispose()中手动调用Get.delete()清除controller
2.绑定widget生命周期,跟随widget生命周期清除
getx提供了多个widget供开发者使用。其中有下列常用的几种
负责管理controller的widget
1.GetView(具有自动获取controller的功能)
class AwesomeController extends GetxController { final String title = 'My Awesome View'; } class AwesomeView extends GetView<AwesomeController> { /// 如果要给controller做个标记的话可以这么写 /// Get.find<AwesomeController>(tag:"myTag"); final String tag = "myTag"; AwesomeView({Key key}):super(key:key); Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(20), child: Text( controller.title ), ); } }
2.GetWidget(缓存controller)
内部跟着缓存走,须使用Get.create(()=>Controller());(因为getWidget适用于保存同一类controller的不同实例。),所以每次都需要调用创建方法。在其从内存中退出时,如widget从数中卸载时,会自动调用onclose对controller进行销毁
部分源码解析
void onClose() { if (_isCreator) { //异步调用controller的销毁逻辑 Get.asap(() { widget!.controller!.onDelete(); Get.log('"${widget!.controller.runtimeType}" onClose() called'); Get.log('"${widget!.controller.runtimeType}" deleted from memory'); GetWidget._cache[widget!] = null; }); } info = null; super.onClose(); }
在widget即将销毁时,会判断是否是通过create创建的controller,如果是则执行销毁缓存controller的方法。
以下是使用方式
class GetViewAndGetWidgetExample extends GetWidget<GetViewCountController> { Widget build(BuildContext context) { Get.create(() => GetViewCountController()); return Scaffold( appBar: AppBar( title: Text("GetX GetView"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Obx(() => Text( "count的值为: ${controller?.count}", style: TextStyle( color: Colors.red, fontSize: 30 ), )), SizedBox(height: 20,), ElevatedButton( onPressed: () { controller.increment(); }, child: Text("点我加1")) ], ), ), ); } }
getwidget用的比较少,一般都是用的getview
2.GetResponsiveView (平板,桌面等适配)
负责状态管理的widget(StatefulWidget类,具有状态管理功能)
1.GetBuilder(相比getx多了id和filter的变量)
2.GetX
3.Obx
1.从使用性能看,GetBuilder是性能最好的工具,需要手动调用update()进行更新
2.obx比较常用,只有初始化变量后就可以响应式更新
3.Getx是结合了obx和Getbuilder的功能,比较消耗内存。
一 .GetBuilder原理解析
部分源码截取
void initState() { // _GetBuilderState._currentState = this; super.initState(); //初始化GetBuilder的时候可以传入initState进行初始化操作。 widget.initState?.call(this); var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag); //global默认为true,即为全局controller。可在当前widget或全局使用 if (widget.global) { if (isRegistered) { if (GetInstance().isPrepared<T>(tag: widget.tag)) { _isCreator = true; } else { _isCreator = false; } //如果注册过则通过find的方式获取controller controller = GetInstance().find<T>(tag: widget.tag); } else { //如果未注册过,则先调用传入的init初始化controller。 //类似下图 //GetBuilder<MusicSetController>( // init: MusicSetController(), // builder: (_) { controller = widget.init; _isCreator = true; //将controller放进缓存中 GetInstance().put<T>(controller!, tag: widget.tag); } } else { //如果非全局,则直接初始化并调用controller的onstart方法。 controller = widget.init; _isCreator = true; controller?.onStart(); } if (widget.filter != null) { _filter = widget.filter!(controller!); } //往下看 _subscribeToController(); } //注册监听器,setstate((){});getUpdate就是setstate监听器。这里会将监听方法存起来,在手动调用 //refresh()或refreshGroup(id)的时候会调用监听器刷新,即setstate. void _subscribeToController() { _remove?.call(); _remove = (widget.id == null) ? controller?.addListener( _filter != null ? _filterUpdate : getUpdate, ) : controller?.addListenerId( widget.id, _filter != null ? _filterUpdate : getUpdate, ); }
1.getBiulder首先会先调用传入的initState初始化函数(如果传入的话)。
2.判断controller是否存在,如果存在则从缓存中获取,否则重新创建一个(从传入的init创建)。
3.注册监听器。只有在手动调用refresh或refreshGroup的时候会执行GetBuider的setstate方法进行刷新。
GetBuilder的销毁
void dispose() { super.dispose(); //首先调用手动传入的dispose方法。 widget.dispose?.call(this); //判断是否创建过或者assignId是否为true。则调用销毁方法。一般_isCreator初始化的时候就为true了。 if (_isCreator! || widget.assignId) { if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) { GetInstance().delete<T>(tag: widget.tag); } } _remove?.call(); controller = null; _isCreator = null; _remove = null; _filter = null; }
GetBuilder使用方法
Widget build(BuildContext context) { // TODO: implement build return Material( child: GetBuilder<MusicSetController>( //如果首次使用,这个必传。需要进行初始化操作 init: MusicSetController(), builder: (_) { return Container(); }, //如要为了保证其执行销毁操作,可传入此参数。在widget销毁的时候,controller也会跟着销毁。 assignId: true, ),
Obx使用方法。
Widget build(BuildContext context) { RxInt count = 0.obx; return Scaffold( appBar: AppBar( title: Text("GetX GetView"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Obx(() => Text( "count的值为: ${count.value}", style: TextStyle( color: Colors.red, fontSize: 30 ), )), SizedBox(height: 20,), ElevatedButton( onPressed: () { count ++; }, child: Text("点我加1")) ], ), ), ); }
1.obx调用比较简单,只需要包裹一个使用count变量的widget
2.同时将count变量初始化成RxInt类型
3.使用变量的时候通过count.value调用即可。
4.如果count变量发生变化,则只刷新obx的widget
obx的原理比较复杂,详细看下一章节。