Flutter狀態(tài)管理終極方案GetX第三篇——依賴注入

GetX第三篇-依賴注入

為什么要使用依賴注入

依賴注入是什么

本來(lái)接受各種參數(shù)來(lái)構(gòu)造一個(gè)對(duì)象涩咖,現(xiàn)在只接受一個(gè)參數(shù)——已經(jīng)實(shí)例化的對(duì)象。

依賴注入的目的

依賴注入是為了將依賴組件的配置和使用分離開为居,以降低使用者與依賴之間的耦合度。

依賴注入的好處

實(shí)現(xiàn)依賴項(xiàng)注入可為您帶來(lái)以下優(yōu)勢(shì):

  • 重用代碼
    更容易換掉依賴項(xiàng)的實(shí)現(xiàn)讥巡。由于控制反轉(zhuǎn)肖爵,代碼重用得以改進(jìn),并且類不再控制其依賴項(xiàng)的創(chuàng)建方式悄泥,而是支持任何配置虏冻。
  • 易于重構(gòu)
    依賴項(xiàng)的創(chuàng)建分離,可以在創(chuàng)建對(duì)象時(shí)或編譯時(shí)進(jìn)行檢查弹囚、修改厨相,一處修改,使用處不需修改。
  • 易于測(cè)試
    類不管理其依賴項(xiàng)蛮穿,因此在測(cè)試時(shí)庶骄,您可以傳入不同的實(shí)現(xiàn)以測(cè)試所有不同用例。

舉個(gè)例子

老王的瑪莎拉蒂需要換個(gè)v8引擎践磅,他是自己拼裝個(gè)引擎呢還是去改裝店買一個(gè)呢单刁?
如果自己拼裝個(gè),引擎的構(gòu)造更新了府适,他需要學(xué)習(xí)改進(jìn)自己的技術(shù)羔飞,買新零件,而直接買一個(gè)成品檐春,就是依賴注入褥傍。

class Car(private val engineParts: String,val enginePiston: String) {

    fun start() {
        val engine= Engine(engineParts,enginePiston)
        engine.start()
    }
}

class Engine(private val engineParts: String,val enginePiston: String){
}

上面代碼中的 Engine 類如果構(gòu)造方法變動(dòng)了,也需要去 Car 類里更改喇聊。而使用依賴注入就不需要改動(dòng) Car 類恍风。

手動(dòng)實(shí)現(xiàn)依賴注入通常有兩種,構(gòu)造函數(shù)傳入和字段傳入誓篱。
構(gòu)造方法:

class Car(private val engine: Engine) {
    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val engine = Engine()
    val car = Car(engine)
    car.start()
}

字段傳入:

class Car {
    lateinit var engine: Engine

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.engine = Engine()
    car.start()
}

上面雖然實(shí)現(xiàn)了依賴注入朋贬,但是增加了樣板代碼,如果注入實(shí)例多窜骄,也很麻煩锦募。Android 上有 DaggerHilt 實(shí)現(xiàn)自動(dòng)注入, GetX 也給我們提供了 Binding 類實(shí)現(xiàn)邻遏。

使用依賴注入

Get有一個(gè)簡(jiǎn)單而強(qiáng)大的依賴管理器糠亩,它允許你只用1行代碼就能檢索到 Controller 或者需要依賴的類,不需要提供上下文准验,不需要在 inheritedWidget 的子節(jié)點(diǎn)赎线。

注入依賴:

Get.put<PutController>(PutController());

獲取依賴:

Get.find<PutController>();

就是這么簡(jiǎn)單。

Get.put()

這是個(gè)立即注入內(nèi)存的注入方法糊饱。調(diào)用后已經(jīng)注入到內(nèi)存中垂寥。

Get.put<S>(
  // 必備:要注入的類。
  // 注:" S "意味著它可以是任何類型的類另锋。
  S dependency

  // 可選:想要注入多個(gè)相同類型的類時(shí)滞项,可以用這個(gè)方法。
  // 比如有兩個(gè)購(gòu)物車實(shí)例夭坪,就需要使用標(biāo)簽區(qū)分不同的實(shí)例文判。
  // 必須是唯一的字符串。
  String tag,

  // 可選:默認(rèn)情況下室梅,get會(huì)在實(shí)例不再使用后進(jìn)行銷毀
  // (例如:一個(gè)已經(jīng)銷毀的視圖的Controller)
  // 如果需要這個(gè)實(shí)例在整個(gè)應(yīng)用生命周期中都存在戏仓,就像一個(gè)sharedPreferences的實(shí)例潭流。
  // 默認(rèn)值為false
  bool permanent = false,

  // 可選:允許你在測(cè)試中使用一個(gè)抽象類后,用另一個(gè)抽象類代替它柜去,然后再進(jìn)行測(cè)試灰嫉。
  // 默認(rèn)為false
  bool overrideAbstract = false,

  // 可選:允許你使用函數(shù)而不是依賴(dependency)本身來(lái)創(chuàng)建依賴。
  // 這個(gè)不常用
  InstanceBuilderCallback<S> builder,
)

permanent是代表是否不銷毀嗓奢。通常Get.put()的實(shí)例的生命周期和 put 所在的 Widget 生命周期綁定讼撒,如果在全局 (main 方法里)put,那么這個(gè)實(shí)例就一直存在股耽。如果在一個(gè) Widget 里 put 根盒,那么這個(gè)那么這個(gè) Widget 從內(nèi)存中刪除,這個(gè)實(shí)例也會(huì)被銷毀物蝙。注意炎滞,這里是刪除,并不是dispose诬乞,具體看上一篇最后的部分册赛。

Get.lazyPut

懶加載一個(gè)依賴,只有在使用時(shí)才會(huì)被實(shí)例化震嫉。適用于不確定是否會(huì)被使用的依賴或者計(jì)算高昂的依賴森瘪。類似 Kotlin 的 Lazy 代理。

  Get.lazyPut<LazyController>(() => LazyController());

LazyController 在這時(shí)候并不會(huì)被創(chuàng)建票堵,而是等到你使用的時(shí)候才會(huì)被 initialized扼睬,也就是執(zhí)行下面這句話的時(shí)候才 initialized

Get.find<LazyController>();

在使用后,使用時(shí)的 Wdiget 的生命周期結(jié)束悴势,也就是這個(gè) Widgetdispose窗宇,這個(gè)實(shí)例就會(huì)被銷毀。

如果在一個(gè) Widget 里 find特纤,然后退出這個(gè) widget军俊,此時(shí)這個(gè)實(shí)例也被銷毀,再進(jìn)入另一個(gè)路由的 Widget叫潦,再次 find蝇完,GetX會(huì)打印錯(cuò)誤信息官硝,提醒沒有 put 矗蕊。及時(shí)全局注入,也一樣氢架∩悼В可以理解為,Get.lazyPut 注入的實(shí)例的生命周期是和在Get.find時(shí)的上下文所綁定岖研。

如果想每次 find 獲取到不同的實(shí)例卿操,可以借助fenix參數(shù)警检。

Get.lazyPut<S>(
  // 必須:當(dāng)你的類第一次被調(diào)用時(shí),將被執(zhí)行的方法害淤。
  InstanceBuilderCallback builder,
  
  // 可選:和Get.put()一樣扇雕,當(dāng)你想讓同一個(gè)類有多個(gè)不同的實(shí)例時(shí),就會(huì)用到它窥摄。
  // 必須是唯一的
  String tag,

  // 可選:下次使用時(shí)是否重建镶奉,
  // 當(dāng)不使用時(shí),實(shí)例會(huì)被丟棄崭放,但當(dāng)再次需要使用時(shí)哨苛,Get會(huì)重新創(chuàng)建實(shí)例,
  // 就像 bindings api 中的 "SmartManagement.keepFactory "一樣币砂。
  // 默認(rèn)值為false
  bool fenix = false
  
)

Get.putAsync

注入一個(gè)異步創(chuàng)建的實(shí)例建峭。比如SharedPreferences

  Get.putAsync<SharedPreferences>(() async {
    final sp = await SharedPreferences.getInstance();
    return sp;
  });

作用域參考Get.put决摧。

Get.create

這個(gè)方法可以創(chuàng)建很多實(shí)例亿蒸。很少用到≌谱可以當(dāng)做Get.put祝懂。

Bindings類

上面實(shí)現(xiàn)了依賴注入和使用,但是和前面講的手動(dòng)注入一樣拘鞋,為了生命周期和使用的 Widget 綁定砚蓬,需要在 Widget 里注入和使用,并沒有完全解耦盆色。要實(shí)現(xiàn)自動(dòng)注入灰蛙,我們就需要這個(gè)類。

這個(gè)包最大的區(qū)別之一隔躲,也許就是可以將路由摩梧、狀態(tài)管理器和依賴管理器完全集成。 當(dāng)一個(gè)路由從Stack中移除時(shí)宣旱,所有與它相關(guān)的控制器仅父、變量和對(duì)象的實(shí)例都會(huì)從內(nèi)存中移除。如果你使用的是流或定時(shí)器浑吟,它們會(huì)自動(dòng)關(guān)閉笙纤,我們不必?fù)?dān)心這些。Bindings 類是一個(gè)解耦依賴注入的類组力,同時(shí) "Binding " 路由到狀態(tài)管理器和依賴管理器省容。 這使得 GetX 可以知道當(dāng)使用某個(gè)控制器時(shí),哪個(gè)頁(yè)面正在顯示燎字,并知道在哪里以及如何銷毀它腥椒。 此外阿宅,Bindings 類將允許我們利用 SmartManager 配置控制。

  • 創(chuàng)建一個(gè)類并實(shí)現(xiàn)Binding
class InjectSimpleBinding implements Bindings {}

因?yàn)?code>Bindings是抽象方法笼蛛,所以要ide會(huì)提示要實(shí)現(xiàn)dependencies洒放。在里面注入我們需要的實(shí)例:

class InjectSimpleBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut<Api>(() => Api());
    Get.lazyPut<InjectSimpleController>(() => InjectSimpleController());
  }
}
  • 通知路由,我們要使用該 Binding 來(lái)建立路由管理器滨砍、依賴關(guān)系和狀態(tài)之間的連接拉馋。

這里有兩種方式,如果使用的是命名路由表:

    GetPage(
      name: Routes.INJECT,
      page: () => InjectSimplePage(),
      binding:InjectSimpleBinding(),
    ),

如果是直接跳轉(zhuǎn):

Get.to(InjectSimplePage(), binding: InjectSimpleBinding());

現(xiàn)在惨好,我們不必再擔(dān)心應(yīng)用程序的內(nèi)存管理煌茴,Get將為我們做這件事。

上面我們注入依賴解耦了日川,但是獲取還是略顯不方便蔓腐,GetX 也為我們考慮到了。GetView完美的搭配 Bindings龄句。

class InjectSimplePage extends GetView<InjectSimpleController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('MyPage')),
      body: Center(
        child: Obx(() => Text(controller.obj.toString())),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.getAge();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

這里完全沒有Get.find回论,但是可以直接使用controller,因?yàn)?code>GetView里封裝好了:

abstract class GetView<T> extends StatelessWidget {
  const GetView({Key key}) : super(key: key);

  final String tag = null;

  T get controller => GetInstance().find<T>(tag: tag);

  @override
  Widget build(BuildContext context);
}

不在需要 StatelessWidget 和 StatefulWidget分歇。這也是開發(fā)最常用的模式傀蓉,推薦大家使用。

當(dāng)然职抡,也許有時(shí)候覺得每次聲明一個(gè) Bingings 類也很麻煩葬燎,那么可以使用 BindingsBuilder ,這樣就可以簡(jiǎn)單地使用一個(gè)函數(shù)來(lái)實(shí)例化任何想要注入的東西缚甩。

  GetPage(
    name: '/details',
    page: () => DetailsView(),
    binding: BindingsBuilder(() => {
      Get.lazyPut<DetailsController>(() => DetailsController());
    }),

就是這么簡(jiǎn)單谱净,Bingings 都不需要?jiǎng)?chuàng)建。兩種方式都可以擅威,大家根據(jù)自己的編碼習(xí)慣選擇最適合的風(fēng)格壕探。

Bindings的工作原理

Bindings 會(huì)創(chuàng)建過(guò)渡性工廠,在點(diǎn)擊進(jìn)入另一個(gè)頁(yè)面的那一刻郊丛,這些工廠就會(huì)被創(chuàng)建李请,一旦路由過(guò)渡動(dòng)畫發(fā)生,就會(huì)被銷毀厉熟。 工廠占用的內(nèi)存很少导盅,它們并不持有實(shí)例,而是一個(gè)具有我們想要的那個(gè)類的 "形狀"的函數(shù)庆猫。 這在內(nèi)存上的成本很低认轨,但由于這個(gè)庫(kù)的目的是用最少的資源獲得最大的性能,所以Get連工廠都默認(rèn)刪除月培。

智能管理

GetX 默認(rèn)情況下會(huì)將未使用的控制器從內(nèi)存中移除嘁字。 但是如果想改變GetX控制類的銷毀方式怎么辦呢,可以用SmartManagement 類設(shè)置不同的行為杉畜。

如何改變

如果想改變這個(gè)配置(通常不需要)纪蜒,就用這個(gè)方法。

void main () {
  runApp(
    GetMaterialApp(
      smartManagement: SmartManagement.onlyBuilders //這里
      home: Home(),
    )
  )
}
  • SmartManagement.full
    這是默認(rèn)的此叠。銷毀那些沒有被使用的纯续、沒有被設(shè)置為永久的類。在大多數(shù)情況下灭袁,我們都使用這個(gè)猬错,不需要更改。

  • SmartManagement.onlyBuilders
    使用該選項(xiàng)茸歧,只有在init:中啟動(dòng)的控制器或用Get.lazyPut()加載到Binding中的控制器才會(huì)被銷毀倦炒。

    如果使用Get.put()或Get.putAsync()或任何其他方法,SmartManagement 沒有權(quán)限也就是不能移除這個(gè)依賴软瞎。

  • SmartManagement.keepFactory
    就像SmartManagement.full一樣逢唤,當(dāng)它不再被使用時(shí),它將刪除它的依賴關(guān)系涤浇,但它將保留它們的工廠鳖藕,這意味著如果再次需要該實(shí)例,它將重新創(chuàng)建該依賴關(guān)系只锭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末著恩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蜻展,更是在濱河造成了極大的恐慌页滚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铺呵,死亡現(xiàn)場(chǎng)離奇詭異裹驰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)片挂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門幻林,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人音念,你說(shuō)我怎么就攤上這事沪饺。” “怎么了闷愤?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵整葡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我讥脐,道長(zhǎng)遭居,這世上最難降的妖魔是什么啼器? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮俱萍,結(jié)果婚禮上端壳,老公的妹妹穿的比我還像新娘。我一直安慰自己枪蘑,他們只是感情好损谦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岳颇,像睡著了一般照捡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上话侧,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天栗精,我揣著相機(jī)與錄音,去河邊找鬼掂摔。 笑死术羔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乙漓。 我是一名探鬼主播级历,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叭披!你這毒婦竟也來(lái)了寥殖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涩蜘,失蹤者是張志新(化名)和其女友劉穎嚼贡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體同诫,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粤策,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了误窖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叮盘。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霹俺,靈堂內(nèi)的尸體忽然破棺而出柔吼,到底是詐尸還是另有隱情,我是刑警寧澤丙唧,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布愈魏,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏培漏。R本人自食惡果不足惜溪厘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望北苟。 院中可真熱鬧桩匪,春花似錦打瘪、人聲如沸友鼻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)彩扔。三九已至,卻和暖如春僻爽,著一層夾襖步出監(jiān)牢的瞬間虫碉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工胸梆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敦捧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓碰镜,卻偏偏與公主長(zhǎng)得像兢卵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绪颖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容