一、前言
在 Dagger2 知識梳理(1) - Dagger2 依賴注入的兩種方式 中,我們提到了兩種實現依賴注入的方法:
- 在依賴類的建構函式上增加
@Inject
註解 - 提供一個
Module
類,在其中建立提供依賴類例項的方法
在使用第二種方法時,Dagger2 在尋找目標依賴類的建立方法時,是根據 Module 提供的方法的返回型別來確定的,因此如果我們提供了多個返回型別相同的建立方法時,那麼Dagger2
就無法判斷使用哪個函式來建立例項,將會在編譯時丟擲異常。對於這種情況,我們稱為 依賴注入迷失。
對於這種情況,我們可以通過@Qualifier/@Named
註解來解決,這篇文章的完整程式碼可以從 Dagger2Sample 的第二章獲取。
二、示例
我們還是像 Dagger2 知識梳理(1) - Dagger2 依賴注入的兩種方式 中介紹的一樣,採用一個資料倉儲DataReposity
作為例子,它內部包含兩個資料來源,分別為LocalSource
和RemoteSource
,而這兩個類都實現了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
在尋找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
中建立目標類的成員變數的方法
即下面的兩個截圖中紅色框部分:
最後,我們用一個例子演示最終的效果:
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();
}
});
}
}
複製程式碼
執行結果:
三、使用 @Named
上面我們使用的是@Qulifier
註解來實現,@Named
也可以達到相同的效果。還是用上面的例子,我們不需要重新定義兩個註解@Local
和@Remote
,而是直接在需要加上別名的兩個地方,新增@Named("Local")
和@Named("Remote")
,也就是將@Named
後面括號中的字串作為關聯目標型別的成員變數和建立方法之間的別名。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/