Dagger2 知識梳理(2) @Qulifier 和 @Named 解決依賴注入迷失

澤毛發表於2017-12-21

一、前言

Dagger2 知識梳理(1) - Dagger2 依賴注入的兩種方式 中,我們提到了兩種實現依賴注入的方法:

  • 在依賴類的建構函式上增加@Inject註解
  • 提供一個Module類,在其中建立提供依賴類例項的方法

在使用第二種方法時,Dagger2 在尋找目標依賴類的建立方法時,是根據 Module 提供的方法的返回型別來確定的,因此如果我們提供了多個返回型別相同的建立方法時,那麼Dagger2就無法判斷使用哪個函式來建立例項,將會在編譯時丟擲異常。對於這種情況,我們稱為 依賴注入迷失

對於這種情況,我們可以通過@Qualifier/@Named註解來解決,這篇文章的完整程式碼可以從 Dagger2Sample 的第二章獲取。

二、示例

我們還是像 Dagger2 知識梳理(1) - Dagger2 依賴注入的兩種方式 中介紹的一樣,採用一個資料倉儲DataReposity作為例子,它內部包含兩個資料來源,分別為LocalSourceRemoteSource,而這兩個類都實現了Source介面,SourceModule用於提供這兩個資料來源,而SourceComponent則作為注入器。

  • 本地資料來源
public class LocalSource implements Source {

    @Override
    public String getData() {
        return "讀取本地資料成功";
    }
}
複製程式碼
  • 網路資料來源
public class RemoteSource implements Source {

    @Override
    public String getData() {
        return "讀取網路資料成功";
    }
}
複製程式碼
  • 依賴注入介面,SourceComponent
@Component(modules = SourceModule.class)
public interface SourceComponent {
    public void inject(DataRepository dataRepository);
}
複製程式碼
  • 建立工廠類
@Module
public class SourceModule {

    @Provides
    public Source provideLocalSource() {
        return new LocalSource();
    }

    @Provides
    public Source providerRemoteSource() {
        return new RemoteSource();
    }
}
複製程式碼
  • 注入目標類
public class DataRepository {

    @Inject
    Source mLocalSource;

    @Inject
    Source mRemoteSource;

    public DataRepository() {
        DaggerSourceComponent.create().inject(this);
    }

    public String getLocalData() {
        return mLocalSource.getData();
    }

    public String getRemoteData() {
        return mRemoteSource.getData();
    }

}
複製程式碼

這裡所採用的就是我們在第一節中介紹的第二種依賴注入的方式,但是如果我們這時候點選make,那麼會曝出下面的錯誤:

Dagger2 知識梳理(2)   @Qulifier 和 @Named 解決依賴注入迷失

這是因為Dagger2在尋找mLocalSource的建立方法時,它會去Component關聯的Module中(也就是SourceModule)尋找返回型別為Source的方法,但是在SourceModule中,provideLocalSource / providerRemoteSource這兩個方法返回的型別都為SourceModule,導致無法確定使用哪個方法來建立mLocalSource

這時候就需要我們提供一個別名,讓 目標類成員變數的型別建立方法的返回型別 形成一對一的關係,一般來說,使用@Qulifier是比較標準的方式。

我們先利用@Qulifier建立兩個別名,分別對應本地和遠端資料來源:

  • 本地資料來源別名
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Local {}
複製程式碼
  • 網路資料來源別名
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {}
複製程式碼

接下來,為了建立唯一關係,我們需要在兩個地方加上這個別名:

  • 目標類的成員變數
  • 工廠Module中建立目標類的成員變數的方法

即下面的兩個截圖中紅色框部分:

(1) 目標類的成員變數

(2) Module 中提供的建立方法

最後,我們用一個例子演示最終的效果:

public class QualifierActivity extends AppCompatActivity {

    private static final String TAG = QualifierActivity.class.getSimpleName();
    private Button mBtnGetData;
    private Button mBtnGetNetData;

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

        mBtnGetData = (Button) findViewById(R.id.bt_get_data);
        mBtnGetData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DataRepository repository = new DataRepository();
                String data = repository.getLocalData();
                Toast.makeText(QualifierActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });
        mBtnGetNetData = (Button) findViewById(R.id.bt_get_net_data);
        mBtnGetNetData.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                DataRepository repository = new DataRepository();
                String data = repository.getRemoteData();
                Toast.makeText(QualifierActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });
    }
}
複製程式碼

執行結果:

Dagger2 知識梳理(2)   @Qulifier 和 @Named 解決依賴注入迷失

三、使用 @Named

上面我們使用的是@Qulifier註解來實現,@Named也可以達到相同的效果。還是用上面的例子,我們不需要重新定義兩個註解@Local@Remote,而是直接在需要加上別名的兩個地方,新增@Named("Local")@Named("Remote"),也就是將@Named後面括號中的字串作為關聯目標型別的成員變數和建立方法之間的別名。

(1) 目標類的成員變數

(2) Module 中提供的建立方法


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章