淺析Dragger2依賴註入實現過程

来源:http://www.cnblogs.com/1992monkey/archive/2017/06/23/7062630.html
-Advertisement-
Play Games

Dragger2是Android應用開發中一個非常優秀的依賴註入框架。本文主要通過結合Google給出的MVP開發案例todo-mvp-dagger(GitHub連接地址:https://github.com/googlesamples/android-architecture/tree/todo- ...


  Dragger2是Android應用開發中一個非常優秀的依賴註入框架。本文主要通過結合Google給出的MVP開發案例todo-mvp-dagger(GitHub連接地址:https://github.com/googlesamples/android-architecture/tree/todo-mvp-dagger/),簡要分析一下Dragger2的依賴註入實現過程。

  如果是剛入手學習Dragger2,這裡推薦三篇非常不錯的連載文章:http://www.jianshu.com/p/cd2c1c9f68d4,這三篇文章寫得非常用心,而且思路清晰,生動有趣,容易理解。

  todo-mvp-dagger在功能上劃分為addedittask,statistics,taskdetail,tasks四個模塊,由於每個模塊除了功能邏輯不同之外,實現方式相差不大,所以我就只分析其中的tasks模塊,其它模塊可參照此模塊的分析流程來分析。

  Dragger2的依賴註入是通過Java註解的方式來實現的。Dragger2中提供了@Inject,@Component,@Module,@Provides等一系列註解,通過註解,Dragger2能夠在程式編譯階段利用程式員創建的Java文件,按照編譯模板自動生成對應的.class輔助文件,在這些.class輔助文件中會有相應代碼來自動完成依賴對象的創建。Dragger2正是以此種看似自動化的技術手段來代替手動new對象的過程。如下圖,紅色框中的.class文件就是Tasks模塊在編譯過後自動生成的.class輔助文件。

  其實,現在有很多框架都利用了註解的方式,通過在編譯階段生成相應的.class文件來完成依賴註入或者其它某些操作,如阿裡已經開源的路由框架ARouter。

  那Dragger2的依賴註入究竟是怎樣的一個過程呢?我們先來看TasksActivity。下麵是TasksActivity的部分代碼。

  TasksActivity中依賴了一個對象:mTasksPresenter。mTasksPresenter用@Inject標註,表示在TasksActivity實例化時,mTasksPresenter需要被自動創建,然後註入到TasksActivity實例當中去。此時,我們可能會想,是不是用@Inject標註一下就實現依賴註入了?其實不然。我們看到上圖下方的紅色框中有一段代碼,這段代碼很長,因為DaggerTasksComponent這個類中使用了Builder模式,只要稍微拆分一下明白了,其實這段代碼就做了一件事情,就是調用了TasksComponent的inject()方法,而這裡才是mTasksPresenter真正被實例化的地方。

  我們再來看TasksComponent,這是一個很簡單的介面,裡面聲明瞭一個inject(TasksActivity activity)方法:

 1 package com.example.android.architecture.blueprints.todoapp.tasks;
 2 
 3 import com.example.android.architecture.blueprints.todoapp.ToDoApplication;
 4 import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepositoryComponent;
 5 import com.example.android.architecture.blueprints.todoapp.util.FragmentScoped;
 6 
 7 import dagger.Component;
 8 
 9 /**
10  * This is a Dagger component. Refer to {@link ToDoApplication} for the list of Dagger components
11  * used in this application.
12  * <P>
13  * Because this component depends on the {@link TasksRepositoryComponent}, which is a singleton, a
14  * scope must be specified. All fragment components use a custom scope for this purpose.
15  */
16 @FragmentScoped
17 @Component(dependencies = TasksRepositoryComponent.class, modules = TasksPresenterModule.class)
18 public interface TasksComponent {
19     
20     void inject(TasksActivity activity);
21 
22 }

  有介面,對應的一般就有實現類,TasksComponent的實現類在哪裡呢?我們註意到TasksComponent 被@Component標註,@Component是乾什麼的?@Component是用來標識介面或者抽象類,被@Componen標註的介面或者抽象類,在程式編譯階段會自動生成帶Dragger首碼的.class文件,例如TasksComponent 被@Component標註,就會生成DraggerTasksComponent.class文件。生成的.class文件便是被@Component標註的介面或者抽象類的實現。我們點開DraggerTasksComponent.class,其中的代碼是這樣子的:

  1 package com.example.android.architecture.blueprints.todoapp.tasks;
  2 
  3 import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;
  4 import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepositoryComponent;
  5 
  6 import javax.annotation.Generated;
  7 import javax.inject.Provider;
  8 
  9 import dagger.MembersInjector;
 10 import dagger.internal.Factory;
 11 import dagger.internal.Preconditions;
 12 
 13 @Generated(
 14         value = "dagger.internal.codegen.ComponentProcessor",
 15         comments = "https://google.github.io/dagger"
 16 )
 17 public final class DaggerTasksComponent implements TasksComponent {
 18     private MembersInjector<TasksPresenter> tasksPresenterMembersInjector;
 19 
 20     private Provider<TasksRepository> getTasksRepositoryProvider;
 21 
 22     private Provider<TasksContract.View> provideTasksContractViewProvider;
 23 
 24     private Provider<TasksPresenter> tasksPresenterProvider;
 25 
 26     private MembersInjector<TasksActivity> tasksActivityMembersInjector;
 27 
 28     private DaggerTasksComponent(Builder builder) {
 29         assert builder != null;
 30         initialize(builder);
 31     }
 32 
 33     public static Builder builder() {
 34         return new Builder();
 35     }
 36 
 37     @SuppressWarnings("unchecked")
 38     private void initialize(final Builder builder) {
 39 
 40         this.tasksPresenterMembersInjector = TasksPresenter_MembersInjector.create();
 41 
 42         this.getTasksRepositoryProvider =
 43                 new Factory<TasksRepository>() {
 44                     private final TasksRepositoryComponent tasksRepositoryComponent =
 45                             builder.tasksRepositoryComponent;
 46 
 47                     @Override
 48                     public TasksRepository get() {
 49                         return Preconditions.checkNotNull(
 50                                 tasksRepositoryComponent.getTasksRepository(),
 51                                 "Cannot return null from a non-@Nullable component method");
 52                     }
 53                 };
 54 
 55         this.provideTasksContractViewProvider =
 56                 TasksPresenterModule_ProvideTasksContractViewFactory.create(builder.tasksPresenterModule);
 57 
 58         this.tasksPresenterProvider =
 59                 TasksPresenter_Factory.create(
 60                         tasksPresenterMembersInjector,
 61                         getTasksRepositoryProvider,
 62                         provideTasksContractViewProvider);
 63 
 64         this.tasksActivityMembersInjector =
 65                 TasksActivity_MembersInjector.create(tasksPresenterProvider);
 66     }
 67 
 68     @Override
 69     public void inject(TasksActivity activity) {
 70         tasksActivityMembersInjector.injectMembers(activity);
 71     }
 72 
 73     public static final class Builder {
 74         private TasksPresenterModule tasksPresenterModule;
 75 
 76         private TasksRepositoryComponent tasksRepositoryComponent;
 77 
 78         private Builder() {
 79         }
 80 
 81         public TasksComponent build() {
 82             if (tasksPresenterModule == null) {
 83                 throw new IllegalStateException(
 84                         TasksPresenterModule.class.getCanonicalName() + " must be set");
 85             }
 86             if (tasksRepositoryComponent == null) {
 87                 throw new IllegalStateException(
 88                         TasksRepositoryComponent.class.getCanonicalName() + " must be set");
 89             }
 90             return new DaggerTasksComponent(this);
 91         }
 92 
 93         public Builder tasksPresenterModule(TasksPresenterModule tasksPresenterModule) {
 94             this.tasksPresenterModule = Preconditions.checkNotNull(tasksPresenterModule);
 95             return this;
 96         }
 97 
 98         public Builder tasksRepositoryComponent(TasksRepositoryComponent tasksRepositoryComponent) {
 99             this.tasksRepositoryComponent = Preconditions.checkNotNull(tasksRepositoryComponent);
100             return this;
101         }
102     }
103 }

  DaggerTasksComponent採用了Builder模式進行設計,實現了inject(TasksActivity activity)方法。DaggerTasksComponent中的代碼有一點點多,而且成員變數都是泛型類對象,看似稍微有點複雜,我們可以整體大概看一下,然後還是從inject(TasksActivity activity)方法的實現入手。inject(TasksActivity activity)的實現很簡單,就一行代碼:

1 tasksActivityMembersInjector.injectMembers(activity);

  這一行代碼調用了介面MembersInjector<T>的void injectMembers(T instance)方法,好,接下來我們看看MembersInjector<T>以及其中的void injectMembers(T instance)方法是用來做什麼的。MembersInjector<T>的代碼如下:

 1 package dagger;
 2 
 3 /**
 4  * Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
 5  * presence or absence of an injectable constructor.
 6  *
 7  * @param <T> type to inject members of
 8  *
 9  * @author Bob Lee
10  * @author Jesse Wilson
11  * @since 2.0 (since 1.0 without the provision that {@link #injectMembers} cannot accept
12  *      {@code null})
13  */
14 public interface MembersInjector<T> {
15 
16   /**
17    * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
18    * absence of an injectable constructor.
19    *
20    * <p>Whenever the object graph creates an instance, it performs this injection automatically
21    * (after first performing constructor injection), so if you're able to let the object graph
22    * create all your objects for you, you'll never need to use this method.
23    *
24    * @param instance into which members are to be injected
25    * @throws NullPointerException if {@code instance} is {@code null}
26    */
27   void injectMembers(T instance);
28 }

  看過註釋,我們知道了,原來這個介面就是用來將依賴註入到目標實體(即依賴對象所依附的實體,顯然,這裡就是指TasksActivity)當中去。到這裡,我們好像有點眉目了。不著急,我們繼續看MembersInjector<T>的實現。剛剛我們看了DaggerTasksComponent中inject(TasksActivity activity)方法的實現,裡面的tasksActivityMembersInjector對象調用了injectMembers()方法,因此可斷定tasksActivityMembersInjector就是MembersInjector<T>的實現類對象,那麼,tasksActivityMembersInjector是怎麼得來的呢?繼續看DaggerTasksComponent的代碼,發現tasksActivityMembersInjector是這樣被創建的:

1 this.tasksActivityMembersInjector = TasksActivity_MembersInjector.create(tasksPresenterProvider);
  TasksActivity_MembersInjector的create()方法創建了tasksActivityMembersInjector。於是,我們再來看TasksActivity_MembersInjector這個類,TasksActivity_MembersInjector代碼如下:
 1 package com.example.android.architecture.blueprints.todoapp.tasks;
 2 
 3 import javax.annotation.Generated;
 4 import javax.inject.Provider;
 5 
 6 import dagger.MembersInjector;
 7 
 8 @Generated(
 9         value = "dagger.internal.codegen.ComponentProcessor",
10         comments = "https://google.github.io/dagger"
11 )
12 public final class TasksActivity_MembersInjector implements MembersInjector<TasksActivity> {
13     private final Provider<TasksPresenter> mTasksPresenterProvider;
14 
15     public TasksActivity_MembersInjector(Provider<TasksPresenter> mTasksPresenterProvider) {
16         assert mTasksPresenterProvider != null;
17         this.mTasksPresenterProvider = mTasksPresenterProvider;
18     }
19 
20     public static MembersInjector<TasksActivity> create(
21             Provider<TasksPresenter> mTasksPresenterProvider) {
22         return new TasksActivity_MembersInjector(mTasksPresenterProvider);
23     }
24 
25     @Override
26     public void injectMembers(TasksActivity instance) {
27         if (instance == null) {
28             throw new NullPointerException("Cannot inject members into a null reference");
29         }
30         instance.mTasksPresenter = mTasksPresenterProvider.get();
31     }
32 
33     public static void injectMTasksPresenter(
34             TasksActivity instance, Provider<TasksPresenter> mTasksPresenterProvider) {
35         instance.mTasksPresenter = mTasksPresenterProvider.get();
36     }
37 }
  TasksActivity_MembersInjector正是剛剛我們所看的MembersInjector<T>的一個實現類,TasksActivity_MembersInjector實現了injectMembers(),在injectMembers()的實現中幹了這麼一件事情:
1 instance.mTasksPresenter = mTasksPresenterProvider.get();

  哦!原來TasksActivity中的mTasksPresenter是通過mTasksPresenterProvider.get()得來的!此時,迷霧變得逐漸清晰了!接下來再看mTasksPresenterProvider是如何來的。

  mTasksPresenterProvider在TasksActivity_MembersInjector的構造方法中被賦值,而TasksActivity_MembersInjector的構造方法是在create()方法中被調用的,於是,我們回到DaggerTasksComponent中調用TasksActivity_MembersInjector的create()方法的地方。找到傳入create()方法的參數tasksPresenterProvider,發現tasksPresenterProvider又是由TasksPresenter_Factory的create()方法創建的。接下來,我們進一步看TasksPresenter_Factory的代碼:

 1 package com.example.android.architecture.blueprints.todoapp.tasks;
 2 
 3 import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;
 4 
 5 import javax.annotation.Generated;
 6 import javax.inject.Provider;
 7 
 8 import dagger.MembersInjector;
 9 import dagger.internal.Factory;
10 import dagger.internal.MembersInjectors;
11 
12 @Generated(
13         value = "dagger.internal.codegen.ComponentProcessor",
14         comments = "https://google.github.io/dagger"
15 )
16 public final class TasksPresenter_Factory implements Factory<TasksPresenter> {
17     private final MembersInjector<TasksPresenter> tasksPresenterMembersInjector;
18 
19     private final Provider<TasksRepository> tasksRepositoryProvider;
20 
21     private final Provider<TasksContract.View> tasksViewProvider;
22 
23     public TasksPresenter_Factory(
24             MembersInjector<TasksPresenter> tasksPresenterMembersInjector,
25             Provider<TasksRepository> tasksRepositoryProvider,
26             Provider<TasksContract.View> tasksViewProvider) {
27         assert tasksPresenterMembersInjector != null;
28         this.tasksPresenterMembersInjector = tasksPresenterMembersInjector;
29         assert tasksRepositoryProvider != null;
30         this.tasksRepositoryProvider = tasksRepositoryProvider;
31         assert tasksViewProvider != null;
32         this.tasksViewProvider = tasksViewProvider;
33     }
34 
35     @Override
36     public TasksPresenter get() {
37         return MembersInjectors.injectMembers(
38                 tasksPresenterMembersInjector,
39                 new TasksPresenter(tasksRepositoryProvider.get(), tasksViewProvider.get()));
40     }
41 
42     public static Factory<TasksPresenter> create(
43             MembersInjector<TasksPresenter> tasksPresenterMembersInjector,
44             Provider<TasksRepository> tasksRepositoryProvider,
45             Provider<TasksContract.View> tasksViewProvider) {
46         return new TasksPresenter_Factory(
47                 tasksPresenterMembersInjector, tasksRepositoryProvider, tasksViewProvider);
48     }
49 }

  我們發現,在TasksPresenter_Factory實現的get()方法中,一個匿名TasksPresenter對象被創建,這個匿名對象正是被註入到TasksActivity的依賴對象!

  我們再回過頭來看,因為這裡的get()方法其實就是Provider<TasksPresenter>的具體實現,在TasksActivity_MembersInjector中的injectMembers(TasksActivity instance)方法中被TasksActivity_MembersInjector的成員變數mTasksPresenterProvider調用,mTasksPresenterProvider調用get()方法後返回的結果被賦值給TasksActivity實例中的依賴對象mTasksPresenter。

  至此,Dragger2中一個完整的依賴註入過程分析就此結束。誠然,本文只是簡單梳理了一下依賴註入的整個流程,對於更深層次的問題,如:Dragger是如何設計.class文件自動生成模板的,自動生成.class文件的具體過程是什麼樣的等等,本文暫時沒有說明,這也是我接下來要做的事情,剛好這幾天也在看ARouter的源碼,發現其中的依賴註入實現和Dragger2差不多,所以,我打算從多個源碼框架中來對比分析和總結Android中的依賴註入實現,待領悟再深入一個層次之後再發一篇總結性文章。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 微信JS SDK接入,主要可以先參考 "官網說明文檔" ,總結起來有幾個步驟: 1.綁定功能變數名稱: 先登錄微信公眾平臺進入“公眾號設置”的“功能設置”里填寫“JS介面安全功能變數名稱”。 備註:登錄後可在“開發者中心”查看對應的介面許可權。 2.引入JS文件:在需要調用JS介面的頁面引入如下JS文件,(支持htt ...
  • 背景 用過Vue Router的童鞋應該對路由傳參的方式多多少少有些印象,Vue Router支持兩種傳參方式:query與params;其中query方式就是動態地在路由url後面追加參數,就是http的get請求方式;那Vue Router與location的search和hash有什麼關係呢? ...
  • 今天做的上傳圖片,點擊圖片刪除。 隨機給圖片id,獲取圖片id,然後刪除圖片。 由於圖片id是隨機的,用點擊img或者點擊class,獲取id都不行,最後用onclick事件獲取。 js代碼如下: 開始這樣獲取: 不行。後來用: 獲取到了。 總結: 儘量使用event.target.id,不要使用t ...
  • 瞭解什麼是淺拷貝與深拷貝之前,首先要明白JavaScript中值類型與引用類型的存儲特征。1. 值類型分為兩種:基本數據類型:string、number、boolearn、undefined、null複合數據類型:Array、Object、Function、Date、RegExp…… 2. 賦值`` ...
  • $scope.users = [ {name:'a',id:'1'}, {name:'b',id:'2'}, {name:'c',id:'3'} ]; $scope.selected='2';//id的值,區分類型 $scope.selected=$scope.users[0].id;//如果想要第... ...
  • 最近看到vue-router的HTML5 History 模式路由的實現,然後順便又去研究了一下HTML5 的 History,以下是自己的一些理解,順便用jquery寫 一個實現類似vue-router裡面HTML5 History 模式路由器,以達到練練手,熟悉熟悉的目的。 一、history. ...
  • 這種純CSS3美化單選按鈕radio的方法適用於以下情況: 1、可相容IE9以上,需要相容IE8的要寫IE的hack把樣式去掉 2、只支持單選按鈕radio,因為單選按鈕選中樣式的圓圈可以用CSS做出來,但是覆選按鈕checkbox的選中效果對勾就需要圖片或者圖標字體庫 3、不需要JS支持切換效果 ...
  • 簡介 常用地址 安裝node,查看版本命令: 因為npm伺服器在國外,如果沒有翻牆且npm特別慢的話,可以考慮安裝淘寶鏡像文件,安裝cnpm命令 npm 安裝參數說明: npm 安裝、卸載、更新、幫助、列出已安裝插件命令: npm自動生成配置文件 安裝好Npm後,全局安裝gulp 然後在你項目里,新 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...