安卓開發 第一篇 關於依賴注入框架dagger2的使用和理解

(這篇博客真是磨難重重啊,寫到一半電腦藍屏了,還好markdown編輯器保持了部分類容)

最近開始重構項目,在重構項目中用到了依賴注入框架dagger2,發現它確實很方便,能大大加快我們編寫代碼的速度,同時也很方便我們對於功能模塊的解耦。在這裏就不過多介紹dagger2了,大家谷歌 百度一下就能得到很多關於dagger2的介紹。學習dagger2是需要一定的學習成本的,我自己開始學習的時候也差不多花了一週的時間才弄明白怎樣使用dagger2,下面就說說自己對dagger2的理解和使用方法。

  1. dagger2中最重要的就是module和component了,module是生產我們需要的對象的地方,component是聯繫module和module生產出的對象的使用場景的橋樑。(說白了,dagger2就像是設計模式中的工廠模式,爲我們生產各種我們需要的對象)

    不明白?上代碼:(假如我們有一個類A,然後我們通過依賴注入方法要在MainActivity中使用)

    • 不使用dagger2

      類A:

      public class A {
          private  int field;
            ...       //其他屬性
      
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      類MainActivity

      public class MainActivity extends AppCompatActivity {
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              A a=new A();
              a.doSomething();
              a.doOtherthing();
          }
      }
    • 使用dagger2

      類A:

       public class A {
          private  int field;
            ...       //其他屬性
      
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      我們先要構造一個Module,這個Module中生產A的對象

      @Module
      public class AMedule {
      
          @Provides
          A  provideA(){
              return  new A();
          }
      
      }

      然後需要一個Component,用了連接Module和MainActivity

      @Component(modules = AMedule.class)
      public interface ActivityComponent {
         A a();
      }

      在MainActivity類中使用

      public class MainActivity extends AppCompatActivity {
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              ActivityComponent component=DaggerActivityComponent.builder()
                      .aMedule(new AMedule())
                      .build();
      
              A a=component.a();
              a.doSomething();
              a.doOtherthing();
          }
      }

      一個簡單的依賴注入就完成了。

      也許大家會覺得這樣寫很麻煩,其實不然,還有一種更加簡單的方法來做這件事,我們把MainActivity注入到ActivityComponent中,直接使用@Inject註解:

    • 使用@Inject註解

      類A :

       public class A {
          private  int field;
            ...       //其他屬性
      
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      我們先要構造一個Module

      @Module
      public class AMedule {
      
          @Provides
          A  provideA(){
              return  new A();
          }
      
      }

      然後需要一個Component

      @Component(modules = AMedule.class)
      public interface ActivityComponent {
          void inject(MainActivity mainActivity);
      }

      在MainActivity類中使用

      public class MainActivity extends AppCompatActivity {
          @Inject
          A a;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
             DaggerActivityComponent.builder()
                      .aMedule(new AMedule())
                      .build()
                      .inject(this);
      
      
              a.doSomething();
              a.doOtherthing();
          }
      }
      

      怎麼樣,是不是簡單,方便一些了。

      什麼?還麻煩,那我們更進一步吧(由於這裏的A類是我們自己定義的,可以更進一步):

    • 更進一步的@Inject註解

      類A :

       public class A {
          private  int field;
            ...       //其他屬性
      
          @Inject
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      我們先要構造一個Module(其實這裏Module已經用不到了,我們不需要通過Modu生產A了,不過爲了與上面對比,還是留着吧)

      @Module
      public class AMedule {
      
      //    @Provides
      //    A  provideA(){
      //        return  new A();
      //    }
      
      }

      然後需要一個Component

      @Component(modules = AMedule.class)
      public interface ActivityComponent {
          void inject(MainActivity mainActivity);
      }

      在MainActivity類中使用

      public class MainActivity extends AppCompatActivity {
          @Inject
          A a;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
             DaggerActivityComponent.builder()
                      .aMedule(new AMedule())
                      .build()
                      .inject(this);
      
              a.doSomething();
              a.doOtherthing();
          }
      }

      是不是看迷糊了,A沒有在Module生產,那是怎麼來的呢?不知道大家注意到沒有,A的構造方法上面有個@Inject註解,沒錯,就是他,就是這個註解生產A對象的。

      看到這裏,我們就知道dagger2生產對象有兩種方式,一種是在Module中通過@Provides註解加上providexxx 方法,另一種就是直接在要生產的對象的類的構造方法加上@inject註解。

      那麼什麼時候用Module,什麼時候用@Inject呢?

      在這裏悄悄告訴大家: 一般生成系統或者SDK或者第三方庫提供的類的對象時用Module,而自定義的類的對象用@Inject,當然,你也可以用一個類繼承系統或者SDK或者第三方庫提供的類,然後在其構造方法加上@Inject註解。

    2.可能大家對Module和Component的還是似懂非懂,在這裏在打一個形象一點的比喻吧。

    例如到吃飯的時間了,我們要去做飯(吃外賣的繞道),我們得去廚房吧(希望不是野外燒烤),我們需要煤氣竈(也可能是電磁爐),鍋,刀,砧板,盆子….(發現需要的真多哈),這些都是一個個類吧,你廚房裏的那些具體的比如美的電飯煲(美的是不是得給點廣告費),蘇泊爾不粘鍋(剛買了一個)…..就是一個個具體的對象,我們有了這些具體的對象才能愉快的做飯,而Module就是讓我們得到這一個個對象(一個大工廠,生產美的電飯煲,蘇泊爾不粘鍋….),Component 就是我們的廚房,DaggerActivityComponent .builder().aMedule(new AMedule()).build() 就是讓我們擁有了一個廚房(想想機器貓的口袋,我們可以從裏面得到任何東西 ),我們可以從廚房裏面拿炊具做飯(往外拿:A a=component.a(),這是把廚房裏的東西拿到廚房外面,在外面做飯),而 DaggerActivityComponent.builder() .aMedule(new AMedule()) .build().inject(this)就是我們自己進入廚房,在廚房裏面 做飯(我們當然很容易獲得廚房裏面的炊具了,用@Inject獲得),好了,開始做飯…

    3.一個Component可以依賴多個Module,如

     @Component(modules = {AModule.class,BModule.class,...})

    同樣,Component也可以依賴另一個Component,如

    @Component(dependencies = BComponent.class,modules = {AModule.class,BModule.class,...})

    甚至多個Component,如

    @Component(dependencies = {BComponent.class,CComponent.class,...}
                 ,modules = {AModule.class,BModule.class,...})

    有個需要注意的地方是被依賴的Component需要將對象暴露出來,依賴的Component才能夠獲取到這個對象,如

    被依賴Component

        @Component(modules = {...})
        public interface BComponent {
            A a();
            B b();
        }

    依賴Component

        @Component(dependencies = BComponent.class,modules = {...})
            public interface CComponent {
    
            }
    

    CComponent可以通過BComponent獲取A,B的對象,其他BComponent的對象對CComponent來說是透明的,不可見。

    4.@Scope註解

    這個註解是用來劃分作用域的(在哪兒使用),意思就是說我劃出一塊區域來,生產出的東西如果限定到此作用域都放到這裏,作用域的場景類(一般就是消費對象的地方,比如Activity)下次需要某個對象的時候,我就來這兒拿,如果有,就直接拿來用,如果沒有,就生產,並且放到這裏,同時自己用(有沒有一種方便自己,方便他人的感覺),dagger2自已有個@Scope註解,叫@Singleton,就是大家都熟悉的單例模式,你也可以自定義@Scope註解,比如限定生產出的某個對象只能在Activity中使用,只能在Application中使用,只能在Fragment中使用等等。。。

    例如:我們寫一個限定作用域爲Activity的Scope註解

    @Scope
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PerActivity {
    
    }
    
    
    • 使用(構造函數方式)

      類A :

      @PerActivity
       public class A {
          private  int field;
            ...       //其他屬性
      
          @Inject
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      然後需要一個Component

      @PerActivity
      @Component()
      public interface ActivityComponent {
          void inject(MainActivity mainActivity);
      }

      在MainActivity類中使用

      public class MainActivity extends AppCompatActivity {
          @Inject
          A a;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
             DaggerActivityComponent.builder()
                      .build()
                      .inject(this);
      
              a.doSomething();
              a.doOtherthing();
          }
      }
    • 使用(Module方式)

      類A :

       public class A {
          private  int field;
            ...       //其他屬性
      
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      我們先要構造一個Module

      @Module
      public class AMedule {
      
         @PerActivity
         @Provides
          A  provideA(){
              return  new A();
          }
      
      }

      然後需要一個Component

      @PerActivity
      @Component(modules = AMedule.class)
      public interface ActivityComponent {
          void inject(MainActivity mainActivity);
      }

      在MainActivity類中使用

      public class MainActivity extends AppCompatActivity {
          @Inject
          A a;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
             DaggerActivityComponent.builder()
                      .aMedule(new AMedule())
                      .build()
                      .inject(this);
      
              a.doSomething();
              a.doOtherthing();
          }
      }

      沒看明白?好吧,我們繼續

      假如我們還有一個SecondActivity也要使用A的對象

      類A :

       public class A {
          private  int field;
            ...       //其他屬性
      
          public A() {
          }
      
          public void doSomething(){
              Log.i("美女","我愛你");
          }
      
          public void doOtherthing(){
              Log.i("美女","我是真的愛你");
          }
          ...    //其他方法
      }

      我們先要構造一個Module

      @Module
      public class AMedule {
      
         @PerActivity
         @Provides
          A  provideA(){
              return  new A();
          }
      
      }

      然後需要一個Component

      @PerActivity
      @Component(modules = AMedule.class)
      public interface ActivityComponent {
          void inject(MainActivity mainActivity);
          void inject(SecondActivity secondActivity);
      }

      在MainActivity類中使用

      public class MainActivity extends AppCompatActivity {
          @Inject
          A a;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
             DaggerActivityComponent.builder()
                      .aMedule(new AMedule())
                      .build()
                      .inject(this);
      
              a.doSomething();
              a.doOtherthing();
          }
      }

      在SecondActivity類中使用

      public class SecondActivity extends AppCompatActivity {
          @Inject
          A a;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
             DaggerActivityComponent.builder()
                      .aMedule(new AMedule())
                      .build()
                      .inject(this);
      
              a.doSomething();
              a.doOtherthing();
          }
      }

      那麼,這裏MainActivity和SecondActivity中的A的對象a是同一個

    5.子Component

    子Component跟前面的一個Component依賴另一個Component有點像,區別是子Component可以獲取到父Component的所有可以生產出的對象,而Component依賴則只能獲取到被依賴的Component所暴露出來的可以生產的對象。 例子:

    類A :

            public class A {
               private  int field;
                  ...       //其他屬性
    
                public A() {
                }
    
                public void doSomething(){
                    Log.i("美女","我愛你");
                }
    
                public void doOtherthing(){
                    Log.i("美女","我是真的愛你");
                }
                ...    //其他方法
            }

    我們先要構造一個Module

            @Module
            public class AMedule {
    
               @Provides
                A  provideA(){
                    return  new A();
                }
    
            }

    然後需要一個Component

            @Component(modules = AMedule.class)
            public interface ActivityComponent {
                void inject(MainActivity mainActivity);
    
                CComponent plus(Module1 module1,Module2 module2,...);//參數爲CComponent依賴的Module
    
            }

    然後一個子Component(使用@Subcomponent註解)

            @Subcomponent(modules = {...})
            public interface CComponent {
    
                void inject(SecondActivity secondActivity);
            }

    在MainActivity類中使用,將MainActivity注入到父Component中

        public class MainActivity extends AppCompatActivity {
            @Inject
            A a;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
               DaggerActivityComponent.builder()
                        .aMedule(new AMedule())
                        .build()
                        .inject(this);
    
                a.doSomething();
                a.doOtherthing();
            }
        }

    在SecondActivity類中使用,將SecondActivity注入到子Component中

        public class SecondActivity extends AppCompatActivity {
            @Inject
            A a;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
               DaggerActivityComponent.builder()
                        .aMedule(new AMedule())
                        .build()
                        .plus(new Module1(),new Module2(),...)//CComponent依賴的Module
                        .inject(this);
    
                a.doSomething();
                a.doOtherthing();
            }
        }
    

    6.@Named註解

    這個註解是用來設置別名的,用來區分同一個類型的不同對象。比如我們都知道Android 中的Context有兩種,一種是全局的,一種是Activity中的,爲了區分他們,我們就可以加上別名;又比如有一個Person類,他有兩個實例,一個是小李,一個是小美,我們也可以用別名區分他們。

    Person類 :

        public class Person {
            private String name;
            private int age;
    
            public Person() {
            }
    
            public Person(String name, int age) {
                this.name = name;
                this.age = age;
            }
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public int getAge() {
                return age;
            }
    
            public void setAge(int age) {
                this.age = age;
            }
    
           @Override
            public String toString() {
                return "Person{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        '}';
            }
        }

    我們先要構造一個Module

        @Module
        public class ActivityMedule {
           @Named"小李"@Provides
            Person  providePerson(){
                return  new Person("小李"18);
            }
           @Named"小美"@Provides
            Person  providePerson(){
                return  new Person("小美"24);
            }
    
        }

    然後需要一個Component

        @Component(modules = ActivityMedule.class)
        public interface ActivityComponent {
            void inject(MainActivity mainActivity);
        }

    在MainActivity類中使用

        public class MainActivity extends AppCompatActivity {
            @Named"小李"@Inject
            Person p1;
    
            @Named"小美"@Inject
            Person p2;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
               DaggerActivityComponent.builder()
                        .activityMedule(new ActivityMedule())
                        .build()
                        .inject(this);
    
                Log.i("person",p1.toString());
                Log.i("person",p2.toString());
            }
        }

    當然,如果你覺得@Named註解滿足不了你的需求,你也可以使用@Qualifier來自定義自己的別名註解

    7.構造函數注入和方法注入(補充)

    之前寫文章的時候忘了這個內容了,有點不好意思,看到這篇文章這麼受大家的歡迎,心中很高興,決定儘量做到完美。

    構造函數注入和方法注入其實差不多,都是爲我們要生產的對象提供他要依賴的其他對象,不明白?看代碼吧

    BeautyGirl類:

        public class BeautyGirl{
            private String name;
    
            private int age;  //恕罪 恕罪,女人的年齡是禁忌
    
            ...  //其他屬性
    
            public void setName(String name){
            this.name = name;
            }
            public String getName(){
            return this.name;
            }
            public void setAge(int age){
            this.age = age;
            }
            public int getAge(){
            return this.age;
            }
    
            ...    //其他方法
        }

    類A :

        public class A {
            private  int field;
    
            private BeautyGirl girl;
    
              ...       //其他屬性
    
            @Inject
            public A(BeautyGirl girl) {
                 this.girl=girl;
            }
    
            public void doSomething(){
                Log.i("美女",girl.getName+"我愛你");
            }
    
            public void doOtherthing(){
                Log.i("美女",girl.getName+"我是真的愛你");
            }
            ...    //其他方法
        }

    我們先要構造一個Module,生產BeautyGirl

        @Module
        public class AMedule {
    
           @PerActivity
           @Provides
            BeautyGirl  provideBeautyGirl(){
               BeautyGirl gril= new BeautyGirl();
               girl.setName("天上少有,地上沒有的大美女");
               girl.setAge(20);
    
               return girl;
            }
    
        }

    然後需要一個Component

        @PerActivity
        @Component(modules = AMedule.class)
        public interface ActivityComponent {
            void inject(MainActivity mainActivity);
        }

    在MainActivity類中使用

        public class MainActivity extends AppCompatActivity {
            @Inject
            A a;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
               DaggerActivityComponent.builder()
                        .aMedule(new AMedule())
                        .build()
                        .inject(this);
    
                a.doSomething();
                a.doOtherthing();
            }
        }

    明白了沒,A的依賴對象BeautyGirl是通過構造參數從外部傳入的(也就是一個依賴注入),當構造方法或者其他方法添加了@Inject註解的時候,我們要生產這個類的對象的時候首先就會去查找滿足的依賴,然後纔會生產我們需要的對象。(方法注入就不講了,跟構造函數一樣,只不過構造函數添加@Inject註解,在場景類(上面的MainActivity)就可以直接通過@Inject生產這個對象)

    8.在android studio的使用,在build.gradle中添加依賴庫

    compile 'com.google.dagger:dagger:2.2' 
    compile 'com.google.dagger:dagger-compiler:2.2' 
    provided 'org.glassfish:javax.annotation:10.0-b28'

    好了,大概就講這麼多吧!

    如果有更深的理解,本文將會修改;
    如果有錯誤的地方,歡迎指正;
    如果你有更好的理解,歡迎交流。

    本文爲原創文章,版權歸博主所有,轉載請註明出處。

    更多資料:
    dagger官方github地址 : https://github.com/google/dagger
    dagger2在android中使用 :http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/
    我的github地址以及使用demo: https://github.com/naivor/naivorapp

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章