Android官方架構(gòu)分析(三)——todo?mvp?dagger

前言

這篇文章是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ù)它們的生命周期也將相對簡單行楞。

代碼分析

代碼結(jié)構(gòu)

上圖可以看到攒暇,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)境苍碟、多人合作的情況下中使用酒觅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市微峰,隨后出現(xiàn)的幾起案子舷丹,更是在濱河造成了極大的恐慌,老刑警劉巖蜓肆,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颜凯,死亡現(xiàn)場離奇詭異,居然都是意外死亡仗扬,警方通過查閱死者的電腦和手機(jī)症概,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來早芭,“玉大人彼城,你說我怎么就攤上這事”朴眩” “怎么了精肃?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帜乞。 經(jīng)常有香客問我司抱,道長,這世上最難降的妖魔是什么黎烈? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任习柠,我火速辦了婚禮,結(jié)果婚禮上照棋,老公的妹妹穿的比我還像新娘资溃。我一直安慰自己,他們只是感情好烈炭,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布溶锭。 她就那樣靜靜地躺著,像睡著了一般符隙。 火紅的嫁衣襯著肌膚如雪趴捅。 梳的紋絲不亂的頭發(fā)上垫毙,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機(jī)與錄音拱绑,去河邊找鬼综芥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛猎拨,可吹牛的內(nèi)容都是我干的膀藐。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼红省,長吁一口氣:“原來是場噩夢啊……” “哼额各!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吧恃,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤臊泰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蚜枢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡针饥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年厂抽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丁眼。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筷凤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苞七,到底是詐尸還是另有隱情藐守,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布蹂风,位于F島的核電站卢厂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惠啄。R本人自食惡果不足惜慎恒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望撵渡。 院中可真熱鬧融柬,春花似錦、人聲如沸趋距。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽节腐。三九已至外盯,卻和暖如春摘盆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背门怪。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工骡澈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掷空。 一個(gè)月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓肋殴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坦弟。 傳聞我的和親對象是個(gè)殘疾皇子护锤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容