開源框架----IOC注入技術(shù)----Dagger2

一、什么是IOC注入?

    IOC ( Inversion of Control )  :

    原來是由程序代碼中主動獲取的資源,現(xiàn)在變成由第三方獲取并使用原來的代碼被動接受的方式,從而達到解耦的效果,也就是控制反轉(zhuǎn)。

    通俗易懂:

    **之前我們將兩個對象A與B產(chǎn)生關(guān)聯(lián),是在一個對象A中去new另外一個對象B,而IOC技術(shù)則是當(dāng)對象A需要對象B時,IOC容器就會給A提供一個B對象,而B對象的創(chuàng)建和銷毀全部交給IOC去處理,對象A就完全不用關(guān)心這些。**

    IOC注入分三種:

            1) 運行時注入:eventBus 等

            2)源碼是注入: android studio插件

            3)編譯期注入:butterknife ,dagger2

二、Dagger2 的介紹

    Dagger2 是Java和Android的依賴注入的編譯期框架。Dagger完成注入有三個角色:

        1)生產(chǎn)者(Module):   主要負責(zé)對象的生產(chǎn)

        2)橋梁(Component):將生產(chǎn)的對象與消費者進行對應(yīng)關(guān)聯(lián)

        3)消費者(Inject):對象的使用

三、Dagger2 的基本使用

1、添加Dagger2的依賴

implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-complier:2.4'

2、提供對象的@Module

  1. @Module注解負責(zé)給我們提供需要的 class 對象;這里可以提供的對象,我們在Inject的時候都可以取到;
  2. 在提供對象的方法上使用@Provides注解;
@Module
public class HttpModule {
    @Provides
    public HttpModule providerHttpModule(){
        return new HttpModule();
    }
}

@Module
public class DatabaseModule {

    @Provides
    public DatabaseModule providerDatabaseModule(){
        return new DatabaseModule();
    }
}

3、@Component組件

  1. 組件必須是interface ;
  2. 在注解中需要指明modules的類型有哪些,多個類型時用逗號隔開;
  3. Component 是一個接口,該接口方法中的參數(shù)是不能使用多態(tài)的,只能明確是哪一個類;例如MainActivity ,不能使用Activity來替換;

@Component(modules = {DatabaseModule.class, HttpModule.class})
public interface CustomComponent {
//此處的參數(shù)是不能多態(tài)的,即不能用Activity來偷懶,必須是明確的類,例如MainActivity
    void injectMainActivity(MainActivity mainActivity);
}



   以上當(dāng)個Component的情況比較簡單,但實際情況中,會出現(xiàn)一個Activity需要在多個Component上inject ,那么有下面的兩種方式處理:
1)dependencies 依賴
//定義一個類型
public class Student {
}

//定義studentModule
@Module
public class StudentModule {
    @Provides
    Student providerStudent(){
        return new Student();
    }
}

//定義StudentComponent
@Component (modules = {StudentModule.class})
public interface StudentComponent {

//    void inject(MainActivity mainActivity);//此處因為MainActivity已經(jīng)在上面的CustomComponent中inject, 這里不能再次inject
    Student getStudent();
}

//接下來就是在CustomComponent中添加StudentComponent依賴
Component(modules = {DatabaseModule.class, HttpModule.class},
        dependencies = {StudentComponent.class})
public interface CustomComponent {
    void injectMainActivity(MainActivity mainActivity);
    void injectSecondActivity(SecondActivity secondActivity);
}

//在MainActivty中使用
public class MainActivity extends AppCompatActivity {

    @Inject
    HttpModule httpModule;

    @Inject
    HttpModule httpModule2;

    @Inject
    DatabaseModule databaseModule;

    @Inject
    Student student;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        DaggerCustomComponent.builder()
                .studentComponent(DaggerStudentComponent.create())//這里添加了依賴后編譯才會出現(xiàn)
                .build()
                .injectMainActivity(this);
       
        Log.e("MainActivity", "MainActivity onCreate: 1----"+httpModule.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 2----"+httpModule2.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 3----"+databaseModule.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 4----"+student.hashCode());
    }
}

2)subcomponent 子組件
     在使用subComponent時,由從組件(子組件)負責(zé)與Activity的inject ,而主組件(父組件)負責(zé)提供獲取子組件的對象;

    2.1) 先來定義parentComponet:
  1. 使用subComponent時,ParentComponent中定義獲取ChildComponent對象的接口;**
  2. ParentComponent和普通的Component 就一樣了;
public class Parent {
}

@Module
public class ParentModule {
    @Provides
    Parent getParent(){
        return new Parent();
    }
}

@Component (modules = {ParentModule.class})
public interface ParentComponent {
    ChildComponent getChildComponent();
}

    2.2) 定義ChildComponent:
  1. 使用@SubComponent注解來定義子組件接口
  2. ChildComponent中提供inject的方法;
public class Child {
}

@Module
public class ChildModule {

    @Provides
    Child getChild(){
        return new Child();
    }

    @Provides
    ChildModule getChildModule(){
        return new ChildModule();
    }
}

@Subcomponent(modules = {ChildModule.class})
public interface ChildComponent {
    void injectThirdActivity(ThirdActivity activity);
}
    定義好了上面的組從組建后,就可以使用了:(這里需要注意的是在inject的時候,需要調(diào)用childComponet的inject方法來進行)
public class ThirdActivity extends AppCompatActivity {
    @Inject
    Child child;

    @Inject
    ChildModule childModule;

    @Inject
    Parent parent;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        DaggerParentComponent.create().getChildComponent().injectThirdActivity(this);

        Log.e("ThirdActivity", "ThirdActivity onCreate: 1----" + child.hashCode());
        Log.e("ThirdActivity", "ThirdActivity onCreate: 2----" + childModule.hashCode());
        Log.e("ThirdActivity", "ThirdActivity onCreate: 3----" + parent.hashCode());
    }
}
    到此為止,相信對Module 、Component 他們之間的關(guān)系已經(jīng)有所了解:Module就是我們的內(nèi)容提供者,或者說就對象的提供者,他負責(zé)創(chuàng)建對象,而Component就像是在Module和Inject對象之間的一座橋梁,讓他們二者產(chǎn)生關(guān)聯(lián)。至于他們是怎么關(guān)聯(lián)上的,我們再最后再細說,接著先說他們的使用。

4、單例Singleton 與 Scope

    當(dāng)我們需要獲取某一個類的單例時,可以使用Singleton注解和自定義的Scope,
4.1)Singleton注解:
  1. 在Module中提供對象的方法上添加@Singleton注解,同時需要在該Module的Component上也需要添加@Singleton注解;

  2. 只要有一個Module是@Singleton注解過的,那么Component就必須要加上@Singleton注解;

  3. Singleton注解的單例時局部單例,也就是說在第二個頁面使用inject出來的HttpModule對象和在MainActivity中的對象時不相同的;

    A、在同一個Activity中時:

       inject第一個單例對象,會在調(diào)用Componet的get方法時進行雙重檢查(第一次獲取對象,就會返回一個對象的實例,并將對象的provider設(shè)置為null );
    
       inject第二個單例對象時,由于上一次已經(jīng)將provider為null ,會直接將上一次的對象返回;
    
       這樣在同一個Activity中的inject的單例就是同一個對象。
    

    B、當(dāng)在不同的Activity中時:

       由于兩個Activity中的Component對象不是同一個對象,所以單例的module對象也不相同。
    
  4. 解決局部單例的方案:要實現(xiàn)全局Component對象,就需要將Component對象放到Application中去;

@Module
public class HttpModule {

    @Singleton
    @Provides
    public HttpModule providerHttpModule(){
        return new HttpModule();
    }
}

@Singleton
@Component(modules = {DatabaseModule.class, HttpModule.class})
public interface CustomComponent {

    void injectMainActivity(MainActivity mainActivity);

    void injectSecondActivity(SecondActivity secondActivity);
}


public class MainActivity extends AppCompatActivity {

    @Inject
    HttpModule httpModule;

    @Inject
    HttpModule httpModule2;

    @Inject
    DatabaseModule databaseModule;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerCustomComponent.builder()
                .studentComponent(DaggerStudentComponent.create())
                .build()
                .injectMainActivity(this);
                
        Log.e("MainActivity", "MainActivity onCreate: 1----"+httpModule.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 2----"+httpModule2.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 3----"+databaseModule.hashCode());
        
    }
}
 全局單例:
//自定義的Application
public class BaseApplication extends Application {

    CustomComponent customComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        customComponent= DaggerCustomComponent.builder()
                .httpModule(new HttpModule())
                .databaseModule(new DatabaseModule())
                .studentComponent(DaggerStudentComponent.create())
                .build();
    }

    public CustomComponent getCustomComponent() {
        return customComponent;
    }
}


public class MainActivity extends AppCompatActivity {
    @Inject
    HttpModule httpModule;

    @Inject
    HttpModule httpModule2;
  
    @Inject
    DatabaseModule databaseModule;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ((BaseApplication)this.getApplication()).getCustomComponent().injectMainActivity(this);

        Log.e("MainActivity", "MainActivity onCreate: 1----"+httpModule.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 2----"+httpModule2.hashCode());
        Log.e("MainActivity", "MainActivity onCreate: 3----"+databaseModule.hashCode());
    }

    public void onclick(View view) {
        startActivity(new Intent(this,SecondActivity.class));
    }
}

public class SecondActivity extends AppCompatActivity {

    @Inject
    HttpModule httpModule;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
     ((BaseApplication)this.getApplication()).getCustomComponent().injectSecondActivity(this);
        Log.e("SecondActivity", "SecondActivity onCreate: 1----"+httpModule.hashCode());
    }
}

//這樣在兩個Activity中inject的httpModule對象就是同一個對象了。
4.2)自定義Scope注解:
    Singleton局部單例的問題,可以通過全局的Component來解決,那么如果有多個Module都是單例,這樣用Sinleton就沒辦法處理了,這時候就需要自定義Scope注解了。
  1. 自定義Scope注解非常簡單,就是將Singleton注解的定義復(fù)制一份,修改一下注解的名字。
  2. 在需要標(biāo)識為單例的Module和Component的地方加上自定義的Scope就OK了;
  3. 多個component上面的scope不能相同,沒有scope的component不能去依賴有scope的component;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomScope {
}

@Scope
@Documented
@Retention(RUNTIME)
public @interface StudentScope {
}

//下面是使用
@Module
public class StudentModule {

    @StudentScope
    @Provides
    Student providerStudent(){
        return new Student();
    }
}

@StudentScope
@Component (modules = {StudentModule.class})
public interface StudentComponent {
//    void inject(MainActivity mainActivity);
    Student getStudent();
}
    這樣就解決了多個Module是單例的問題了。

5、標(biāo)記 @Named 注解

    當(dāng)我們在Module中需要返回同一個類型的兩個或多個對象時,如果按照上面的步驟,那么就會出現(xiàn)編譯出錯的問題,這是因為在Module中有兩個或多個獲取對象的方法,在inject的時候不知道該如何對應(yīng),這就需要使用@Named注解,其實說白了就是給返回相同類型對象的方法加上一個唯一的標(biāo)記name,這樣在inject的時候使用這個標(biāo)記j就可以將對象對應(yīng)起來了。
//////////////module
@Module
public class UseNamedModule {
    @Named("C")
    @Provides
    Book getCBook(){
        return new Book("C語言","c");
    }

    @Named("JAVA")
    @Provides
    Book getJavaBook(){
        return new Book("JAVA語言","java");
    }

    public static class Book{
        String name;

        public String getName() {
            return name;
        }

        public String getAuthor() {
            return author;
        }

        String author;

        public Book(String name, String author) {
            this.name = name;
            this.author = author;
        }
    }
}

////////component
@Component (modules = {UseNamedModule.class})
public interface UseNamedComponent {
    void injectFourActivity(FourActivity activity);
}

//使用
public class FourActivity extends AppCompatActivity {
    @Named("C")
    @Inject
    UseNamedModule.Book cBook;

    @Named("JAVA")
    @Inject
    UseNamedModule.Book javaBook;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
      
        DaggerUseNamedComponent.create().injectFourActivity(this);
        Log.e("FourActivity", "FourActivity----"+cBook.getName()+"----"+cBook.getAuthor());
        Log.e("FourActivity", "FourActivity----"+javaBook.getName()+"----"+javaBook.getAuthor());
    }
}

說明:

  1. Named是用于區(qū)分在Module中提供了兩個相同類型的對象時,給對象添加的一個標(biāo)記,這樣在Inject的時候同樣使用Named標(biāo)記來進行對應(yīng);
  2. 不使用Named,會導(dǎo)致編譯的時候,不知道該使用Module的哪一個提供對象的方法;

6、懶加載 :Lazy , Provider

    先來看看編譯生成的代碼: 
public final class FiveActivity_MembersInjector implements MembersInjector<FiveActivity> {
  
  private final Provider<ParamModule.Teacher> lazyProvider;
  private final Provider<ParamModule.Teacher> providerProvider;

  @Override
  public void injectMembers(FiveActivity instance) {
    injectLazy(instance, DoubleCheck.lazy(lazyProvider));
    injectProvider(instance, providerProvider);
  }
  ///。。。。此類省略了很多代碼
}

//下面是DoubleCheck的lazy方法
 public static <P extends Provider<T>, T> Lazy<T> lazy(P provider) {
    if (provider instanceof Lazy) {
      @SuppressWarnings("unchecked")
      final Lazy<T> lazy = (Lazy<T>) provider;
      // Avoids memoizing a value that is already memoized.
      // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
      // are different types using covariant return on get(). Right now this is used with
      // DoubleCheck<T> exclusively, which is implemented such that P and L are always
      // the same, so it will be fine for that case.
      return lazy;
    }
    return new DoubleCheck<T>(checkNotNull(provider));
  }
    Lazy 、Provider的使用:
//這里省略了Module 和Component的代碼

public class FiveActivity extends AppCompatActivity {
    @Inject
    Lazy<ParamModule.Teacher>  lazy;

    @Inject
    Provider<ParamModule.Teacher> provider;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        DaggerParamComponent.builder()
                .paramModule(new ParamModule("Param",15))
                .build().injectFiveActivity(this);
        Log.e("FiveActivity", "FiveActivity--lazy--"+lazy.get().getName());
        Log.e("FiveActivity", "FiveActivity--provider--"+provider.get().getName());
    }
}

說明:

  1. 在使用Lazy ,Provider實現(xiàn)懶加載時,在使用Lazy ,provider獲取對象時需要先調(diào)用其get()方法;
  2. Lazy比Provider在進行inject時,多做了DoubleCheck檢查;

7、參數(shù)傳遞

    在Module提供對象時,我們的對象經(jīng)常需要一些參數(shù)來完成對象的構(gòu)造,那么inject對象時,這些參數(shù)值又該如何傳遞給了對象呢?

    將對象需要的參數(shù)保存在Module類中,在創(chuàng)建Module對象時,再講參數(shù)值傳遞給Module
@Module
public class ParamModule {

    public ParamModule(String name, int age) {
        this.name = name;
        this.age = age;
    }

    String name;
    int age;

    @Provides
    Teacher providerTeacher(){
        return new Teacher(name,age);
    }
    
    //靜態(tài)內(nèi)部類
    public static class Teacher{
        public Teacher(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }
        String name;

        public int getAge() {
            return age;
        }
        int age;
    }
}

//給module傳遞參數(shù)值
public class FiveActivity extends AppCompatActivity {

    @Inject
    ParamModule.Teacher teacher;

    @Inject
    Lazy<ParamModule.Teacher>  lazy;

    @Inject
    Provider<ParamModule.Teacher> provider;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        DaggerParamComponent.builder()
                .paramModule(new ParamModule("Param",15))//此處就是給Module傳遞參數(shù)
                .build().injectFiveActivity(this);

        Log.e("FiveActivity", "FiveActivity----"+teacher.getName()+teacher.getAge());
        Log.e("FiveActivity", "FiveActivity--lazy--"+lazy.get().getName());
        Log.e("FiveActivity", "FiveActivity--provider--"+provider.get().getName());
    }
}

四、Dagger2的原理分析

    核心原理:

    **APT技術(shù)(注解處理器)   +   簡單工廠模式  +   Builder建造者模式**
  1. 將我們添加了@Module注解的類編譯生成對應(yīng)的對象的簡單工廠類,該類負責(zé)提供該對象的實例,

  2. 將我們添加了@Component注解的接口編譯生成對應(yīng)的該接口的實現(xiàn)類,該實現(xiàn)類中包括了所有Module可提供的對象和依賴的Component對象,通過建造者模式將對象創(chuàng)建出來;

  3. 對使用Inject注解的類編譯生成一個實現(xiàn)了MembersInjector泛型接口的實現(xiàn)類;

  1. 當(dāng)調(diào)用Component的實現(xiàn)類創(chuàng)建對象時,先調(diào)用其builder()方法生成Component builder實例,然后調(diào)用build()方法時創(chuàng)建好Module提供的對象實例,這樣我們再調(diào)用Component的inject方法時就可以將通過(3)的實現(xiàn)類來用創(chuàng)建好的Module對象給MembersInjector泛型實例對象的成員進行賦值了。

下面來看看編譯生成的代碼就非常清楚了:

1、Module生成的代碼

public final class StudentModule_ProviderStudentFactory implements Factory<Student> {
  private final StudentModule module;

  public StudentModule_ProviderStudentFactory(StudentModule module) {
    this.module = module;
  }

  @Override
  public Student get() {
    return providerStudent(module);
  }

  public static StudentModule_ProviderStudentFactory create(StudentModule module) {
    return new StudentModule_ProviderStudentFactory(module);
  }

  public static Student providerStudent(StudentModule instance) {
    return Preconditions.checkNotNullFromProvides(instance.providerStudent());
  }
}

2、Component 代碼:

public final class DaggerStudentComponent implements StudentComponent {
  private final DaggerStudentComponent studentComponent = this;

  private Provider<Student> providerStudentProvider;

  private DaggerStudentComponent(StudentModule studentModuleParam) {
    initialize(studentModuleParam);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static StudentComponent create() {
    return new Builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final StudentModule studentModuleParam) {
    this.providerStudentProvider = DoubleCheck.provider(StudentModule_ProviderStudentFactory.create(studentModuleParam));
  }

  @Override
  public Student getStudent() {
    return providerStudentProvider.get();
  }

  public static final class Builder {
    private StudentModule studentModule;

    private Builder() {
    }

    public Builder studentModule(StudentModule studentModule) {
      this.studentModule = Preconditions.checkNotNull(studentModule);
      return this;
    }

    public StudentComponent build() {
      if (studentModule == null) {
        this.studentModule = new StudentModule();
      }
      return new DaggerStudentComponent(studentModule);
    }
  }
}

3、inject生成的代碼:

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
 
  private final Provider<Student> studentProvider;

  //構(gòu)造方法
  public MainActivity_MembersInjector(
      Provider<Student> studentProvider) { 
   this.studentProvider = studentProvider;
  }

  //create方法:調(diào)用構(gòu)造方法
  public static MembersInjector<MainActivity> create(Provider<Student> studentProvider) {
    return new MainActivity_MembersInjector(studentProvider);
  }

  @Override
  public void injectMembers(MainActivity instance) { 
   injectStudent(instance, studentProvider.get());
  }

  @InjectedFieldSignature("com.leon.opensource_dagger2.MainActivity.student")
  public static void injectStudent(MainActivity instance, Student student) {
    instance.student = student;
  }
}

五、知識點

我們運行程序的時候,經(jīng)常會出現(xiàn)一些錯誤,這些錯誤信息中包含了很多關(guān)鍵信息,如下面的錯誤信息就可以了解到應(yīng)用啟動的調(diào)用關(guān)系:

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容