美文网首页app应用开发框架
开源框架----IOC注入技术----Dagger2

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

作者: 初夏的雪 | 来源:发表于2021-08-03 16:53 被阅读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)
    

    相关文章

      网友评论

        本文标题:开源框架----IOC注入技术----Dagger2

        本文链接:https://www.haomeiwen.com/subject/quyevltx.html