編輯:關於Android編程
依賴注入(DI)和控制反轉(IOC):
依賴注入是從應用程序的角度在描述,可以把依賴注入描述完整點:應用程序依賴容器創建並注入它所需要的外部資源;而控制反轉是從容器的角度在描述,描述完整點:容器控制應用程序,由容器反向的向應用程序注入應用程序所需要的外部資源。
使用依賴注入可以帶來以下好處:
依賴的注入和配置獨立於組件之外。
因為對象是在一個獨立、不耦合的地方初始化,所以當注入抽象方法的時候,我們只需要修改對象的實現方法,而不用大改代碼庫。
依賴可以注入到一個組件中:我們可以注入這些依賴的模擬實現,這樣使得測試更加簡單。
為了最大程度的提高代碼的復用性、測試性和維護性,java的依賴注入為注入類中的使用定義了一整套注解(和接口)標准 Java 依賴注入標准(JSR-330,Dependency Injection for Java)1.0 規范已於2009年 10 月份發布 。
Dagger1是Android上最流行的依賴注入框架。它是由Square公司受到Guice啟發創建的。Dagger2是Dagger1的分支,由谷歌公司接手開發,目前的版本是2.2。Dagger2是受到AutoValue項目的啟發
Android開發從一開始的MVC框架,到MVP,到MVVM,不斷變化。現在MVVM的data-binding還在實驗階段,傳統的MVC框架Activity內部可能包含大量的代碼,難以維護,現在主流的架構還是使用MVP(Model + View + Presenter)的方式。但是 MVP 框架也有可能在Presenter中集中大量的代碼,引入DI框架Dagger2 可以實現 Presenter 與 Activity 之間的解耦,Presenter和其它業務邏輯之間的解耦,提高模塊化和可維護性。
Dagger github地址為
https://github.com/square/dagger
https://github.com/google/dagger
Dagger2對Dagger的改進如下
再也沒有使用反射:圖的驗證、配置和預先設置都在編譯的時候執行。
容易調試和可跟蹤:完全具體地調用提供和創建的堆棧
更好的性能:谷歌聲稱他們提高了13%的處理性能
代碼混淆:使用派遣方法,就如同自己寫的代碼一樣
這裡我們只分析Dagger2的使用和源碼,以下的所有Dagger指的都是Dagger2
Dagger的gradle並不是簡單的在dependencies中加上相關compile就可以了,這裡說一下配置方法
dependencies { classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } apply plugin: 'com.neenbedankt.android-apt' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile group: 'com.google.dagger', name: 'dagger', version: '2.4' compile group: 'com.google.dagger', name: 'dagger-compiler', version: '2.4' }生成的中間文件在build/generated/source/apt/debug中
Dagger提供的注入依賴方法有:
構造方法注入:在類的構造方法前面注釋@Inject
成員變量注入:在類的成員變量(非私有)前面注釋@Inject
函數方法注入:在函數前面注釋@Inject
以上順序是Dagger建議使用的,因為在運行的過程中,總會有一些奇怪的問題甚至是空指針,這也意味著依賴在對象創建的時候可能還沒有初始化完成。
class Thermosiphon implements Pump { private final Heater heater; @Inject Thermosiphon(Heater heater) { this.heater = heater; } ... } class CoffeeMaker { @Inject Heater heater; ... } class CoffeeMaker { @Inject public void setPump(Pump pump){ this.pump=pump; } ... }
如果@Inject注解了成員變量,但是沒有注解構造方法,有需要的話會注入,但是不會創建新的對象。如果對無參構造函數進行注解,可以創建新對象。
沒有通過@Inject注解的類是不能用Dagger創建的。
接口不能構造。
第三方類不能注明。
配置對象必須配置
我們需要通過被 @Provides 注解所標注的方法來實現依賴。每個方法的返回的類型就是我們需要實現的依賴。
一旦需要注入一個 Heater,provideHeater() 就會被調用:
@Provides Heater provideHeater() { return new ElectricHeater(); }被 @Provides 標注的方法自身也可以有依賴。
@Provides Pump providePump(Thermosiphon pump) { return pump; }所有的 @Provides 標注的方法都必須屬於一個 module。用 @Module 標注的類就是一個 module。
@Module class DripCoffeeModule { @Provides Heater provideHeater() { return new ElectricHeater(); } @Provides Pump providePump(Thermosiphon pump) { return pump; } }
用 @Inject 和 @Provides 來標注的類最終會組成一個由對象構成的圖,
在Dagger2中,該集合依賴關系是一個接口定義的方法,沒有任何參數,只返回所需的類型。通過將@Componet注解應用到這樣的接口上,並將模塊類型傳遞給模塊參數。
Dagger是一個基於有向無環圖結構的依賴注入庫,DAG——有向無環圖(Directed Acyclic Graph),因此Dagger的使用過程中不能出現循環依賴。
@Component(modules = DripCoffeeModule.class) interface CoffeeShop { CoffeeMaker maker(); }具體實現是在接口名的基礎上加上Dagger前綴,調用builder方法設置依賴構建實例。
CoffeeShop coffeeShop = DaggerCoffeeShop.builder() .dripCoffeeModule(new DripCoffeeModule()) .build();如果 @Component不是在最外層注解,除了前綴,還需要用_連接外部結構的名字
class Foo { static class Bar { @Component interface BazComponent {} } }
生成的組件名稱為DaggerFoo_Bar_BazComponent
有一個可訪問的默認構造函數的module都可以作為構建者,如果沒有設置,會自動構造一個實例。而對於@Provides方法都是靜態的module,實現不需要實例。如果所有的依賴性會未經用戶創建一個實例的依賴構造,則生成的實現類將具有create()方法可用於獲取一個新的實例,而不必處理builder方式。
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
6、單例和使用范圍綁定
使用@Provides 注解的方法,加上 @Singleton,就能保證生成單例。整個生命周期中只有一個實例
使用@Singleton注解的類可以多線程共享。
不同的作用域的生命周期是不同的,單例Singleton注解(Application scope)是最長的scope
組件可能有多個作用域注釋。他們有相同的使用范圍,不同的注解相當於別名,所以組件可以包括任何其聲明作用域范圍的綁定。要聲明一個組件與給定作用域關聯,只需將scope注釋應用到組件接口。
@Provides @Singleton static Heater provideHeater() { return new ElectricHeater(); }
@Singleton class CoffeeMaker { ... }
@Component(modules = DripCoffeeModule.class) @Singleton interface CoffeeShop { CoffeeMaker maker(); }自定義scope
@Scope @Retention(RUNTIME) public @interface PerActivity { }
@PerActivity @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface HomeComponent extends AbstractActivityComponent { void inject(HomeActivity homeActivity); void inject(HomeFragment homeFragment); }
有時候需要限制@Inject注解的類實例化得對象個數,不需要保證任何組件或者子組件的生命周期內完全相同的實例使用,在android中內存比較珍貴,這種方式比較有用。使用@Reusable注解的類實例化對象之後會被緩存。父組件緩存之後,子類會重用。
誰也不能保證該組件將調用綁定只有一次,使用@Reusable綁定返回值可變的對象,或者指向相同實例很重要的對象,是很危險的。如果不關心創建多少對象,不可變對象不限定范圍,使用@Reusable注解是安全的。
@Reusable // It doesn't matter how many scoopers we use, but don't waste them. class CoffeeScooper { @Inject CoffeeScooper() {} } @Module class CashRegisterModule { @Provides @Reusable // DON'T DO THIS! You do care which register you put your cash in. // Use a specific scope instead. static CashRegister badIdeaCashRegister() { return new CashRegister(); } } @Reusable // DON'T DO THIS! You really do want a new filter each time, so this // should be unscoped. class CoffeeFilter { @Inject CoffeeFilter() {} }
有些時候需要懶加載,要用到Lazy
只有一個get方法,如果不調用,dagger不會創建對象
調用之後,創建對象並保存
再次調用,返回相同對象
class GridingCoffeeMaker { @Inject LazylazyGrinder; public void brew() { while (needsGrinding()) { // Grinder created once on first call to .get() and cached. lazyGrinder.get().grind(); } } }
有些情況下, 你需要多個對象實例, 而不是僅僅注入一個對象實例。這時你可以利用Provider實現, 每次調用Provider的get()函數將返回新的
class BigCoffeeMaker { @Inject ProviderfilterProvider; public void brew(int numberOfPots) { ... for (int p = 0; p < numberOfPots; p++) { maker.addFilter(filterProvider.get()); //new filter every time. maker.addCoffee(...); maker.percolate(); ... } } }
有時單獨類型是不足以識別區分依賴性。這個時候可以使用Qualifier注解,這裡使用@Named注解,
class ExpensiveCoffeeMaker { @Inject @Named("water") Heater waterHeater; @Inject @Named("hot plate") Heater hotPlateHeater; ... }
@Provides @Named("hot plate") static Heater provideHotPlateHeater() { return new ElectricHeater(70); } @Provides @Named("water") static Heater provideWaterHeater() { return new ElectricHeater(93); }
對這個框架使用的比較少,主要參考github文檔
http://google.github.io/dagger/users-guide.html
使用MVP比較少,做的項目基本都是MVC的,這個框架不是非常適用。不能充分發揮Dagger優勢。本文不是很深入。後期深入看一下源碼,再做詳細分析。
本例子演示如何添加一個簡單的單頁導航,在此基礎上,再演示如何在第2個頁面中顯示第1個頁面中撥打過的所有電話號碼。(1)通過該例子理解Android App的基本架構。(2
最近忙找實習,加上實驗室在推新項目,需要學習新知識。所以很長一段時間沒去整理了官博客了,github也蠻久沒更新,很慚愧。接下來還是要堅持寫。今天就簡單的寫一下我在項目中
使用C++ 語言編寫,模塊化設計,可以嵌入任何語言編寫的程序中,也可以嵌入各種流行腳本中直接調用。模塊內部封裝了所有的操作,外部只需要調用相應的導出函數即
前言當前的網絡開源庫有許多,如volley,okhttp,retrofit等,這三個庫當前是比較火的,其中,okhttp和retrofit由square團隊開發。關於這三