1. 程式人生 > 其它 >Android 使用dagger2進行依賴注入(基礎篇)

Android 使用dagger2進行依賴注入(基礎篇)

0. 前言

Dagger2是首個使用生成程式碼實現完整依賴注入的框架,極大減少了使用者的編碼負擔, 本文主要介紹如何使用dagger2進行依賴注入。如果你不還不瞭解依賴注入,請看這一篇

1. 簡單的依賴注入

首先我們構建一個簡單Android應用。我們建立一個UserModel,然後將它顯示到TextView中。這裡的問題是,在建立UserModel的時候,我們使用了前文所說的hard init。一旦我們的UserModel的建立方式發生了改變(比如需要傳入Context物件到建構函式),我們就需要修改所有建立UserModel的程式碼。而我們希望的是,對於UserModel的修改不影響其他模組的程式碼(比如這裡的MainActivity)。

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        UserModel user = new UserModel();
        ((TextView) findViewById(R.id.user_desc_line)).setText(user.id + "n" + user.name + "n" + user.gender);
    }
    ...
}

1.1 構建依賴

我們首先想到的是,將建立UserModel的程式碼獨立出來,這樣可以保證MainActivity的程式碼不被修改。dagger2中,這個負責提供依賴的元件被稱為Module。我們構建的ActivityModule程式碼如下所示。

@Module
public class ActivityModule {

    @Provides UserModel provideUserModel() {
        return new UserModel();
    }
}

可以看到,我們使用@Module標識型別為module,並用@Provides標識提供依賴的方法。

1.2 構建Injector

有了提供依賴的元件,我們還需要將依賴注入到需要的物件中。連線提供依賴和消費依賴物件的元件被稱為Injector。dagger2中,我們將其稱為component。ActivityComponent程式碼如下:

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

可以看到,Component是一個使用@Component標識的Java interface。interface的inject方法需要一個消耗依賴的型別物件作為引數。 注意:這裡必須是真正消耗依賴的型別MainActivity,而不可以寫成其父類,比如Activity。因為dagger2在編譯時生成依賴注入的程式碼,會到inject方法的引數型別中尋找可以注入的物件,但是實際上這些物件存在於MainActivity,而不是Activity中。如果函式宣告引數為Activity,dagger2會認為沒有需要注入的物件。當真正在MainActivity中建立Component例項進行注入時,會直接執行按照Activity作為引數生成的inject方法,導致所有注入都失敗。(是的,我是掉進這個坑了。)

1.3 完成依賴注入

最後,我們需要在MainActivity中構建Injector物件,完成注入。這部分程式碼如下所示。

public class MainActivity extends ActionBarActivity {
    private ActivityComponent mActivityComponent;

    @Inject UserModel userModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mActivityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule()).build();
        mActivityComponent.inject(this);
        ((TextView) findViewById(R.id.user_desc_line)).setText(userModel.id + "n" + userModel.name + "n" + userModel.gender);
    }
    ...
}

首先,我們使用@Inject標誌被注入的物件userModel(注意userModel不能為private),之後通過dagger2生成的實現了我們提供的ActivityComponent介面類DaggerActivityComponent建立component,呼叫其inject方法完成注入。

至此,我們使用dagger實現了最簡單的依賴注入。

2. 多層依賴

除了上面這種最簡單的形式,dagger2還可以使用component作為component的依賴,實現多層級的依賴注入。

2.1 構建依賴

我們新建立一個名為ShoppingCartModel的Domain Model。並按照1.1的方法構建其Module如下。

@Module
public class ContainerModule {
    @Provides ShoppingCartModel provideCartModel() {
        return new ShoppingCartModel();
    }
}

2.2 構建Injector

與1.2不同的是,我們的Injector提供的依賴不僅來自ContainerModule,我們還需要使用之前的ActivityComponent提供的UserModel依賴。

@Component(dependencies = ActivityComponent.class, modules = ContainerModule.class)
public interface ContainerComponent {
    void inject(MainActivity mainActivity);
}

所以如程式碼所示,我們在component後增加ActivityComponent了dependencies引數,使得一個Component成為了另一個Component的依賴。

2.3 低階Component提供依賴

目前的ActivityComponent程式碼如下所示。可以看到其只提供了inject方法,而沒有提供需要的UserModel依賴。我們需要的是將ActivityModule提供的UserModel傳遞給依賴ActivityComponent的ContainerComponent。

修改後程式碼如下:

@Component(modules = ActivityModule.class)
public interface ActivityComponent {
//    void inject(MainActivity activity);
    UserModel userModel();
}

可以看到,我們為介面增加了提供UserModel依賴的方法,同時,如果不需要使它直接進行注入,可以去掉其inject方法,此時該Component只作為一種依賴的組織模組。

最後,MainActivity中進行依賴注入的程式碼如下。

public class MainActivity extends ActionBarActivity {
    private ActivityComponent mActivityComponent;

    @Inject
    UserModel userModel;

    @Inject
    ShoppingCartModel cartModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mActivityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule()).build();
        ContainerComponent containerComponent = DaggerContainerComponent.builder().activityComponent(mActivityComponent).containerModule(new ContainerModule()).build();

        containerComponent.inject(this);

        ((TextView) findViewById(R.id.user_desc_line)).setText(userModel.id + "n" + userModel.name + "n" + userModel.gender + "n" + cartModel.total);
    }
    ...
}

3. 最後

本文試圖用最簡單的例子介紹Android中如何使用dagger2進行依賴注入,因此有很多dagger2的特性並未涉及,比如@Scope註釋,以及dagger2自動生成程式碼的分析除錯。關於dagger2更深入的特性的分析,還需要在大量使用後再做出總結。

參考

  1. Dagger 2
  2. Tasting Dagger 2 on Android
  3. Dependency injection with Dagger 2 - the API