找回密码
 会员注册
查看: 20|回复: 0

剖析Flutter的常用库get_it

[复制链接]

3

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2024-10-11 23:12:30 | 显示全部楼层 |阅读模式
前言通常外企的领导在工作群发通知消息就马上看到有人回复:「I get it.」这时候,也会有人说:「I got it.」只是当你说 I get it. 的时候,更像是明白了一些之前不明白的事情,或者配合其他吐槽的话会显得不耐烦。而说 I got it. 的时候,就是纯粹表示,我知道了明白了。今天我们就来介绍Flutter中的常用库get_it一、来由在Dart和Flutter工程中,为一个组件提供对象/服务的默认方式是通过InheritedWidget。还有Provider、Singleton、IoC等方式。1.1 InheritedWidget如果希望一个部件或其模型能够访问服务,则组件必须是继承的组件的子组件。然后这会导致不必要的嵌套。而且依赖性强,持续性维护差。///创建数据类DataWidget,然后在页面中用数据类DataWidget包含页面child部分///1、创建DataWidgetclassDataWidgetextendsInheritedWidget{DataWidget({@requiredthis.data,Widgetchild}):super(child:child);finalintdata;staticDataWidgetof(BuildContextcontext){returncontext.dependOnInheritedWidgetOfExactType();}@overrideboolupdateShouldNotify(DataWidgetold){returnold.data!=data;}}///2、创建InheritedWidgetTestRoute.dart文件classInheritedWidgetTestRouteextendsStatefulWidget{@override_InheritedWidgetTestRouteStatecreateState()=>new_InheritedWidgetTestRouteState();}class_InheritedWidgetTestRouteStateextendsState{intcount=0;@overrideWidgetbuild(BuildContextcontext){returnCenter(childataWidget(//重点:使用DataWidget包裹data:count,child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[Padding(padding:constEdgeInsets.only(bottom:20.0),child:Text(DataWidget.of(context).data.toString())),RaisedButton(child:Text("Increment"),//每点击一次,将count自增,然后更新页面渲染,DataWidget的data将被更新onPressed)=>setState(()=>++count),)],),),);}}1.2 Provider为组件提供对象/服务,还有一种方式是使用Provider,使用比较繁琐。///创建数据类`CountNotifier`,然后在页面中用数据类`CountNotifier`包裹child部分。voidmain()=>runApp(MyApp());classMyAppextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){//创建Widget持有CountNotifier数据returnChangeNotifierProvider.value(value:CountNotifier(),child:MaterialApp(title:'PrivoderDemo',theme:ThemeData(primarySwatch:Colors.blue,),homerovidePage(title:'Provider测试页面'),),);}}classProvidePageextendsStatelessWidget{finalStringtitlerovidePage({Keykey,this.title}):super(key:key);@overrideWidgetbuild(BuildContextcontext){//获取CountNotifier数据(最简单的方式)finalcounter=Provider.of(context);returnScaffold(appBar:AppBar(title:Text(title),),body:Center(child:Column(mainAxisSize:MainAxisSize.min,mainAxisAlignment:MainAxisAlignment.center,children:[Text('按下按钮,使数字增加:',),Text('${counter.count}',style:Theme.of(context).textTheme.display1,),],),),floatingActionButton:FloatingActionButton(onPressed){counter.increment();},tooltip:'Increment',child:constIcon(Icons.add),),);}}///核心:继承自ChangeNotifier///可以单独放在一个类文件里classCountNotifierwithChangeNotifier{int_count=0;intgetcount=>_count;increment(){_count++;//核心方法,通知刷新UI,调用build方法notifyListeners();}}1.3 Singleton、IoC我们也可以通过其他的方式在App中的任意位置获取到要访问的对象,但是:如果使用Singleton,则无法轻松地将实现切换到另一个(例如用于单元测试的模拟版本)用于依赖项注入的IoC容器提供了类似的功能,但代价是启动时间慢且可读性差,因为不知道神奇注入的对象来自何处。由于大多数IoC库都依赖反射,因此它们不能与Flutter一起使用。1.4 GetIt在App迭代发展中,随着代码工程的增长,在某些时候需要将App的部分逻辑放在与Widget分离的类中。使Widget不具有直接依赖关系可以使代码更好地组织并更易于测试和维护。但是现在需要一种从 UI 代码访问这些对象的方法。作者escamoteur 借鉴.net中的Service Locator Splat概念,在Dart中开发而成。Service Locators 的概念,它是一种将接口(抽象基类)与具体实现解耦的方法,同时允许通过接口从您的 App 中的任何地方访问具体实现。故GetIt应运而生,从1.0到现在的7.x二、介绍2.1 定义[GetIt] 官方介绍This is a simple 「Service Locator」 for Dart and Flutter projects with some additional goodies highly inspired by Splat. It can be used instead of InheritedWidget or Provider to access objects e.g. from your UI.Typical usage:Accessing service objects like REST API clients or databases so that they easily can be mocked.Accessing View/AppModels/Managers/BLoCs from Flutter Views「V7.0 has some breaking changes」 Check please check the release notes to see what's new.译文:GetIt是一个用于 Dart 和 Flutter 项目的简单服务定位器,其中包含一些受到 Splat 启发的附加功能。它可以用来代替 InheritedWidget 或 Provider 比如从你的用户界面来访问对象。典型用法(使用场景):访问 REST API 客户端或数据库等服务对象,以便轻松模拟它们从 Flutter 视图访问 View/AppModels/Managers/BLoCs简而言之GetIt是一个简单的直接服务定位器,允许将接口与具体实现分离,并从应用程序的任何地方访问具体实现。一句话总结GetIt是一个工具箱。2.2 特点调用极快 (复杂度O(1))易学/易用不会像提供程序或 Redux 那样使用特殊的小部件来使您的 UI 树混乱以访问您的数据。Redux 的设计思想很简单,就两句话。(1)Web 应用是一个状态机,视图与状态是一一对应的。(2)所有的状态,保存在一个对象里面。三、使用3.1 引入在某包下的pubspec.yaml引入框架dependencies:flutter:sdk:flutterdomain_common:path:../domain_commonres_common:path:../res_commonprovider:^5.0.0get_it:^7.1.33.2 获取GetIt以单例模式使用GetItgetIt=GetIt.instance;//orGetItgetIt=GetIt.I;//重新自定义一个新的GetItgetIt=GetIt.asNewInstance();3.3 注册GetItlocator=GetIt.instance;//在GetIt里注册工厂类Mutoulocator.registerFactory(()=>MutouImpl());//在GetIt里注册单例locator.registerSingleton(()=>LocalFileSystem());locator.registerLazySingleton(()=>ClientRepositoryImpl());3.4 调用GetItlocator=GetIt.instance;//调用工厂对象varmutouModel=locator.get();//调用单例varfileSystemSingleton=locator();3.5 注册方式有三种常用的注册方式GetItlocator=GetIt.instance;FuturesetupLocator({booltest=false})async{//一,单例模式,使用registerSingleton//在main.dart中调用setupLocator()时,就会立即注册FileSystem//注册单例,保证每一次调用FileSystem工具时,都是同一个locator.registerSingleton(FileSystem());//有扩展函数可以传参可以异步//locator.registerFactoryParam//locator.registerFactoryAsync//locator.registerFactoryParamAsync//还可以扩展依赖//locator.registerSingletonWithDependencies//二,懒汉模式单例,使用registerLazySingleton//调用setupLocator()时,没有生成,直到在其他地方第一次调用ClientRepository工具时,才会注册//registerSingleton注册单例,保证每一次调用ClientRepository工具时,都是同一个locator.registerLazySingleton(ClientRepositoryImpl());//也支持异步模式//locator.registerLazySingletonAsync//三,工厂模式,使用registerFactory/registerFactoryParam/registerFactoryParamAsync//调用setupLocator()时,没有生成,直到在其他地方第一次调用Mutou工具时,才会注册//参数必须时工厂函数,工厂函数不需要参数用registerFactory//参数必须时工厂函数,工厂函数需要参数用registerFactoryParam//参数必须时工厂函数,工厂函数是异步且需要参数用registerFactoryParamAsynclocator.registerFactory(()=>MutouImpl()));locator.registerFactoryParam((str,num)=>MutouPlusImpl(param1:str,param2:num));locator.registerFactoryParamAsync((str,num)=>MutouProImpl(param1:str,param2:num));}3.6 其他//判断某个对象是否已注册boolisRegistered({Objectinstance,StringinstanceName});//重置所有注册的对象Futurereset({booldispose=true});//画个圈V5.0后才有//是否范围内重置FutureresetScope({booldispose=true});//之前的所有注册作废,在一个新的范围内新建voidpushNewScope({voidFunction(GetItgetIt)init,StringscopeName,ScopeDisposeFuncdispose,});//在某个范围内退出,在这个范围内注册的所有对象作废FuturepopScope();//若有多个范围,则制定某个范围内退出,相应范围注册的所有对象作废FuturepopScopesTill(Stringname);//获取当前范围名字StringgetcurrentScopeName;//在特定时间timeout内完成所有异步对象创建,ignorePendingAsyncCreation是是否忽略异步对象的创建FutureallReady({Durationtimeout,boolignorePendingAsyncCreation=false,});//在特定timeout时长下,是否完成异步的对象创建FutureisReady({Objectinstance,StringinstanceName,Durationtimeout,Objectcallee,});//不要被这个名字误解了,这个还是检查异步对象是否被创建,因为同步的不需要检查boolisReadySync({Objectinstance,StringinstanceName,});//检查是否完成所有异步对象创建,ignorePendingAsyncCreation是是否忽略异步对象的创建boolallReadySync([boolignorePendingAsyncCreation=false]);//注册为异步对象的时候,在init函数里手动调动///Ifyouwanttousethismechanismyouhavetopass[signalsReady==true]whenregistering///theSingleton.///Typicallythisisusedinthiswayinsidetheregisteredobjectsinit///method`GetIt.instance.signalReady(this);`voidsignalReady(Objectinstance);四、源码本文以 get_it: ^7.1.3为样本分析4.1 本身本身是一个服务工厂或工具箱类class_ServiceFactory{final_ServiceFactoryTypefactoryType;//里面的单例final_GetItImplementation_getItInstance;latefinalTypeparam1Type;latefinalTypeparam2Type;//存储各种对象///Becauseofthedifferentcreationmethodsweneedalternativefactoryfunctions///onlyoneofthemisalwaysset.finalFactoryFunccreationFunction;finalFactoryFuncAsyncasyncCreationFunction;finalFactoryFuncParamcreationFunctionParam;finalFactoryFuncParamAsyncasyncCreationFunctionParam;///DisposefunctionthatisusedwhenascopeispoppedfinalDisposingFuncdisposeFunction;///IncaseofanamedregistrationtheinstancenameisherestoredforeasyaccessfinalStringinstanceName;///trueifoneoftheasyncregistrationfunctionshavebeenusedfinalboolisAsync;///IfaanexistingObjectgetsregisteredoranasync/lazySingletonhasfinished///itscreationitisstoredhereObjectinstance;///thetypethatwasusedwhenregistering.usedforruntimecheckslatefinalTyperegistrationType;///toenableSingletonstosignalthattheyareready(theirinitializationisfinished)lateCompleter_readyCompleter;///thereturnedfutureofpendingasyncfactorycallsorfactorycallwithdependenciesFuturependingResult;///Ifotherobjectsarewaitingforthisone///theyarestoredherefinalListobjectsWaiting=[];boolgetisReady=>_readyCompleter.isCompleted;boolgetisNamedRegistration=>instanceName!=null;StringgetdebugName=>'$instanceNameregistrationType';boolgetcanBeWaitedFor=>shouldSignalReady||pendingResult!=null||isAsync;finalboolshouldSignalReady;_ServiceFactory(this._getItInstance,this.factoryType,{this.creationFunction,this.asyncCreationFunction,this.creationFunctionParam,this.asyncCreationFunctionParam,this.instance,this.isAsync=false,this.instanceName,requiredthis.shouldSignalReady,this.disposeFunction,}):assert(!(disposeFunction!=null&instance!=null&instanceisDisposable),'Youaretryingtoregistertype${instance.runtimeType.toString()}''thatimplements"Disposable"butyoualsoprovideadisposingfunction'){registrationType=T;param1Type=P1;param2Type=P2;_readyCompleter=Completer();}4.2 注册以常用的registerLazySingleton为例看下注册的源码@overridevoidregisterLazySingleton(FactoryFuncfunc,{StringinstanceName}){_register(type:_ServiceFactoryType.lazy,instanceName:instanceName,factoryFunc:func,isAsync:false,shouldSignalReady:false);}void_register({@required_ServiceFactoryTypetype,FactoryFuncfactoryFunc,FactoryFuncParamfactoryFuncParam,FactoryFuncAsyncfactoryFuncAsync,FactoryFuncParamAsyncfactoryFuncParamAsync,Tinstance,@requiredStringinstanceName,@requiredboolisAsync,IterabledependsOn,@requiredboolshouldSignalReady,}){//......参数判断已忽略finalserviceFactory=_ServiceFactory(type,creationFunction:factoryFunc,creationFunctionParam:factoryFuncParam,asyncCreationFunctionParam:factoryFuncParamAsync,asyncCreationFunction:factoryFuncAsync,instance:instance,isAsync:isAsync,instanceName:instanceName,shouldSignalReady:shouldSignalReady,);if(instanceName==null){_factories[T]=serviceFactory;}else{_factoriesByName[instanceName]=serviceFactory;}//普通单例立即被创建注册if(type==_ServiceFactoryType.constant&!shouldSignalReady&!isAsync&(dependsOn.isEmptyfalse)){serviceFactory.instance=factoryFunc();serviceFactory._readyCompleter.complete();return;}//若是异步或有其他依赖函数创建后的单例,要检查它若依赖于其他单例被创建后再创建if((isAsync||(dependsOn.isNotEmptyfalse))&type==_ServiceFactoryType.constant){///AnyclientawaitingthecompletionofthisSingleton///HastowaitforthecompletionoftheSingletonitselfaswell///asforthecompletionofalltheSingletonsthisonedependson///Forthisweuse[outerFutureGroup]///A`FutureGroup`completesonlyifit'sclosedandallcontained///FutureshavecompletedfinalouterFutureGroup=FutureGroup();FuturedependentFuture;if(dependsOn.isNotEmptyfalse){//有异步依赖就等待单例外部依赖的函数完成稿后finaldependentFutureGroup=FutureGroup();for(finaltypeindependsOn){finaldependentFactory=_factories[type];throwIf(dependentFactory==null,ArgumentError('DependentType$typeisnotregisteredinGetIt'));throwIfNot(dependentFactory.canBeWaitedFor,ArgumentError('DependentType$typeisnotanasyncSingleton'));dependentFactory.objectsWaiting.add(serviceFactory.registrationType);dependentFutureGroup.add(dependentFactory._readyCompleter.future);}dependentFutureGroup.close();//依赖的函数完成了dependentFuture=dependentFutureGroup.future;}else{//无依赖直接执行dependentFuture=Future.sync((){});}outerFutureGroup.add(dependentFuture);///ifsomeoneusesgetAsynconanasyncSingletonthathasnotbestartedtogetcreated///becauseitsdependentonotherobjectsthisdoesn'tworkbecause[pendingResult]isnotsetin///thatcase.Thereforewehavetoset[outerFutureGroup]as[pendingResult]dependentFuture.then((_){FutureisReadyFuture;if(!isAsync){//非异步,单例有依赖serviceFactory.instance=factoryFunc();isReadyFuture=Future.value(serviceFactory.instanceasT);if(!serviceFactory.shouldSignalReady){//单例未创建完成//标记为完成serviceFactory._readyCompleter.complete();}}else{//异步单例finalasyncResult=factoryFuncAsync();isReadyFuture=asyncResult.then((instance){serviceFactory.instance=instance;if(!serviceFactory.shouldSignalReady&!serviceFactory.isReady){serviceFactory._readyCompleter.complete();}returninstance;});}outerFutureGroup.add(isReadyFuture);outerFutureGroup.close();});//等待返回的结果是outerFutureGroup列表中的最近一个serviceFactory.pendingResult=outerFutureGroup.future.then((completedFutures){returncompletedFutures.lastasT;});}}4.3 调用以调用get函数的源码分析Tget({StringinstanceName,dynamicparam1,dynamicparam2,}){//一、获取数组_findFactoryByNameOrTypefinalinstanceFactory=_findFactoryByNameAndType(instanceName);Objectinstance=Object;//lateif(instanceFactory.isAsync||instanceFactory.pendingResult!=null){///Weuseanasserthereinsteadofan`if..throw`forperformancereasonsassert(instanceFactory.factoryType==_ServiceFactoryType.constant||instanceFactory.factoryType==_ServiceFactoryType.lazy,"Youcan'tusegetwithanasyncFactoryof${instanceNameT.toString()}.",);assert(instanceFactory.isReady,'Youtriedtoaccessaninstanceof${instanceNameT.toString()}thatisnotreadyyet',);instance=instanceFactory.instance!;}else{//二、数组中获取对应对象instance=instanceFactory.getObject(param1,param2);}assert(instanceisT,'Objectwithname$instanceNamehasadifferenttype''(${instanceFactory.registrationType.toString()})thantheonethatisinferred''(${T.toString()})whereyoucallit',);returninstanceasT;}//一、获取数组_findFactoryByNameOrType///Isusedbyseveralotherfunctionstoretrievethecorrect[_ServiceFactory]_ServiceFactory_findFactoryByNameAndType(StringinstanceName,[Typetype,]){finalinstanceFactory=_findFirstFactoryByNameAndTypeOrNull(instanceName,type:type);assert(instanceFactory!=null,//ignore:missing_whitespace_between_adjacent_strings'Object/factorywith${instanceName!=null'withname$instanceNameand':''}''type${T.toString()}isnotregisteredinsideGetIt.''\n(DidyouaccidentallydoGetItsl=GetIt.instance();insteadofGetItsl=GetIt.instance;''\nDidyouforgettoregisterit)',);returninstanceFactory!;}///Isusedbyseveralotherfunctionstoretrievethecorrect[_ServiceFactory]_ServiceFactory_findFirstFactoryByNameAndTypeOrNull(StringinstanceName,{Typetype,boollookInScopeBelow=false}){///Weuseanasserthereinsteadofan`if..throw`becauseitgetscalledoneverycall///of[get]///`(constObject()is!T)`testsif[T]isarealtypeandnotObjectordynamicassert(type!=null||constObject()is!T,'GetIt:Thecompilercouldnotinferthetype.Youhavetoprovideatype''andoptionallyaname.Didyouaccidentallydo`varsl=GetIt.instance();`''insteadofvarsl=GetIt.instance;',);_ServiceFactoryinstanceFactory;intscopeLevel=_scopes.length-(lookInScopeBelow2:1);//快速查找while(instanceFactory==null&scopeLevel>=0){//注意:factoriesByName =>>{};//factoriesByName这是个Map,所以复杂度是O(1))finalfactoryByTypes=_scopes[scopeLevel].factoriesByName[instanceName];if(type==null){instanceFactory=factoryByTypes!=nullfactoryByTypes[T]as_ServiceFactory:null;}else{///inmostcaseswecanrelyonthegenerictypeTbecauseitispassed///inbycallers.Incaseofdependenttypesthisdoesnotworkasthesetypes///aredynamicinstanceFactory=factoryByTypes!=nullfactoryByTypes[type]as_ServiceFactory:null;}scopeLevel--;}returninstanceFactory;}//二、数组中获取对应对象///returnsaninstancedependingonthetypeoftheregistrationif[async==false]TgetObject(dynamicparam1,dynamicparam2){assert(!(factoryType!=_ServiceFactoryType.alwaysNew&(param1!=null||param2!=null)),'Youcanonlypassparameterstofactories!');try{switch(factoryType){//始终创建最新的模式case_ServiceFactoryType.alwaysNew:if(creationFunctionParam!=null){returncreationFunctionParam(param1asP1,param2asP2);}else{returncreationFunction();}break;//常量模式case_ServiceFactoryType.constant:returninstanceasT;break;//懒汉模式case_ServiceFactoryType.lazy:if(instance==null){instance=creationFunction();_readyCompleter.complete();}returninstanceasT;break;default:throwStateError('ImpossiblefactoryType');}}catch(e,s){print('Errorwhilecreating${T.toString()}');print('Stacktrace:\n$s');rethrow;}}总结GetIt作为一个常用的动态服务定位器,一个工具箱,摆脱了InheritedWidget、Provider、Singleton、IoC等苛刻繁琐的流程,简单易用,脱颖而出。作为Flutter开发者,经常流于表面的使用,当静下心来分析其源码,自有一套完整健全的逻辑。作为使用者,管中窥豹,时见一斑。作为剖析者,细品,别有一番风味。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 01:14 , Processed in 0.736966 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表