架構進階,Dagger2的原理及使用詳解 [復制鏈接]

2019-8-2 09:59
rongchuang 閱讀:842 評論:0 贊:0
Tag:  Dagger2

目錄

  • 一:Dagger2是什么?
  • 二:為什么要有Dagger2
  • 三:Dagger2如何使用
  1. 基本的概念
  2. 如何使用Dagger2
  3. 高級用法
  • (1)構造方法需要其他參數時候
  • (2) 模塊之間的依賴關系
  • (3) @Named注解使用
  • (4) @Singleton注解
  • (5)自定義Scoped
  • (6)Subcomponent
  • (7)lazy 和 Provider
  • 四: MVP + Dagger2

Ps:文末有架構師進階資料和面試題資料


一:Dagger2是什么?

是一個依賴注入框架,butterknife也是一個依賴注入框架。不過butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是對象的管理,其目的是為了降低程序耦合。

二:為什么要有Dagger2


8年Android開發程序員:架構進階,Dagger2的原理及使用詳解


下面我就手寫了

public class A {
public void eat() {
System.out.print("吃飯了");
}
}

使用的時候我們就要

A a = new A();
a.eat();

如果現在改了,早A的構造方法中必須傳入B對象

public class A {
private B b;
public A(B b) {
this.b = b;
}
public void eat() {
System.out.print("吃飯了");
}
}

那么使用的時候

A a = new A(new B());
a.eat();

可能就有人說了,不就加一個對象么,這里只是我舉的一個很簡單的例子,看的感覺很簡單,但是在實際開發中,如果現在改了一個這個構造方法。是不是意味著,整個項目中的都的改,一不小心, 就是BUG 啊

三:Dagger2如何使用

1. 基本的概念

上來給你說,怎么玩,肯定懵逼,這里我簡單說一下幾個概念,想有個認知,在往下看,會好很多,Dagger 是通過@Inject使用具體的某個對象,這個對象呢,是由@Provides注解提供,但是呢,這個@Provides只能在固定的模塊中,也就是@Module注解,我們查找的時候,不是直接去找模塊,而是去找@Component


8年Android開發程序員:架構進階,Dagger2的原理及使用詳解


我們反向推導,當我們使用

@Inject
A a

想要獲取a對象的示例的時候,Dagger2 會先去找,當前Activity或者Fragment所連接的橋梁,例如上圖中,連接的只有一個橋梁,實際上可以有多個,這個橋梁,會去尋找他所依賴的模塊,如圖中,依賴了模塊A,和模塊B,然后在模塊中,會去尋找@Providers注解,去尋找A的實例化對象。

2. 如何使用Dagger2

(1) 引入依賴庫

Dagger2官網

 compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

(2) 創建Moudule

//第一步 添加@Module 注解
@Module
public class MainModule {
}

(3)創建具體的示例

//第一步 添加@Module 注解
@Module
public class MainModule {
//第二步 使用Provider 注解 實例化對象
@Provides
A providerA() {
return new A();
}
}

(4)創建一個Component

//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class})
public interface MainComponent {
//第三步 寫一個方法 綁定Activity /Fragment
void inject(MainActivity activity);
}

(5)Rebuild Project


8年Android開發程序員:架構進階,Dagger2的原理及使用詳解


然后AS 會自動幫我們生成一個


8年Android開發程序員:架構進階,Dagger2的原理及使用詳解


開頭都是以Dagger開始的

(6)將Component與Activity/Fragment綁定關系

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 對象的實例
*/
@Inject
A a;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

/***
* 第一步 添加依賴關系
*/
//第一種方式
DaggerMainConponent.create().inject(this);

//第二種方式
DaggerMainConponent.builder().build().inject(this);

/***
* 第三步 調用A 對象的方法
*/
a.eat();
}
}

肯定有小伙伴說了,為了拿到一個對象,這么大個彎,太麻煩了。別急慢慢看,路要一步一步走嘛

3. 高級用法

(1)構造方法需要其他參數時候

怎么說呢,就和最來時的意思一樣,

A a = new A(new B());
a.eat();

這種情況,如何使用Dagger2呢

肯定有小伙伴這么想

 @Provides
A providerA() {
return new A(new B());
}

直接 new 一個B ,這樣的使用方法,是不對的!!!!!!,不對的!!!!!!!,不對的!!!!!!!!!

正確的打開方式

這時候,我們什么都不用該,只需要在moudule中添加一個依賴就可以了

@Module
public class MainModule {

/***
* 構造方法需要其他參數時候
*
* @return
*/
@Provides
B providerB() {
return new B();
}

@Provides
A providerA(B b) {
return new A(b);
}
}

(2) 模塊之間的依賴關系



模塊與模塊之間的聯系,

@Module (includes = {BModule.class})// includes 引入)
public class AModule {
@Provides
A providerA() {
return new A();
}
}

這樣的話,Dagger會現在A moudule 中尋找對象,如果沒找到,會去找module B 中是否有被Inject注解的對象,如果還是沒有,那么GG,拋出異常

一個Component 應用多個 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);
}

注意 這里有坑。一下會講解


(3) @Named注解使用

相當于有個表示,雖然大家都是同一個對象,但是實例化對象不同就不如

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--->[email protected]
07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->[email protected]

(4) @Singleton注解

單利模式,是不是超級方便,你想然哪個對象單利化,直接在他的Provider上添加@Singleton 就行了

例如

 @Singleton
@Provides
A providerA(B b) {
return new A(b);
}

注意: 第一個坑!!!

如果 moudule所依賴的Comonent 中有被單利的對象,那么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---->[email protected]
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->[email protected]

不相信的小伙伴可以吧@Singleton去掉試試

現在我們完成了單利,然后做了一個事情,就是點擊某個按鈕,跳轉到一個新的Activiry,兩邊都引用同樣一個A 對象,打印A 的地址,

說一下,一個Conponent 可以被對個Activity/Fragment 引用,如

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
void inject(MainActivity activity);
void inject(TestAct activity);
}

上面與兩個Activity, MainActivity 和 TestAct ,都引用相同的 對象,答應地址看看

12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->[email protected]
12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->[email protected]

竟然不同,說好的單利呢

注意: 第二個坑,單利對象只能在同一個Activity中有效。不同的Activity 持有的對象不同

那有人就要問了,沒什么辦法么,我就想全局只要一個實例化對象啊? 辦法肯定是有的,


(5) 自定義Scoped

/**
* @作者 : Android架構
* @創建日期 :2017/7/14 下午3:04
* @方法作用:
* 參考Singleton 的寫法
* Scope 標注是Scope
* Documented 標記在文檔
* @Retention(RUNTIME) 運行時級別
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}

首先想一下,什么樣的對象,能夠做到全局單例,生命周期肯定和APP 綁定嘛,這里我做演示,一個AppAip 我們要對這個對象,全局單利,所以二話不說,先給Application 來個全家桶,

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;
}
}

最后是如何使用

首先,這個是個橋梁,依賴方式,上文已經說過了

@ActivityScoped
@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainComponent {
void inject(MainActivity activity);
void inject(TestAct activity);
}

細心的小伙伴可能已經發現了,只有在上面一個MainComponent添加了一個@ActivityScoped,這里說明一下,@Singleton是Application 的單利

注意,第三個坑,子類component 依賴父類的component ,子類component的Scoped 要小于父類的Scoped,Singleton的級別是Application

所以,我們這里的@Singleton 級別大于我們自定義的@ActivityScoped,同時,對應module 所依賴的component ,也要放上相應的Scope

好吧,上面的例子,打印Log.

12-30 02:16:30.899 4717-4717/? E/TAG: A1---->[email protected]
12-30 02:16:31.009 4717-4717/? E/TAG: A2---->[email protected]

一樣啦

爬坑指南(極度重要)

  1. Provide 如果是單例模式 對應的Compnent 也要是單例模式
  2. inject(Activity act) 不能放父類
  3. 即使使用了單利模式,在不同的Activity 對象還是不一樣的
  4. 依賴component, component之間的Scoped 不能相同
  5. 子類component 依賴父類的component ,子類component的Scoped 要小于父類的Scoped,Singleton的級別是Application
  6. 多個Moudle 之間不能提供相同的對象實例
  7. Moudle 中使用了自定義的Scoped 那么對應的Compnent 使用同樣的Scoped

(6)Subcomponent

這個是系統提供的一個Component,當使用Subcomponent,那么默認會依賴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);,便是這個橋梁,我是要注入到MainActivity,但是dagger 并不會給我生成一個Dagger開頭的DaggerTestSubComponent 這個類,如果我想使用TestSubModule.class里面提供的對象,依然還是使用DaggerMainConponent例如

 DaggerMainConponent
.builder()
.mainModule(new MainModule())
.build()
.add(new TestSubModule())
.inject(this);

可以看到這里有一個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初始化了一個present對象,然后放到一個池子里,需要的時候就get它,所以你每次get的時候拿到的對象都是同一個;并且當你第一次去get時,它才會去初始化這個實例.

procider(強制加載)的作用:

1:同上當你第一次去get時,它才會去初始化這個實例

2:后面當你去get這個實例時,是否為同一個,取決于他Module里實現的方式


四: MVP + Dagger2

這是現在主流的設計架構

MVP,這個我會在后面的文章介紹,這里不做太多解釋

當你了解MVP 的時候,你就知道,所有的業務邏輯全在Presenter,

換句話, presenter 持有的對象,控制著你程序的全部邏輯,這在dagger 中,講白了 我們只要將所有的presetner 對象控制就可以了

下面附上目錄結構,當然僅僅作為參考。dagger 強大的用法還是需要各位自己去體會,下面的項目 是我剛剛學會dagger 時候 寫的一個項目


8年Android開發程序員:架構進階,Dagger2的原理及使用詳解


8年Android開發程序員:架構進階,Dagger2的原理及使用詳解


可以看到 ,我是 將所有的activity 或者 fragment 全部添加在同一個Component中,當然現在的話 不推薦,比如Utils 你可以專門做一個Component,

首先放上我的Module,公司項目,很多東西沒敢放上來,體諒,可以看到 我這里提供了一個SplashPresenter,也就是啟動頁的Presneter,業務邏輯

@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架構
* @ 創建日期 : 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;
}

.....
}

當我使用的時候,只需要注入即可,如下代碼

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


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

时时彩改欢乐生肖