Dagger 2 極速入門

沉默的範大叔發表於2017-07-21

Github地址

Dagger是一個快速的依賴注入框架,供Android和Java開發使用。以前由Square維護,現在轉交給Google,Github連結為:github.com/google/dagg…

依賴注入

Dagger為依賴注入而生。什麼是依賴?什麼是注入?為什麼要使用依賴注入?這是我們學習Dagger之前必須瞭解的。
依賴就是一個類中要使用其他的類來完成某些工作,這樣一個類就依賴了另外一個類。比如在MainActivity中必須使用一個User類的物件,那麼MainActivity就依賴了User類。

public class MainActivity extends AppCompatActivity {

    User mUser;//MainActivity依賴User類

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUser = new User();//初始化mUser
        setContentView(R.layout.activity_main);
    }
}複製程式碼

一般我們直接在MainActivity中直接初始化mUser物件,但如果有天User類的構造方法中增加了一個引數,比如使用者名稱,那麼所有呼叫User的無參構造方法的地方全部要修改,這顯然不是我們希望看到的。於是我們思考可不可以提供一個User的工廠類或者容器類專門負責User物件的建立,這樣User物件的建立就不會跟MainActivity發生耦合,不管User的構造方法如何變,都不會影響到MainActivity。那麼程式碼可能是這樣:

public class MainActivity extends AppCompatActivity {

    User mUser;//MainActivity依賴User類

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUser = UserFactory.getUser()//通過工廠類注入mUser
        setContentView(R.layout.activity_main);
    }
}複製程式碼

那麼mUser物件就不是在MainActivity建立,而是由UserFactory建立,設定給mUser物件,這就可以稱之為mUser被注入了,這樣MainActivity就不需要關心User物件是怎樣建立出來,對User物件建立的修改就不會影響到MainActivity裡面的程式碼,這就是依賴注入的好處的。這就好比我們打針,人體依賴藥液來治療疾病,但藥液並不是人體自己生產的,而是醫藥公司生產然後通過注射器注入到人體,而我們人體根本就無需關心藥液是如何被製造出來的。

Dagger的作用就是通過註解的方式,幫我們自動生成建立物件的工廠類。

Dagger 2使用

1. 新增依賴

可通過連結github.com/google/dagg…查詢最新版本

dependencies {
    compile 'com.google.dagger:dagger:2.11'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
}複製程式碼

2. 新增註解@Inject

分別在MainActivity的User成員變數和User的構造方法上新增@Inject依賴。

public class MainActivity extends AppCompatActivity {

    //成員變數上新增註解
    @Inject
    User mUser;
}


public class User {

    String name;

    //構造方法上新增註解
    @Inject
    public User() {
        this.name  = "Leon";
    }

}複製程式碼

3. 建立Component類

事實上完成第二步後,MainActivity中成員mUser並沒有呼叫構造方法完成初始化,還需要一個類來完成注入,這就是Component類。
我們建立一個MainComponent介面,提供一個inject方法,其引數為將被注入的類MainActivity

public interface MainComponent {

    void inject(MainActivity activity);
}複製程式碼

然後,我們在Android Studio下選擇選單build->Make Project,這時候,會在app模組下build/generated/source/apt/debug/包名/目錄下生成三個檔案DaggerMainComponentMainActivity_MembersInjectorUser_Factory。DaggerMainComponent即為介面MainComponent的實現類。

4. 注入

最後,我們可以使用生成的DaggerMainComponnet完成注入,成員變數mUser將被賦值。如果是老司機,是不是會覺得這跟ButterKnife.bind(this)有異曲同工之妙呢?

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //注入,內部會呼叫User的構造方法完成成員變數mUser的初始化
    DaggerMainComponent.builder().build().inject(this);
    //列印結果,mUser不為null
    Log.d(TAG, "onCreate: " + mUser.name);
}複製程式碼

Module使用

假如我想在一個Activity裡面注入一個TextView,如下:

public class MainActivity extends AppCompatActivity {

    @Inject
    TextView mTextView;
}複製程式碼

如果按照上述步驟,就需要找到TextView的構造方法,加上一個@Inject註解,這個顯然是無法做到的。Module就是為了解決不能夠通過註解構造方法來建立一個例項的問題。我們建立一個TextViewMoudle類,使用@Module註解。另外需要提供一個方法返回一個TextView例項,並且用@Provides註解。

@Module
public class TextViewModule {

    @Provides
    TextView provideTextView(Context context) {
        return new TextView(context);
    }
}複製程式碼

provideTextView方法有個Context引數,需要外界傳入到TextViewModule,這裡給TextViewModule提供一個帶有Context引數的構造方法。另外還需要提供個provideContext方法返回上下文,因為Dagger框架呼叫provideTextView方法獲取一個TextView例項時,發現要傳一個Context型別的引數,這時候他會查詢被@Provides註解並且返回值為Context型別的方法獲取一個Context例項傳入provideTextView方法。

@Module
public class TextViewModule {
    //儲存一個上下文成員變數
    private Context mContext;

    //構造方法接收一個上下文
    public TextViewModule(Context context) {
        this.mContext = context;
    }

    @Provides
    TextView provideTextView(Context context) {
        return new TextView(context);
    }


    @Provides
    Context provideContext() {
        return mContext;
    }
}複製程式碼

接下來還需要在MainComponent介面上指定TextViewModule。然後點選選單選項build->Make Project,這時又會在build/generated/source/apt/debug/包名/目錄下生成兩個新檔案TextViewModule_ProvideContextFactoryTextViewModule_ProvideTextViewFactory

@Component(modules = TextViewModule.class)
public interface MainComponent {

    void inject(MainActivity activity);
}複製程式碼

最後在注入時建立一個TextViewModule傳入,表示告訴Dagger框架可以從TextViewModule中獲取TextView例項。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //傳入TextViewMoudle
    DaggerMainComponent.builder().textViewModule(new TextViewModule(this)).build().inject(this);
    FrameLayout frameLayout = (FrameLayout) findViewById(R.id.frame);
    //mTextView已被注入,不為null
    mTextView.setText(mUser.name);
    frameLayout.addView(mTextView);
}複製程式碼

相關文章