前言
這篇文章是android-architecture源碼分析系列文章的第三篇绞吁,我們將對todo-mvp-dagger設(shè)計(jì)模式進(jìn)行分析凿试。由于該Demo是在todo?mvp項(xiàng)目的基礎(chǔ)上擴(kuò)展出來的,所以在讀這篇文章之前最好有了解過todo?mvp的代碼杨帽,并且讀過上一篇文章android-architecture源碼分析(一)——todo?mvp歹袁。
如果你還不太了解dagger2的使用,請務(wù)必先自行學(xué)習(xí)dagger2相關(guān)知識靶瘸。dagger2相對標(biāo)準(zhǔn)MVP架構(gòu)模式會更加抽象更難以理解铁材,所以這篇文章更適合有一定開發(fā)經(jīng)驗(yàn)的同學(xué)尖淘。我在學(xué)習(xí)dagger2的時(shí)候也遇到過困難和理解不透的地方,如有錯(cuò)誤著觉,敬請諒解。
依賴注入
在架構(gòu)中引入Dagger2的目的就是為了解耦惊暴,Dagger2的作用就是從對依賴對象賦值這一方面進(jìn)行解耦饼丘。當(dāng)一個(gè)類需要依賴其他對象時(shí),一般情況下由程序員來操作依賴對象的賦值辽话,而引入Dagger2以后肄鸽,就可以將賦值操作交給Dagger2而不再需要親自操作。
以上將賦值操作交由框架來處理的方式油啤,就是控制反轉(zhuǎn)(Inversion of Control典徘,縮寫為IoC)。
通過控制反轉(zhuǎn)益咬,對象在被創(chuàng)建的時(shí)候逮诲,由一個(gè)調(diào)控系統(tǒng)內(nèi)所有對象的外界實(shí)體,將其所依賴的對象的引用傳遞給它幽告。也可以說梅鹦,依賴被注入到對象中.
一般的Ioc框架都是通過反射來實(shí)現(xiàn),而Dagger2基于性能考慮冗锁,通過android的apt動態(tài)生成代碼來實(shí)現(xiàn)齐唆。
在架構(gòu)中引入Dagger2最大的好處就是復(fù)雜的依賴關(guān)系維護(hù)起來會相對簡單。當(dāng)某個(gè)核心類中有非常多的依賴對象冻河,且它的構(gòu)造函數(shù)和生命周期也相對復(fù)雜箍邮,當(dāng)我們需要修改它的構(gòu)造函數(shù)時(shí),其他依賴了這個(gè)類叨叙、用到了這個(gè)類的構(gòu)造方法的地方也需要同步修改锭弊,而且當(dāng)這個(gè)類的某些依賴對象還是別人寫的,且不太熟悉它們的生命周期的情況下摔敛,修改起來會變得無從下手廷蓉。而引入Dagger2后,只需要維護(hù)好各個(gè)相對應(yīng)的Component马昙、module桃犬,便可以很方便的構(gòu)建和修改對象的構(gòu)造函數(shù),維護(hù)它們的生命周期也將相對簡單行楞。
代碼分析
上圖可以看到攒暇,Dagger2引入以后會多出一些Component、module類子房,這也就是我們在依賴注入中需要使用到的類形用。
我們從TodoApplication開始分析代碼:
public class ToDoApplication extends Application {
private TasksRepositoryComponent mRepositoryComponent;
@Override
public void onCreate() {
super.onCreate();
mRepositoryComponent = DaggerTasksRepositoryComponent.builder()
.applicationModule(new ApplicationModule((getApplicationContext())))
.build();
}
public TasksRepositoryComponent getTasksRepositoryComponent() {
return mRepositoryComponent;
}
}
在Application里初始化TasksRepositoryComponent類并提供實(shí)例方法就轧。DaggerTasksRepositoryComponent是Dagger2自動生成的代碼,在實(shí)例化TasksRepositoryComponent的過程中創(chuàng)建了ApplicationModule實(shí)例田度。
我們再來看一下ApplicationModule的代碼:
@Module
public final class ApplicationModule {
private final Context mContext;
ApplicationModule(Context context) {
mContext = context;
}
@Provides
Context provideContext() {
return mContext;
}
}
ApplicationModule的構(gòu)造函數(shù)需要傳入Context對象妒御,也就是我們在ToDoApplication中傳入的getApplicationContext()。初始化完成以后就可以由provideContext方法對外提供一個(gè)Context實(shí)例镇饺。
@Module 表示該類為Dagger2的Module組件乎莉。
@Provides 表示某個(gè)實(shí)例對象的提供方法或創(chuàng)建方法。
再來看TasksRepositoryComponent的代碼:
@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
TasksRepository getTasksRepository();
}
TasksRepositoryComponent提供了TasksRepository實(shí)例的方法奸笤。TasksRepository實(shí)例就是MVP模式中的Model層實(shí)例惋啃,也就是說TasksRepositoryComponent提供了這個(gè)程序的核心數(shù)據(jù)接口。
modules = {TasksRepositoryModule.class, ApplicationModule.class}监右,表示作為Component組件立倍,TasksRepositoryComponent接口的實(shí)例的構(gòu)建需要創(chuàng)建TasksRepositoryModule, ApplicationModule這兩個(gè)實(shí)例對象漩符。也就是說狠角,Dagger2會自動生成TasksRepositoryComponent接口的實(shí)現(xiàn)類巴碗,這個(gè)實(shí)現(xiàn)類的構(gòu)造則需要TasksRepositoryModule和 ApplicationModule的實(shí)例對象。
@Component 表示該類為Dagger2的Component組件味榛。
@Singleton 表示單例對象椭坚,必須確保只創(chuàng)建一個(gè)類的實(shí)例。
來看看TasksRepositoryModule類:
@Module
abstract class TasksRepositoryModule {
@Singleton
@Binds
@Local
abstract TasksDataSource provideTasksLocalDataSource(TasksLocalDataSource dataSource);
@Singleton
@Binds
@Remote
abstract TasksDataSource provideTasksRemoteDataSource(TasksRemoteDataSource dataSource);
}
TasksRepositoryModule作為抽象類搏色,提供了兩個(gè)返回TasksDataSource的抽象方法善茎,分別傳入了TasksLocalDataSource和TasksRemoteDataSource的實(shí)例對象,也就是說這兩個(gè)抽象方法分別提供本地?cái)?shù)據(jù)和遠(yuǎn)程數(shù)據(jù)兩種數(shù)據(jù)來源频轿。
@Binds垂涯、@Local 和 @Remote
表示自定義注解,Dagger2在注入過程中是通過返回類型來確定使用哪個(gè)創(chuàng)建方法來獲得實(shí)例對象航邢,而當(dāng)有多個(gè)方法都返回同一類型時(shí)(上面兩個(gè)方法都返回了TasksDataSource對象)耕赘,則通過自定義注解來分辨。那么@Local和@Remote就是讓Dagger2分辨到底使用Local數(shù)據(jù)還是Remote數(shù)據(jù)膳殷。
再來看看TasksRepository類:
@Singleton
public class TasksRepository implements TasksDataSource {
private final TasksDataSource mTasksRemoteDataSource;
private final TasksDataSource mTasksLocalDataSource;
...
@Inject
TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
@Local TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = tasksRemoteDataSource;
mTasksLocalDataSource = tasksLocalDataSource;
}
...
}
TasksRepository的構(gòu)造函數(shù)需要傳入兩個(gè)TasksDataSource的實(shí)例對象操骡,分別對應(yīng)@Remote和@Local,而@Remote和@Local則對應(yīng)到TasksRepositoryModule類提供的兩種提供方法赚窃,提供TasksLocalDataSource和TasksRemoteDataSource兩種實(shí)力對象册招。
@Inject 構(gòu)造方法聲明該注解時(shí)表示當(dāng)Component在所擁有的Module類中找不到依賴需求方需要類型的提供方法時(shí),Dagger2就會檢查該需要類型的有沒有用@Inject聲明的構(gòu)造方法勒极,有則用該構(gòu)造方法創(chuàng)建一個(gè)是掰。
也就是說,TasksRepositoryComponent對應(yīng)的兩個(gè)Module類(TasksRepositoryModule, ApplicationModule)都找不到返回TasksRepository的提供方法辱匿,就會在聲明了@Inject的構(gòu)造方法中查找键痛。而TasksRepositoryComponent中的TasksRepository就是使用了這一機(jī)制調(diào)用了構(gòu)造方法創(chuàng)建實(shí)例炫彩。
@Local對應(yīng)的TasksLocalDataSource類:
@Singleton
public class TasksLocalDataSource implements TasksDataSource {
private TasksDbHelper mDbHelper;
@Inject
public TasksLocalDataSource(@NonNull Context context) {
checkNotNull(context);
mDbHelper = new TasksDbHelper(context);
}
...
}
TasksLocalDataSource的構(gòu)造函數(shù)也是聲明了@Inject,由Dagger2調(diào)用創(chuàng)建絮短;而傳入的Context參數(shù)則是由ApplicationModule中的provideContext()方法提供江兢,由Dagger2實(shí)現(xiàn)注入。
@Remote對應(yīng)的TasksRemoteDataSource類比較簡單丁频,這里略過划址。
到此我們大體分析了MVP結(jié)構(gòu)的Model層核心數(shù)據(jù)來源的實(shí)例對象的創(chuàng)建過程,下面我們分析AddEditTask模塊和AddEditTaskPresenter對象的注入過程限府。
先了解AddEditTaskComponent類:
@FragmentScoped
@Component(dependencies = TasksRepositoryComponent.class,
modules = AddEditTaskPresenterModule.class)
public interface AddEditTaskComponent {
void inject(AddEditTaskActivity addEditTaskActivity);
}
AddEditTaskComponent中的inject(AddEditTaskActivity addEditTaskActivity)方法就是表示將Module組件中的提供方法的實(shí)例對象注入到AddEditTaskActivity的聲明@Inject的對象中。這里比較抽象痢缎,繼續(xù)往下看就會明白胁勺。
dependencies = TasksRepositoryComponent.class表示組件依賴,類似與面向?qū)ο笾械睦^承独旷,TasksRepositoryComponent類似于基類署穗,只有繼承了該類才能夠調(diào)用該類的方法來回去相應(yīng)的對象。
@FragmentScoped 表示自定義作用域嵌洼,篇幅原因這里不做解釋案疲,請自行g(shù)oogle自定義作用域。
再來看AddEditTaskPresenterModule類:
@Module
public class AddEditTaskPresenterModule {
private final AddEditTaskContract.View mView;
private String mTaskId;
public AddEditTaskPresenterModule(AddEditTaskContract.View view, @Nullable String taskId) {
mView = view;
mTaskId = taskId;
}
@Provides
AddEditTaskContract.View provideAddEditTaskContractView() {
return mView;
}
@Provides
@Nullable
String provideTaskId() {
return mTaskId;
}
}
這里提供了一個(gè)構(gòu)造方法和兩個(gè)提供方法麻养,兩個(gè)提供方法分別提供了View層實(shí)例對象和TaskId褐啡。
再看一下AddEditTaskPresenter類:
final class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
TasksDataSource.GetTaskCallback {
@NonNull
private final TasksDataSource mTasksRepository;
@NonNull
private final AddEditTaskContract.View mAddTaskView;
@Nullable
private String mTaskId;
/**
* Dagger strictly enforces that arguments not marked with {@code @Nullable} are not injected
* with {@code @Nullable} values.
*/
@Inject
AddEditTaskPresenter(@Nullable String taskId, TasksRepository tasksRepository,
AddEditTaskContract.View addTaskView) {
mTaskId = taskId;
mTasksRepository = tasksRepository;
mAddTaskView = addTaskView;
}
...
}
AddEditTaskPresenter的構(gòu)造函數(shù)也聲明了@Inject,傳入的參數(shù)分別是taskId鳖昌、TasksRepository對象备畦、View層實(shí)例對象。
依賴注入的準(zhǔn)備工作做好了许昨,下面來看將AddEditTaskPresenter對象注入到AddEditTaskActivity類中的流程:
public class AddEditTaskActivity extends AppCompatActivity {
public static final int REQUEST_ADD_TASK = 1;
@Inject AddEditTaskPresenter mAddEditTasksPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addtask_act);
...
// Create the presenter
DaggerAddEditTaskComponent.builder()
.addEditTaskPresenterModule(
new AddEditTaskPresenterModule(addEditTaskFragment, taskId))
.tasksRepositoryComponent(
((ToDoApplication) getApplication()).getTasksRepositoryComponent()).build()
.inject(this);
}
...
}
DaggerAddEditTaskComponent為Dagger2自動生成的類懂盐,使用了建造者模式的鏈?zhǔn)浇Y(jié)構(gòu)。new AddEditTaskPresenterModule(addEditTaskFragment, taskId)為實(shí)例化AddEditTaskPresenterModule對象糕档,傳入的兩個(gè)參數(shù)將由AddEditTaskPresenterModule中的提供方法對外提供莉恼。((ToDoApplication) getApplication()).getTasksRepositoryComponent())則提供了TasksRepositoryComponent的實(shí)例對象。
至此速那,AddEditTaskPresenter的構(gòu)造函數(shù)的參數(shù)已準(zhǔn)備完畢俐银,taskId和addTaskView由AddEditTaskPresenterModule實(shí)例提供,而tasksRepository由TasksRepositoryComponent實(shí)例提供琅坡。mAddEditTasksPresenter變量聲明了@Inject悉患,而inject(this);中的this表示AddEditTaskActivity實(shí)例,執(zhí)行結(jié)果就是將創(chuàng)建一個(gè)AddEditTaskPresenter實(shí)例并注入到AddEditTaskActivity的mAddEditTasksPresenter對象中榆俺。
總結(jié)
todo?mvp?dagger架構(gòu)在MVP架構(gòu)基礎(chǔ)上售躁,引入Dagger2依賴注入框架坞淮,進(jìn)一步實(shí)現(xiàn)Model、Presenter陪捷、View各層的解耦回窘,并在復(fù)雜的依賴環(huán)境中簡化了維護(hù)工作,更方便單元模塊測試市袖。但是項(xiàng)目引入Dagger2會增加學(xué)習(xí)成本啡直,建議在大中型項(xiàng)目、復(fù)雜依賴環(huán)境苍碟、多人合作的情況下中使用酒觅。