Flutter狀態管理終極方案GetX第二篇——依賴注入 GetX第三篇-依賴注入

GetX第三篇-依賴注入

爲什麼要使用依賴注入

依賴注入是什麼

本來接受各種參數來構造一個對象,現在只接受一個參數——已經實例化的對象。

依賴注入的目的

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

依賴注入的好處

實現依賴項注入可爲您帶來以下優勢:

  • 重用代碼
    更容易換掉依賴項的實現。由於控制反轉,代碼重用得以改進,並且類不再控制其依賴項的創建方式,而是支持任何配置。
  • 易於重構
    依賴項的創建分離,可以在創建對象時或編譯時進行檢查、修改,一處修改,使用處不需修改。
  • 易於測試
    類不管理其依賴項,因此在測試時,您可以傳入不同的實現以測試所有不同用例。

舉個例子

老王的瑪莎拉蒂需要換個v8引擎,他是自己拼裝個引擎呢還是去改裝店買一個呢?
如果自己拼裝個,引擎的構造更新了,他需要學習改進自己的技術,買新零件,而直接買一個成品,就是依賴注入。

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 類如果構造方法變動了,也需要去 Car 類裏更改。而使用依賴注入就不需要改動 Car 類。

手動實現依賴注入通常有兩種,構造函數傳入和字段傳入。
構造方法:

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()
}

上面雖然實現了依賴注入,但是增加了樣板代碼,如果注入實例多,也很麻煩。Android 上有 DaggerHilt 實現自動注入, GetX 也給我們提供了 Binding 類實現。

使用依賴注入

Get有一個簡單而強大的依賴管理器,它允許你只用1行代碼就能檢索到 Controller 或者需要依賴的類,不需要提供上下文,不需要在 inheritedWidget 的子節點。

注入依賴:

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

獲取依賴:

Get.find<PutController>();

就是這麼簡單。

Get.put()

這是個立即注入內存的注入方法。調用後已經注入到內存中。

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

  // 可選:想要注入多個相同類型的類時,可以用這個方法。
  // 比如有兩個購物車實例,就需要使用標籤區分不同的實例。
  // 必須是唯一的字符串。
  String tag,

  // 可選:默認情況下,get會在實例不再使用後進行銷燬
  // (例如:一個已經銷燬的視圖的Controller)
  // 如果需要這個實例在整個應用生命週期中都存在,就像一個sharedPreferences的實例。
  // 默認值爲false
  bool permanent = false,

  // 可選:允許你在測試中使用一個抽象類後,用另一個抽象類代替它,然後再進行測試。
  // 默認爲false
  bool overrideAbstract = false,

  // 可選:允許你使用函數而不是依賴(dependency)本身來創建依賴。
  // 這個不常用
  InstanceBuilderCallback<S> builder,
)

permanent是代表是否不銷燬。通常Get.put()的實例的生命週期和 put 所在的 Widget 生命週期綁定,如果在全局 (main 方法裏)put,那麼這個實例就一直存在。如果在一個 Widget 裏 put ,那麼這個那麼這個 Widget 從內存中刪除,這個實例也會被銷燬。注意,這裏是刪除,並不是dispose,具體看上一篇最後的部分。

Get.lazyPut

懶加載一個依賴,只有在使用時纔會被實例化。適用於不確定是否會被使用的依賴或者計算高昂的依賴。類似 Kotlin 的 Lazy 代理。

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

LazyController 在這時候並不會被創建,而是等到你使用的時候纔會被 initialized,也就是執行下面這句話的時候才 initialized

Get.find<LazyController>();

在使用後,使用時的 Wdiget 的生命週期結束,也就是這個 Widgetdispose,這個實例就會被銷燬。

如果在一個 Widget 裏 find,然後退出這個 widget,此時這個實例也被銷燬,再進入另一個路由的 Widget,再次 find,GetX會打印錯誤信息,提醒沒有 put 。及時全局注入,也一樣。可以理解爲,Get.lazyPut 注入的實例的生命週期是和在Get.find時的上下文所綁定。

如果想每次 find 獲取到不同的實例,可以藉助fenix參數。

Get.lazyPut<S>(
  // 必須:當你的類第一次被調用時,將被執行的方法。
  InstanceBuilderCallback builder,
  
  // 可選:和Get.put()一樣,當你想讓同一個類有多個不同的實例時,就會用到它。
  // 必須是唯一的
  String tag,

  // 可選:下次使用時是否重建,
  // 當不使用時,實例會被丟棄,但當再次需要使用時,Get會重新創建實例,
  // 就像 bindings api 中的 "SmartManagement.keepFactory "一樣。
  // 默認值爲false
  bool fenix = false
  
)

Get.putAsync

注入一個異步創建的實例。比如SharedPreferences

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

作用域參考Get.put

Get.create

這個方法可以創建很多實例。很少用到。可以當做Get.put

Bindings類

上面實現了依賴注入和使用,但是和前面講的手動注入一樣,爲了生命週期和使用的 Widget 綁定,需要在 Widget 裏注入和使用,並沒有完全解耦。要實現自動注入,我們就需要這個類。

這個包最大的區別之一,也許就是可以將路由、狀態管理器和依賴管理器完全集成。 當一個路由從Stack中移除時,所有與它相關的控制器、變量和對象的實例都會從內存中移除。如果你使用的是流或定時器,它們會自動關閉,我們不必擔心這些。Bindings 類是一個解耦依賴注入的類,同時 "Binding " 路由到狀態管理器和依賴管理器。 這使得 GetX 可以知道當使用某個控制器時,哪個頁面正在顯示,並知道在哪裏以及如何銷燬它。 此外,Bindings 類將允許我們利用 SmartManager 配置控制。

  • 創建一個類並實現Binding
class InjectSimpleBinding implements Bindings {}

因爲Bindings是抽象方法,所以要ide會提示要實現dependencies。在裏面注入我們需要的實例:

class InjectSimpleBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut<Api>(() => Api());
    Get.lazyPut<InjectSimpleController>(() => InjectSimpleController());
  }
}
  • 通知路由,我們要使用該 Binding 來建立路由管理器、依賴關係和狀態之間的連接。

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

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

如果是直接跳轉:

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

現在,我們不必再擔心應用程序的內存管理,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,因爲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。這也是開發最常用的模式,推薦大家使用。

當然,也許有時候覺得每次聲明一個 Bingings 類也很麻煩,那麼可以使用 BindingsBuilder ,這樣就可以簡單地使用一個函數來實例化任何想要注入的東西。

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

就是這麼簡單,Bingings 都不需要創建。兩種方式都可以,大家根據自己的編碼習慣選擇最適合的風格。

Bindings的工作原理

Bindings 會創建過渡性工廠,在點擊進入另一個頁面的那一刻,這些工廠就會被創建,一旦路由過渡動畫發生,就會被銷燬。 工廠佔用的內存很少,它們並不持有實例,而是一個具有我們想要的那個類的 "形狀"的函數。 這在內存上的成本很低,但由於這個庫的目的是用最少的資源獲得最大的性能,所以Get連工廠都默認刪除。

智能管理

GetX 默認情況下會將未使用的控制器從內存中移除。 但是如果想改變GetX控制類的銷燬方式怎麼辦呢,可以用SmartManagement 類設置不同的行爲。

如何改變

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

void main () {
  runApp(
    GetMaterialApp(
      smartManagement: SmartManagement.onlyBuilders //這裏
      home: Home(),
    )
  )
}
  • SmartManagement.full
    這是默認的。銷燬那些沒有被使用的、沒有被設置爲永久的類。在大多數情況下,我們都使用這個,不需要更改。

  • SmartManagement.onlyBuilders
    使用該選項,只有在init:中啓動的控制器或用Get.lazyPut()加載到Binding中的控制器纔會被銷燬。

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

  • SmartManagement.keepFactory
    就像SmartManagement.full一樣,當它不再被使用時,它將刪除它的依賴關係,但它將保留它們的工廠,這意味着如果再次需要該實例,它將重新創建該依賴關係。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章