目錄
- 一:Dagger2是什么惯雳?
- 二:為什么要有Dagger2
- 三:Dagger2如何使用
- 基本的概念
- 如何使用Dagger2
- 高級(jí)用法
- (1)構(gòu)造方法需要其他參數(shù)時(shí)候
- (2) 模塊之間的依賴關(guān)系
- (3) @Named注解使用
- (4) @Singleton注解
- (5)自定義Scoped
- (6)Subcomponent
- (7)lazy 和 Provider
- 四: MVP + Dagger2
Ps:文末有架構(gòu)師進(jìn)階資料和面試題資料
一:Dagger2是什么队橙?
是一個(gè)依賴注入框架拦坠,butterknife也是一個(gè)依賴注入框架连躏。不過(guò)butterknife,最多叫奶油刀贞滨,Dagger2被叫做利器啊入热,他的主要作用,就是對(duì)象的管理晓铆,其目的是為了降低程序耦合勺良。
二:為什么要有Dagger2
下面我就手寫了
public class A {
public void eat() {
System.out.print("吃飯了");
}
}
使用的時(shí)候我們就要
A a = new A();
a.eat();
如果現(xiàn)在改了,早A的構(gòu)造方法中必須傳入B對(duì)象
public class A {
private B b;
public A(B b) {
this.b = b;
}
public void eat() {
System.out.print("吃飯了");
}
}
那么使用的時(shí)候
A a = new A(new B());
a.eat();
可能就有人說(shuō)了骄噪,不就加一個(gè)對(duì)象么尚困,這里只是我舉的一個(gè)很簡(jiǎn)單的例子,看的感覺很簡(jiǎn)單链蕊,但是在實(shí)際開發(fā)中尾组,如果現(xiàn)在改了一個(gè)這個(gè)構(gòu)造方法忙芒。是不是意味著,整個(gè)項(xiàng)目中的都的改讳侨,一不小心, 就是BUG 啊
三:Dagger2如何使用
1. 基本的概念
上來(lái)給你說(shuō)奏属,怎么玩跨跨,肯定懵逼,這里我簡(jiǎn)單說(shuō)一下幾個(gè)概念囱皿,想有個(gè)認(rèn)知勇婴,在往下看,會(huì)好很多嘱腥,Dagger 是通過(guò)@Inject使用具體的某個(gè)對(duì)象耕渴,這個(gè)對(duì)象呢,是由@Provides注解提供齿兔,但是呢橱脸,這個(gè)@Provides只能在固定的模塊中,也就是@Module注解分苇,我們查找的時(shí)候添诉,不是直接去找模塊,而是去找@Component
我們反向推導(dǎo)栏赴,當(dāng)我們使用
@Inject
A a
想要獲取a對(duì)象的示例的時(shí)候,Dagger2 會(huì)先去找靖秩,當(dāng)前Activity或者Fragment所連接的橋梁须眷,例如上圖中,連接的只有一個(gè)橋梁沟突,實(shí)際上可以有多個(gè)花颗,這個(gè)橋梁,會(huì)去尋找他所依賴的模塊事扭,如圖中捎稚,依賴了模塊A,和模塊B求橄,然后在模塊中今野,會(huì)去尋找@Providers注解,去尋找A的實(shí)例化對(duì)象罐农。
2. 如何使用Dagger2
(1) 引入依賴庫(kù)
Dagger2官網(wǎng)
compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
(2) 創(chuàng)建Moudule
//第一步 添加@Module 注解
@Module
public class MainModule {
}
(3)創(chuàng)建具體的示例
//第一步 添加@Module 注解
@Module
public class MainModule {
//第二步 使用Provider 注解 實(shí)例化對(duì)象
@Provides
A providerA() {
return new A();
}
}
(4)創(chuàng)建一個(gè)Component
//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class})
public interface MainComponent {
//第三步 寫一個(gè)方法 綁定Activity /Fragment
void inject(MainActivity activity);
}
(5)Rebuild Project
然后AS 會(huì)自動(dòng)幫我們生成一個(gè)
開頭都是以Dagger開始的
(6)將Component與Activity/Fragment綁定關(guān)系
package com.allens.daggerdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.allens.daggerdemo.Bean.A;
import com.allens.daggerdemo.component.DaggerMainConponent;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
/***
* 第二步 使用Inject 注解涵亏,獲取到A 對(duì)象的實(shí)例
*/
@Inject
A a;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/***
* 第一步 添加依賴關(guān)系
*/
//第一種方式
DaggerMainConponent.create().inject(this);
//第二種方式
DaggerMainConponent.builder().build().inject(this);
/***
* 第三步 調(diào)用A 對(duì)象的方法
*/
a.eat();
}
}
肯定有小伙伴說(shuō)了宰睡,為了拿到一個(gè)對(duì)象蒲凶,這么大個(gè)彎,太麻煩了拆内。別急慢慢看旋圆,路要一步一步走嘛
3. 高級(jí)用法
(1)構(gòu)造方法需要其他參數(shù)時(shí)候
怎么說(shuō)呢,就和最來(lái)時(shí)的意思一樣麸恍,
A a = new A(new B());
a.eat();
這種情況灵巧,如何使用Dagger2呢
肯定有小伙伴這么想
@Provides
A providerA() {
return new A(new B());
}
直接 new 一個(gè)B ,這樣的使用方法抹沪,是不對(duì)的?桃蕖!H谂贰C羝!噪馏!麦到,不對(duì)的!J判健S缫!6谩2角濉!虏肾,不對(duì)的@ !7夂馈G绰帧!4挡骸5诓健!缘琅!
正確的打開方式
這時(shí)候粘都,我們什么都不用該,只需要在moudule中添加一個(gè)依賴就可以了
@Module
public class MainModule {
/***
* 構(gòu)造方法需要其他參數(shù)時(shí)候
*
* @return
*/
@Provides
B providerB() {
return new B();
}
@Provides
A providerA(B b) {
return new A(b);
}
}
(2) 模塊之間的依賴關(guān)系
模塊與模塊之間的聯(lián)系刷袍,
@Module (includes = {BModule.class})// includes 引入)
public class AModule {
@Provides
A providerA() {
return new A();
}
}
這樣的話翩隧,Dagger會(huì)現(xiàn)在A moudule 中尋找對(duì)象,如果沒找到呻纹,會(huì)去找module B 中是否有被Inject注解的對(duì)象堆生,如果還是沒有专缠,那么GG,拋出異常
一個(gè)Component 應(yīng)用多個(gè) module
@Component(modules = {AModule.class,BModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}
dependencies 依賴其他Component
@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainConponent {
void inject(MainActivity activity);
}
注意 這里有坑淑仆。一下會(huì)講解
(3) @Named注解使用
相當(dāng)于有個(gè)表示涝婉,雖然大家都是同一個(gè)對(duì)象,但是實(shí)例化對(duì)象不同就不如
A a1 = new A();
A a2 = new A();
// a1 a2 能一樣嘛
Module中 使用@Named注解
@Module
public class MainModule {
private MainActivity activity;
public MainModule(MainActivity activity) {
this.activity = activity;
}
@Named("dev")
@Provides
MainApi provideMainApiDev(MainChildApi mainChildApi, String url) {
return new MainApi(mainChildApi, activity,"dev");
}
@Named("release")
@Provides
MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {
return new MainApi(mainChildApi, activity,"release");
}
}
在Activity/Fragment中使用
public class MainActivity extends AppCompatActivity {
@Named("dev")
@Inject
MainApi apiDev;
@Named("release")
@Inject
MainApi apiRelease;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.mainChildModule(new MainChildModule())
.build()
.inject(this);
apiDev.eat();
apiRelease.eat();
Log.i("TAG","apiDev--->" + apiDev);
Log.i("TAG","apiRelease--->" + apiRelease);
}
}
打印Log
07-14 01:46:01.170 2006-2006/? I/TAG: apiDev--->com.allen.rxjava.MainApi@477928f
07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->com.allen.rxjava.MainApi@f2b291c
(4) @Singleton注解
單利模式糯景,是不是超級(jí)方便嘁圈,你想然哪個(gè)對(duì)象單利化,直接在他的Provider上添加@Singleton 就行了
例如
@Singleton
@Provides
A providerA(B b) {
return new A(b);
}
注意: 第一個(gè)坑s盎础!钞澳!
如果 moudule所依賴的Comonent 中有被單利的對(duì)象怠惶,那么Conponnent也必須是單利的
@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
}
然后 在Activity中使用,直接打印a1 a2 的地址轧粟,
@Inject
A a2;
@Inject
A a1;
可以看到Log
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
不相信的小伙伴可以吧@Singleton去掉試試
現(xiàn)在我們完成了單利策治,然后做了一個(gè)事情,就是點(diǎn)擊某個(gè)按鈕兰吟,跳轉(zhuǎn)到一個(gè)新的Activiry,兩邊都引用同樣一個(gè)A 對(duì)象通惫,打印A 的地址,
說(shuō)一下混蔼,一個(gè)Conponent 可以被對(duì)個(gè)Activity/Fragment 引用,如
@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
void inject(MainActivity activity);
void inject(TestAct activity);
}
上面與兩個(gè)Activity, MainActivity 和 TestAct 履腋,都引用相同的 對(duì)象,答應(yīng)地址看看
12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->com.allens.daggerdemo.Bean.A@4f81861
竟然不同惭嚣,說(shuō)好的單利呢
注意: 第二個(gè)坑遵湖,單利對(duì)象只能在同一個(gè)Activity中有效。不同的Activity 持有的對(duì)象不同
那有人就要問(wèn)了晚吞,沒什么辦法么延旧,我就想全局只要一個(gè)實(shí)例化對(duì)象啊槽地? 辦法肯定是有的迁沫,
(5) 自定義Scoped
/**
* @作者 : Android架構(gòu)
* @創(chuàng)建日期 :2017/7/14 下午3:04
* @方法作用:
* 參考Singleton 的寫法
* Scope 標(biāo)注是Scope
* Documented 標(biāo)記在文檔
* @Retention(RUNTIME) 運(yùn)行時(shí)級(jí)別
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}
首先想一下,什么樣的對(duì)象捌蚊,能夠做到全局單例集畅,生命周期肯定和APP 綁定嘛,這里我做演示逢勾,一個(gè)AppAip 我們要對(duì)這個(gè)對(duì)象牡整,全局單利,所以二話不說(shuō)溺拱,先給Application 來(lái)個(gè)全家桶逃贝,
Module
@Module
public class AppModule {
@Singleton
@Provides
AppApi providerAppApi() {
return new AppApi();
}
}
Component
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
AppApi getAppApi();
}
Application
public class MyApp extends Application {
private AppConponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppConpoment.create();
}
public AppConponent getAppComponent() {
return appConponent;
}
}
最后是如何使用
首先谣辞,這個(gè)是個(gè)橋梁,依賴方式沐扳,上文已經(jīng)說(shuō)過(guò)了
@ActivityScoped
@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainComponent {
void inject(MainActivity activity);
void inject(TestAct activity);
}
細(xì)心的小伙伴可能已經(jīng)發(fā)現(xiàn)了泥从,只有在上面一個(gè)MainComponent添加了一個(gè)@ActivityScoped,這里說(shuō)明一下,@Singleton是Application 的單利
注意沪摄,第三個(gè)坑躯嫉,子類component 依賴父類的component ,子類component的Scoped 要小于父類的Scoped杨拐,Singleton的級(jí)別是Application
所以祈餐,我們這里的@Singleton 級(jí)別大于我們自定義的@ActivityScoped,同時(shí)哄陶,對(duì)應(yīng)module 所依賴的component 帆阳,也要放上相應(yīng)的Scope
好吧,上面的例子屋吨,打印Log.
12-30 02:16:30.899 4717-4717/? E/TAG: A1---->com.allens.daggerdemo.Bean.AppApi@70bfc2
12-30 02:16:31.009 4717-4717/? E/TAG: A2---->com.allens.daggerdemo.Bean.AppApi@70bfc2
一樣啦
爬坑指南(極度重要)
- Provide 如果是單例模式 對(duì)應(yīng)的Compnent 也要是單例模式
- inject(Activity act) 不能放父類
- 即使使用了單利模式蜒谤,在不同的Activity 對(duì)象還是不一樣的
- 依賴component, component之間的Scoped 不能相同
- 子類component 依賴父類的component 至扰,子類component的Scoped 要小于父類的Scoped鳍徽,Singleton的級(jí)別是Application
- 多個(gè)Moudle 之間不能提供相同的對(duì)象實(shí)例
- Moudle 中使用了自定義的Scoped 那么對(duì)應(yīng)的Compnent 使用同樣的Scoped
(6)Subcomponent
這個(gè)是系統(tǒng)提供的一個(gè)Component,當(dāng)使用Subcomponent,那么默認(rèn)會(huì)依賴Component
例如
@Subcomponent(modules = TestSubModule.class)
public interface TestSubComponent {
void inject(MainActivity activity);
}
@Component(modules = {MainModule.class})
public interface MainConponent {
TestSubComponent add(TestSubModule module);
}
在TestSubComponent中 我void inject(MainActivity activity);敢课,便是這個(gè)橋梁阶祭,我是要注入到MainActivity,但是dagger 并不會(huì)給我生成一個(gè)Dagger開頭的DaggerTestSubComponent 這個(gè)類,如果我想使用TestSubModule.class里面提供的對(duì)象翎猛,依然還是使用DaggerMainConponent例如
DaggerMainConponent
.builder()
.mainModule(new MainModule())
.build()
.add(new TestSubModule())
.inject(this);
可以看到這里有一個(gè)add的方法胖翰,真是我在MainConponent添加的TestSubComponent add(TestSubModule module);
(7)lazy 和 Provider
public class Main3Activity extends AppCompatActivity {
@PresentForContext
@Inject
Lazy<Present> lazy;
@PresentForName
@Inject
Provider<Present> provider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
ActivityComponent activityComponent = DaggerActivityComponent.builder()
.appComponent(appComponent)
.activityModule(new ActivityModule())
.build();
activityComponent.injectActivity(this);
Present present = lazy.get();
Present present1 = provider.get();
}
}
其中Lazy(懶加載)的作用好比component初始化了一個(gè)present對(duì)象,然后放到一個(gè)池子里,需要的時(shí)候就get它,所以你每次get的時(shí)候拿到的對(duì)象都是同一個(gè);并且當(dāng)你第一次去get時(shí),它才會(huì)去初始化這個(gè)實(shí)例.
procider(強(qiáng)制加載)的作用:
1:同上當(dāng)你第一次去get時(shí),它才會(huì)去初始化這個(gè)實(shí)例
2:后面當(dāng)你去get這個(gè)實(shí)例時(shí),是否為同一個(gè),取決于他Module里實(shí)現(xiàn)的方式
四: MVP + Dagger2
這是現(xiàn)在主流的設(shè)計(jì)架構(gòu)
MVP,這個(gè)我會(huì)在后面的文章介紹切厘,這里不做太多解釋
當(dāng)你了解MVP 的時(shí)候萨咳,你就知道,所有的業(yè)務(wù)邏輯全在Presenter疫稿,
換句話培他, presenter 持有的對(duì)象,控制著你程序的全部邏輯遗座,這在dagger 中舀凛,講白了 我們只要將所有的presetner 對(duì)象控制就可以了
下面附上目錄結(jié)構(gòu),當(dāng)然僅僅作為參考途蒋。dagger 強(qiáng)大的用法還是需要各位自己去體會(huì)猛遍,下面的項(xiàng)目 是我剛剛學(xué)會(huì)dagger 時(shí)候 寫的一個(gè)項(xiàng)目
可以看到 懊烤,我是 將所有的activity 或者 fragment 全部添加在同一個(gè)Component中梯醒,當(dāng)然現(xiàn)在的話 不推薦,比如Utils 你可以專門做一個(gè)Component腌紧,
首先放上我的Module茸习,公司項(xiàng)目,很多東西沒敢放上來(lái)壁肋,體諒号胚,可以看到 我這里提供了一個(gè)SplashPresenter,也就是啟動(dòng)頁(yè)的Presneter,業(yè)務(wù)邏輯
@Module
public class ApiModule {
public ApiModule() {
}
@Provides
@Singleton
Handler provideHandler() {
return new Handler();
}
@Provides
@Singleton
SQLiteDatabase provideSQLiteDatabase() {
return new DataBaseHelper(MyApp.context, Config.SqlName, null, Config.SqlVersion).getWritableDatabase();
}
/**
* @ User : Android架構(gòu)
* @ 創(chuàng)建日期 : 2017/7/13 下午3:24
* @模塊作用 :
* <p>
* ====================================================================================================================================
* ====================================================================================================================================
*/
private SplashPresenter splashPresenter;
public ApiModule(SplashAct splashAct) {
splashPresenter = new SplashPresenter(splashAct, new SplashModel());
}
@Provides
@Singleton
SplashPresenter provideSplashPresenter() {
return splashPresenter;
}
.....
}
當(dāng)我使用的時(shí)候浸遗,只需要注入即可猫胁,如下代碼
public class SplashAct extends BaseActivity implements SplashContract.View {
@Inject
SplashPresenter presenter;
@Inject
Handler handler;
@Inject
ApiService apiService;
@Override
protected void onCreate() {
setContentView(R.layout.activity_splash);
}
@Override
protected void initInject() {
DaggerApiComponent.builder()
.apiModule(new ApiModule(this))
.build()
.inject(this);
}
@Override
protected void initListener() {
presenter.getWordsInfo(true, apiService);
}
@Override
public void gotoLogInAct() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
startActivity(new Intent(SplashAct.this, LogInAct.class));
finish();
}
}, 1500);
}
}
最后
給大家分享一份移動(dòng)架構(gòu)大綱,包含了移動(dòng)架構(gòu)師需要掌握的所有的技術(shù)體系跛锌,大家可以對(duì)比一下自己不足或者欠缺的地方有方向的去學(xué)習(xí)提升杜漠;
需要高清架構(gòu)圖以及圖中視頻資料和文章項(xiàng)目源碼的可以加入我的技術(shù)交流群:825106898私聊群主小姐姐免費(fèi)獲取