看一個簡單dagger2的Demo1
目標類B
public class B { @Inject public B() { } public String getName(){ return "BBBBBBB"; }}頁面Activitypublic class MainActivity extends BaseActivity { @Inject B b; @Override PRotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //實例化component對象,注意DaggerBComponent是生成的,所以打這行代碼時,先編譯一下 BComponent component = DaggerBComponent.builder().build(); //注入 component.injectB(this); Log.e("-------","通過inject來實現======"+b.getName()); }}@Componentpublic interface BComponent { void injectB(Main2Activity activity);}運行,打印的結果為"通過...======BBBBBBB"
其中Inject注解(Annotation)來標注目標類中所依賴的其他類(MainActivity中的B),同樣用注解來標注所依賴的其他類的構造函數(B的構造方法)
1:用Inject注解標注目標類中的其他類(MainActivity中的屬性B)
2:用Inject注解標注其他類的構造方法(B類的構造方法)
3:用Inject注解標注屬性時,屬性不能用private修飾,不然會報錯,如下:
Error:(13, 20) 錯誤: Dagger does not support injection into private fields原因是Dagger2是采用編譯時產生注入代碼(一個注入工具類)來為變量注入值,注入過程如下所示:
public void injectMembers(MainActivity instance) { if (instance == null) { throw new NullPointerException("Cannot inject members into a null reference"); } supertypeInjector.injectMembers(instance); instance.str = strProvider.get();//為變量str注入值,若變量str為private,此處就無法引用到str}補充:inject還可以標注在方法里,使用@Inject注解提供依賴的方式是在這個類的public方法中作注解:
public class LoginActivityPresenter { private LoginActivity loginActivity; @Inject public LoginActivityPresenter(LoginActivity loginActivity) { this.loginActivity = loginActivity; } @Inject public void enableWatches(Watches watches) { watches.register(this); //Watches instance required fully constructed LoginActivityPresenter }}方法的所有參數都是通過依賴圖表提供的。但是為什么我們需要方法注入呢?在某些情況下會用到,如當我們希望傳入類的當前實例(this引用)到被注入的依賴中。方法注入會在構造器調用后馬上被調用,所以這表示我們可以傳入完全被構造的this。因為enableWatches方法里需要用到LoginActivityPresenter對象實例,而用inject標注這個方法的作用在于,當這個構造方法被調用時,會把這個實例傳給watches.register(this)中去.
其中Component也是一個注解類,一個類要想是Component,
1:必須用Component注解來標注該類
2:該類是接口或抽象類
3:方法名必須是injectXXX(Object),參數object是表示你要注入到哪里去,這里不能寫父類BaseActivity,因為具體寫了哪個類,它就到哪個類中找Inject標注的屬性
上文中提到Component在目標類中所依賴的其他類與其他類的構造函數之間可以起到一個橋梁的作用。
這個demo中component的工作原理是:
Component會查找MainActivity中用Inject注解標注的屬性(B),查找到相應的屬性后會接著查找該屬性(B)對應的用Inject標注的構造函數(這時候就發生聯系了),剩下的工作就是初始化該屬性的實例并把實例進行賦值。因此我們也可以給Component叫另外一個名字注入器(Injector)
Demo1還有種是對象B的構造方法是由參數的,那么又應該怎么弄呢?后面會有說明
引出Module類
項目中使用到了第三方的類庫,第三方類庫又不能修改,所以根本不可能把Inject注解加入這些類中,這時我們的Inject就失效了。
那么就需要封裝第三方的類庫,封裝的代碼怎么管理呢,總不能讓這些封裝的代碼散落在項目中的任何地方,總得有個好的管理機制,那Module就可以擔當此任。可以把封裝第三方類庫的代碼放入Module中,像Demo2:
目標類A
public class A { String aa = "小花";//注意這里沒有用inject標注 public A() { }}Moudle類(類名命名木有要求)
@Modulepublic class AModule { @Provides A providesA(){ return new A(); }}Component類(類名命名木有要求)
@Component(modules = {AModule.class})public interface AComponent { void inject(NewActivity act);}頁面NewActivity
public class NewActivity extends AppCompatActivity { @Inject A a1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_new); //實例化component對象 AComponent component = DaggerAComponent.builder().aModule(new AModule()).build(); //注入 component.inject(this); Log.e("---------",a1.aa); }}打印結果:小花
其中,Module類其實是簡單工廠模式,類里面基本都是創建類實例的方法
這時Component是怎樣的工作原理:
Component是注入器,它一端連接目標類,另一端連接目標類依賴實例,它把目標類依賴實例注入到目標類中。上文中的Module是一個提供類實例的類,所以Module應該是屬于Component的實例端的(連接各種目標類依賴實例的端),Component的新職責就是管理好Module,Component中的modules屬性可以把Module加入Component,modules可以加入多個Module。
Module中的創建類實例方法用Provides進行標注,Component在搜索到目標類中用Inject注解標注的屬性后,Component就會去Module中去查找用Provides標注的對應的創建類實例方法,這樣就可以解決第三方類庫用dagger2實現依賴注入了。
那么問題來了,如果實例D的構造也用@Inject標注的話,這時Component應該怎么作為橋梁的呢?
首先Component會搜索目標類中用Inject標注的屬性(B),然后如果有Module類,就會先從Module類中找,看有沒有用@Provides標注的方法,是否提供了這個實例,有則創建返回;沒有的話,就去看這個實例(B)的構造方法是否Inject標注,有則,創建返回;也就是說,Component會先去從Module中找,沒有的話,才去構造中找
注意:Component可以包含一個或多個Module類,也可以不包含,Demo1即使不包含
如果構造是有參數的,Component應該怎么連接?
public class D { String name = "卡卡卡"; public D(B b) { }}以下是初始化目標類的具體步驟
步驟1:查找Module中是否存在創建該類的方法。步驟2:若存在創建類方法,查看該方法是否存在參數 步驟2.1:若存在參數,則按從**步驟1**開始依次初始化每個參數 步驟2.2:若不存在參數,則直接初始化該類實例,一次依賴注入到此結束步驟3:若不存在創建類方法,則查找Inject注解的構造函數, 看構造函數是否存在參數 步驟3.1:若存在參數,則從**步驟1**開始依次初始化每個參數 步驟3.2:若不存在參數,則直接初始化該類實例,一次依賴注入到此結束注意:如果參數沒有從module中找到,其構造又未被inject標注,則會報錯
所以:在module中,不要出現參數和返回值一樣,如上所述的話,會造成死循環
總結:
Inject,Component,Module,Provides是dagger2中的最基礎最核心的知識點。奠定了dagger2的整個依賴注入框架。
Inject主要是用來標注目標類的依賴和依賴的構造函數Component它是一個橋梁,一端是目標類,另一端是目標類所依賴類的實例,它也是注入器(Injector)負責把目標類所依賴類的實例注入到目標類中,同時它也管理Module。Module和Provides是為解決第三方類庫而生的,Module是一個簡單工廠模式,Module可以包含創建類實例的方法,這些方法用Provides來標注
新聞熱點
疑難解答