app应用开发框架

开源框架----IOC注入技术----Dagger2

2021-08-03  本文已影响0人  初夏的雪

一、什么是IOC注入?

    IOC ( Inversion of Control )  :

    原来是由程序代码中主动获取的资源,现在变成由第三方获取并使用原来的代码被动接受的方式,从而达到解耦的效果,也就是控制反转。

    通俗易懂:

    **之前我们将两个对象A与B产生关联,是在一个对象A中去new另外一个对象B,而IOC技术则是当对象A需要对象B时,IOC容器就会给A提供一个B对象,而B对象的创建和销毁全部交给IOC去处理,对象A就完全不用关心这些。**

    IOC注入分三种:

            1) 运行时注入:eventBus 等

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

            3)编译期注入:butterknife ,dagger2

二、Dagger2 的介绍

    Dagger2 是Java和Android的依赖注入的编译期框架。Dagger完成注入有三个角色:

        1)生产者(Module):   主要负责对象的生产

        2)桥梁(Component):将生产的对象与消费者进行对应关联

        3)消费者(Inject):对象的使用

三、Dagger2 的基本使用

1、添加Dagger2的依赖

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

2、提供对象的@Module

  1. @Module注解负责给我们提供需要的 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 是一个接口,该接口方法中的参数是不能使用多态的,只能明确是哪一个类;例如MainActivity ,不能使用Activity来替换;

@Component(modules = {DatabaseModule.class, HttpModule.class})
public interface CustomComponent {
//此处的参数是不能多态的,即不能用Activity来偷懒,必须是明确的类,例如MainActivity
    void injectMainActivity(MainActivity mainActivity);
}



   以上当个Component的情况比较简单,但实际情况中,会出现一个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已经在上面的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())//这里添加了依赖后编译才会出现
                .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时,由从组件(子组件)负责与Activity的inject ,而主组件(父组件)负责提供获取子组件的对象;

    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的时候,需要调用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 他们之间的关系已经有所了解:Module就是我们的内容提供者,或者说就对象的提供者,他负责创建对象,而Component就像是在Module和Inject对象之间的一座桥梁,让他们二者产生关联。至于他们是怎么关联上的,我们再最后再细说,接着先说他们的使用。

4、单例Singleton 与 Scope

    当我们需要获取某一个类的单例时,可以使用Singleton注解和自定义的Scope,
4.1)Singleton注解:
  1. 在Module中提供对象的方法上添加@Singleton注解,同时需要在该Module的Component上也需要添加@Singleton注解;

  2. 只要有一个Module是@Singleton注解过的,那么Component就必须要加上@Singleton注解;

  3. Singleton注解的单例时局部单例,也就是说在第二个页面使用inject出来的HttpModule对象和在MainActivity中的对象时不相同的;

    A、在同一个Activity中时:

       inject第一个单例对象,会在调用Componet的get方法时进行双重检查(第一次获取对象,就会返回一个对象的实例,并将对象的provider设置为null );
    
       inject第二个单例对象时,由于上一次已经将provider为null ,会直接将上一次的对象返回;
    
       这样在同一个Activity中的inject的单例就是同一个对象。
    

    B、当在不同的Activity中时:

       由于两个Activity中的Component对象不是同一个对象,所以单例的module对象也不相同。
    
  4. 解决局部单例的方案:要实现全局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注解的定义复制一份,修改一下注解的名字。
  2. 在需要标识为单例的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、标记 @Named 注解

    当我们在Module中需要返回同一个类型的两个或多个对象时,如果按照上面的步骤,那么就会出现编译出错的问题,这是因为在Module中有两个或多个获取对象的方法,在inject的时候不知道该如何对应,这就需要使用@Named注解,其实说白了就是给返回相同类型对象的方法加上一个唯一的标记name,这样在inject的时候使用这个标记j就可以将对象对应起来了。
//////////////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是用于区分在Module中提供了两个相同类型的对象时,给对象添加的一个标记,这样在Inject的时候同样使用Named标记来进行对应;
  2. 不使用Named,会导致编译的时候,不知道该使用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实现懒加载时,在使用Lazy ,provider获取对象时需要先调用其get()方法;
  2. Lazy比Provider在进行inject时,多做了DoubleCheck检查;

7、参数传递

    在Module提供对象时,我们的对象经常需要一些参数来完成对象的构造,那么inject对象时,这些参数值又该如何传递给了对象呢?

    将对象需要的参数保存在Module类中,在创建Module对象时,再讲参数值传递给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);
    }
    
    //静态内部类
    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传递参数值
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传递参数
                .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技术(注解处理器)   +   简单工厂模式  +   Builder建造者模式**
  1. 将我们添加了@Module注解的类编译生成对应的对象的简单工厂类,该类负责提供该对象的实例,

  2. 将我们添加了@Component注解的接口编译生成对应的该接口的实现类,该实现类中包括了所有Module可提供的对象和依赖的Component对象,通过建造者模式将对象创建出来;

  3. 对使用Inject注解的类编译生成一个实现了MembersInjector泛型接口的实现类;

  1. 当调用Component的实现类创建对象时,先调用其builder()方法生成Component builder实例,然后调用build()方法时创建好Module提供的对象实例,这样我们再调用Component的inject方法时就可以将通过(3)的实现类来用创建好的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;

  //构造方法
  public MainActivity_MembersInjector(
      Provider<Student> studentProvider) { 
   this.studentProvider = studentProvider;
  }

  //create方法:调用构造方法
  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;
  }
}

五、知识点

我们运行程序的时候,经常会出现一些错误,这些错误信息中包含了很多关键信息,如下面的错误信息就可以了解到应用启动的调用关系:

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)
上一篇下一篇

猜你喜欢

热点阅读