40、Android--Dagger2

Dagger2

Dagger2是一款基於 Java 註解來實現的徹底在編譯階段完成依賴注入的開源庫。Dagger2 應於 Java 和 Android 開發而不僅僅是 Android,主要用於模塊間解耦、提升代碼的健壯性和可維護性。java

依賴引入

在app/build.gradle中的dependencies{ }加入如下代碼同步便可:android

implementation 'com.google.dagger:dagger:2.23.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'

三大組成部分

dagger2三個重要組成部分爲:module、component,、container,三者的關係以下圖所示:json

因爲Dagger2主要是編譯期對註解內容進行獲取,因此這三個組成部分也有相應的註解符號。app

Containeride

@Inject : 用於標記須要注入的依賴。函數

Modulegradle

@Module : 用該註解修飾的類,裏面主要提供該依賴對象的實例化方法,方法名通常爲provide + 對象名。ui

@Provides : 用這個來修飾@Module註解類中的方法,代表要提供的實例化對象this

Componetgoogle

@Componet : 能夠理解爲注入器,它會把目標類依賴的實例注入到目標類中。。

在構建好註解以後,項目須要rebuild一下,整個依賴注入的過程就算完成。

Dagger2基本使用

@Inject和@Component

一、首先編寫Person類,使用@Inject註解在構造函數上,用於標記須要注入的依賴。以下所示:

public class Person {
    private static final String TAG = "Person";
    @Inject
    public Person() {
    }

    public void eat(){
        Log.e(TAG, "eat....");
    }
}

二、接下來用@Component註解來完成依賴注入。咱們須要定義一個接口,接口命名建議爲:目標類+Component,在編譯後Dagger2就會爲咱們生成名爲Dagger+目標類名+Component的輔助類。具體代碼以下所示:

@Component
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

Component則能夠理解爲注入器,它會把目標類依賴的實例注入到目標類中。在這裏須要定義inject方法,傳入須要注入依賴的目標類。

三、在MainActivity中使用@Inject構建Person對象

public class MainActivity extends AppCompatActivity {
    @Inject
    Person person;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainActivityComponent.create().inject(this);
        person.eat();
    }
}

@Module和@Provides

若是項目中使用了第三方的類庫,能夠採用@Module和@Provides來處理。咱們建立GsonModule類,以下所示:

@Module
public class GsonModule {
    @Provides
    public Gson provideGson(){
        return new Gson();
    }
}

@Module標註在類上,用來告訴Component,能夠從這個類中獲取依賴對象,也就是Gson類。

@Provodes 標記在方法上,表示能夠經過這個方法來獲取依賴對象的實例。

@Module標註的類其實就是一個工廠,用來生成各類類;@Provodes標記的方法,就是用來生成這些類的實例的。接着來編寫Component類,以下所示:

@Component(modules = GsonModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

這和此前的區別就是加上了modules=GsonModule.class,用來指定Module。須要注意的是,Component 中能夠指定多個Module。接下來在MainActivity中使用Gson,以下所示:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Inject
    Gson gson;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainActivityComponent.create().inject(this);
        User user = new User("Legend", 31);
        String json = gson.toJson(user);
        Log.e(TAG, json);
    }
}

其中,User類就是一個包含name和age的JavaBean,很是簡單。

還有一種狀況,當咱們使用依賴注入的時候,若是須要注入的對象是抽象的,則@Inject也沒法使用,由於抽象的類並不能實例化。這時也能夠採用@Module和@Provides。

一、定義一個Engine抽象類

public abstract class Engine {
    public abstract void work();
}

二、接着定義它的實現類GasolineEngine:

public class GasolineEngine extends Engine{
    @Override
    public String work() {
        return "汽油發動機發動";
    }
}

三、隨後在Car類中引用Engine,以下所示:

public class Car {
    private Engine engine;
    @Inject
    public Car(Engine engine) {
        this.engine = engine;
    }

    public String run(){
        return engine.work();
    }
}

四、建立EngineModule類,以下所示:

@Module
public class EngineModule {
    @Provides
    public Engine provideEngine(){
        return new GasolineEngine();
    }
}

五、接着在Component中指定EngineModule:

@Component(modules = EngineModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

六、最後在MainActivity中使用,以下所示:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Inject
    Car car;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainActivityComponent.create().inject(this);
        Log.e(TAG, car.run());
    }
}

@Named和@Qualifier

@Qualifier 是限定符,@Named 則是@Qualifier 的一種實現。

當有兩個相同的依賴時,它們都繼承同一個父類或者均實現同一個接口。當它們被提供給高層時,Component 就不知

道咱們到底要提供哪個依賴 對象了,由於它找到了兩個。

好比在上面的汽車例子中,若是咱們再提供一個DieselEngine給它, EngineModule能夠改寫爲以下代碼所示:

@Module
public class EngineModule {
    @Provides
    @Named("Gasoline")
    public Engine provideGasoline(){
        return new GasolineEngine();
    }

    @Provides
    @Named("Diesel")
    public Engine provideDiesel(){
        return new DieselEngine();
    }
}

給不一樣的Provides定義不一樣的@Named,接下來在Car類中指定要採用哪一種Provides:

public class Car {
    private Engine engine;
    @Inject
    public Car(@Named("Diesel") Engine engine) {
        this.engine = engine;
    }

    public String run(){
        return engine.work();
    }
}

上面的例子也能夠用@Qualifier來 實現,@Named傳遞的值只能是字符串;而@Qualifier則更靈活一些,@Qualifier不是直接標記在屬性上的,而是用來自定義註解的,以下所示:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Gasoline {
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Diesel {
}

分別自定義兩個註解@Gasoline和@Diesel,接下來修改EngineModule類,以下所示:

@Module
public class EngineModule {
    @Provides
    @Gasoline
    public Engine provideGasoline(){
        return new GasolineEngine();
    }

    @Provides
    @Diesel
    public Engine provideDiesel(){
        return new DieselEngine();
    }
}

最後在Car類指定須要哪一種依賴:

public class Car {
    private Engine engine;
    @Inject
    public Car(@Gasoline Engine engine) {
        this.engine = engine;
    }

    public String run(){
        return engine.work();
    }
}

@Singleton和@Scope

@Scope是用來自定義註解的,而@Singleton則是用來配合實現局部單例和全局單例的。

首先在 GsonModule 中添加@Singleton,以下所示:

@Module
public class GsonModule {
    @Singleton
    @Provides
    public Gson provideGson(){
        return new Gson();
    }
}

接下來在MainActivityComponent中添加@Singleton:

@Singleton
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

咱們在MainActivity中打印Gson的hashCode值,就會發現值是相同的,以下所示:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Inject
    Gson gson;
    @Inject
    Gson gson1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainActivityComponent.create().inject(this);
        Log.e(TAG, gson.hashCode() + "--------" + gson1.hashCode());
    }
}

Gson 在 MainActivity 中是單例,若是在其餘Activity中則再也不是單例的形式。(局部單例)

若是想要實現全局單例,就須要保證對應的Component只有一個實例。能夠用@Singleton結合Application來實現:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

接下來在GsonModule中使用@ApplicationScope:

@Module
public class GsonModule {
    @ApplicationScope
    @Provides
    public Gson provideGson(){
        return new Gson();
    }
}

爲了處理多個Activity,咱們建立ActivityComponent,並使用@ApplicationScope,以下所示:

@ApplicationScope
@Component(modules = GsonModule.class)
public interface ActivityComponent {
    void inject(MainActivity activity);
    void inject(SecondActivity activity);
}

建立App類繼承自Application,用來提供ActivityComponent實例,以下所示:

public class App extends Application {
    ActivityComponent activityComponent;
    @Override
    public void onCreate() {
        super.onCreate();
        activityComponent = DaggerActivityComponent.builder().build();
    }

    public static App get(Context context){
        return (App) context.getApplicationContext();
    }

    public ActivityComponent getActivityComponent() {
        return activityComponent;
    }
}

最後在MainActivity中實現以下代碼:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Inject
    Gson gson;
    @Inject
    Gson gson1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        App.get(this).getActivityComponent().inject(this);
        Button btnJump = findViewById(R.id.btn_jump);
        btnJump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
    }
}

SecondActivity中的代碼也是相似的,運行程序,發現MainActivity和SecondActivity的Gson的內存地址是同樣的。

@Component的dependencies

@Component也能夠用dependencies依賴於其餘Component。咱們從新建立一個Swordsman類:

public class Swordsman {
    @Inject
    public Swordsman() {
    }

    public String fighting(){
        return "真是麻煩呀!";
    }
}

接下來,按照慣例建立SwordsmanModule和SwordsmanComponent,以下所示:

@Module
public class SwordsmanModule {
    @Provides
    public Swordsman providerSwordsman(){
        return new Swordsman();
    }
}

@Component(modules = SwordsmanModule.class)
public interface SwordsmanComponent {
    Swordsman getSwordsman();
}

隨後在此前定義的App中引入SwordsmanComponent,以下所示:

public class App extends Application {
    ActivityComponent activityComponent;
    @Override
    public void onCreate() {
        super.onCreate();
        activityComponent = DaggerActivityComponent.builder().build();
        DaggerSwordsmanComponent.builder().build();

    }

    public static App get(Context context){
        return (App) context.getApplicationContext();
    }

    public ActivityComponent getActivityComponent() {
        return activityComponent;
    }
}

最後咱們在SecondActivity中使用Swordsman,以下所示:

public class SecondActivity extends AppCompatActivity {
    private static final String TAG = "SecondActivity";
    @Inject
    Swordsman swordsman;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        App.get(this).getActivityComponent().inject(this);
        String content = swordsman.fighting();
        Log.e(TAG, "swordsman:" + content);
    }
}

注意:自定義的全局Application須要在清單文件中定義:

android:name=".App"

懶加載

Dagger2提供了懶加載模式,在@Inject的時候不初始化,而是使用的時候,調用get方法來獲取實例。 接着咱們改寫上面提到的Swordsman,將它改寫爲懶加載模式。

public class SecondActivity extends AppCompatActivity {
    private static final String TAG = "SecondActivity";
    @Inject
    Lazy<Swordsman> swordsmanLazy;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        App.get(this).getActivityComponent().inject(this);
        Swordsman swordsman = swordsmanLazy.get();
        String content = swordsman.fighting();
        Log.e(TAG, "swordsman:" + content);
    }
}
相關文章
相關標籤/搜索