快速入门Dagger2

作者: OneXzgj | 来源:发表于2018-03-18 18:17 被阅读336次

    1、Dagger2的介绍和简单使用:

    A fast dependency injector for Android and Java.
    一个快速的依赖注入库为java和android,
    什么是依赖注入?有兴趣的同学可以看一下我之前转载的一篇依赖注入的文章。
    https://github.com/google/dagger
    这是Dagger2的github地址,里面介绍了在项目中如何使用和依赖。

    1.1 在介绍入门之前,先简单的学习和认识一下基本的概念和名词:

    @inject

    该注解定义在需要依赖的地方使用,即:标有这个注解的地方是告诉Dagger这个类或者字段需要依赖注入,当调用相关的注入方法时候,Dagger就会创建一个该类的实例,将其依赖注入。

    @module

    Module类里面的方法是专门提供依赖,所以,一个类被@Module所注解,也就是告诉Dagger,构造实例从哪里去找需要的依赖(或者是创建),modules的一个重要的特点是它们设计为分区和组合在一起,即,假如我们的APP中需要多个module,这个时候,我们可以使用modules这个注解进行将其组合在一块。

    @provide

    在module中,我们定义的方法用这个注解,以此来告诉Dagger,我们想要创建这些对象,并提供。

    @component

    component类似于一个桥梁,也可以说一个注入器,即@Inject和@Module之间的桥梁,它的作用是连接这两部分。
    这里只是先做一个概念的解释和阐述

    2、如何快速使用 ?分为4步:

    2.1 在Demo中,我们模拟一个用户管理类(增加用户功能)
    public class UserManage {
    
        public void addUser(){
            Log.d("UserManage","Add a new User");
        }
    }
    
    2.2 创建Module去提供相应的构建
    @Module
    public class UserModule {
      
      @Provides
        UserManage provideUserManager(){
            return new UserManage();
        }
    }
    
    2.3 创建Component实现桥梁
    @Component(modules =UserModule.class )
    public interface UserComponent {
        void inject(MainActivity activity);
    }
    
    2.4 在Activity中进行依赖注入UserManage,这个类
    public class MainActivity extends AppCompatActivity {
        
        @Inject
        UserManage userManage;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerUserComponent.create().inject(this);
            userManage.addUser();
        }
    }
    

    我相信看上面的代码可能有一定的难度,下面我会详细的解释一下一步步是如何实现,为什么这么做?在详细介绍之前,我们先看看我们的Demo是不是可以运行起来,如果可以打印出addUser()中的日志,说明我们在MainActivity中的将UserManage依赖成功。


    R(EK1X3JF9UQM6WD{LPVL%F.jpg

    说明我们依赖成功,并且调用了UserMange的addUser()方法,切记我们在运行前,一定要进行rebuild project一下,使用apt插件工具帮我们自动生成代码。

    3. Dagger2进阶使用:

    到这里,我们算是已经是一个Dagger2的入门了,学会了简单的使用。但是如何将Dagger2运用到自己的实际项目中,还需要在实际项目中使用。在这里我们所有使用的类是没有进行参数的传递,这显然是不符合在实际项目中的业务因此,这里我们需要给UserManage传递一个User参数,代表实际要注入的对象。

    @Module
    public class UserModule {
    
        @Provides
        UserManage provideUserManager(User user){
            return new UserManage(user);
        }
        
    }
    

    那么我们如何进行依赖注入这个User对象,我们重新rebuild的话,控制台会报下面的错误,可以看出,是因为没有提供User对象,而造成的,那么我们该如何提供这个User对象的实例?


    图片.png

    有3种方案:(提供给它User对象不就完事了)

    3.1、在UserModule中提供一个User对象 ,即:
    @Module
    public class UserModule {
    
        @Provides
        UserManage provideUserManager(User user){
            return new UserManage(user);
        }
    
        @Provides
        User provideUser(){
            return new User("OneX_Zgj","22");
        }
    
    }
    

    我们在UserManager中打印了相关则信息:

    public class UserManage {
    
        public UserManage(User user){
            Log.d("TAG", "UserManage: "+user.getName() +" : " +user.getAge());
        }
    
        public void addUser(){
            Log.d("Tag","Add a new User");
        }
    }
    

    MainActivity和Component中的代码没有动:
    运行结果:可以看到执行成功了,并且依赖注入了User对象


    图片.png
    3.2:单独创建一个UserCreateModule

    单独创建一个UserCreateModule进行provide 一个特定的User对象,但是我们在UserModule中如何使用呢?

    先看UserCreateModule中的实现

    @Module
    public class UserCreateModule {
    
        @Provides
        User provideUser(){
            return new User("OneX_zgj","22");
        }
    
    }
    
    @Module(includes = UserCreateModule.class)
    public class UserModule {
    
        @Provides
        UserManage provideUserManager(User user){
            return new UserManage(user);
        }
    
    //    @Provides
    //    User provideUser(){
    //        return new User("OneX_Zgj","22");
    //    }
    
    }
    

    现在观察在UserModule中是如何进行实现的?细心的同学肯定会发现在类上面的注解放生了变化,对,就是因为这个注解,我们就可以引用和依赖UserCreateModule中提供的对象。

    图片.png
    3.3:注解依赖到UserComponent中

    我们将UserCreateModule加入到UserComponent中,也是可以运行的:

    @Component(modules ={UserModule.class,UserCreateModule.class} )
    public interface UserComponent {
        void inject(MainActivity activity);
    }
    
    

    4. 创建和区分不同的对象实例

    4.1在介绍和使用之前,先介绍一下这两个注解

    @Qualifier:区分不同的对象实例
    @Named :其实是@Qualifier的一种实现,我们也可以自己定义。

    4.1.1 @Name 注解的使用:标记不同的对象实例

    在实际项目中,我们有业务是进行创建出不同的实例对象,但是从我们的Demo
    比如我们开发一般在测试库,而实际发布,需要改用正式版库,那么,普通的话,我们需要修改不同地址等等。
    在这里我们简单模拟一下功能,我们使用NetUrl假设封装了地址,我们需要2中不同的url,去适配测试库和正式库,所以在代码中,我们需要根据某种状态或者标识,进行将它们区分出来:

    NetUrl的简单实现:

    public class NetUrl {
    
        private String url;
    
        public NetUrl(String url) {
            this.url = url;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    }
    

    在看一下我们在Module中的实现和使用@Name注解区分不同的实例(正式库和测试库)

    @Module
    public class OkhttpApi {
    
        @Provides
        @Named("test")
        NetUrl provideNetTestUrl(){
            return new NetUrl("http://test");
        }
    
        @Provides
        @Named("release")
        NetUrl  provideNetReleaseUrl(){
            return new NetUrl("http://release");
        }
    }
    

    在看一下OkhttpApiComponent中的实现:

    @Component(modules = OkhttpApi.class)
    public interface OkhttpApiComponent {
        void inject(MainActivity activity);
    }
    

    接下来我们查看下MainActivity中的代码:需要注意的是,如果在Module中进行加入了@Name注解,我们在MainActivity中的@Inject旁边也要加入@Name注解,指明当前对象需要注入什么样的实例。

    public class MainActivity extends AppCompatActivity {
    
    //    @Inject
    //    UserManage userManage;
    
        @Inject
        @Named("test")
        NetUrl TestApi;
    
        @Inject
        @Named("release")
        NetUrl ReleaseApi;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            DaggerOkhttpApiComponent.create().inject(this);
            Log.d("Tag", TestApi.getUrl());
            Log.d("Tag", ReleaseApi.getUrl());
            
        }
    }
    
    image.png
    4.1.2我们通过自定义Scope来实现和区分不同的实例
    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Release {
    }
    

    1、定义了Release的scope,好奇的同学,可能会问,你是怎么知道这样定义Scope的,哈哈,答案是:我肯定抄的啦,就是模仿@Name注解进行写的。
    2、定义Test的Scope

    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Test {
    }
    

    在Module中替换相应的注解

    @Module
    public class OkhttpApi {
    
    //    @Provides
    //    @Named("test")
    //    NetUrl provideNetTestUrl(){
    //        return new NetUrl("http://test");
    //    }
    //
    //
    //    @Provides
    //    @Named("release")
    //    NetUrl  provideNetReleaseUrl(){
    //        return new NetUrl("http://release");
    //    }
    
    
        @Provides
        @Test
        NetUrl provideNetTestUrl(){
            return new NetUrl("http://test");
        }
    
    
        @Provides
        @Release
        NetUrl  provideNetReleaseUrl(){
            return new NetUrl("http://release");
        }
    }
    

    在MainActivity中进行替换为我们自定义的Scope

    public class MainActivity extends AppCompatActivity {
    
    //    @Inject
    //    UserManage userManage;
    
    //    @Inject
    //    @Named("test")
    //    NetUrl TestApi;
    //
    //    @Inject
    //    @Named("release")
    //    NetUrl ReleaseApi;
    
    
        @Inject
        @Test
        NetUrl TestApi;
    
        @Inject
        @Release
        NetUrl ReleaseApi;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            DaggerOkhttpApiComponent.create().inject(this);
            Log.d("Tag", TestApi.getUrl());
            Log.d("Tag", ReleaseApi.getUrl());
    
        }
    }
    
    image.png

    @Singleton的使用,在实际项目开发中,我们会对网络请求对象进行单例(比如okhttp实例),来节约内存,这个时候我们就需要进行使用Singleton,来表明该对象是要用单例创建的。

    4.2 如何在实际项目中进行单例的操作:(比如确保在App中有only one 个okhttp的实例?)

    下面的Demo将演示如何在Application中创建一个单例:

    4.2.1 创建Module
    @Module
    public class AppMoudle {
    
        private MyApp context;
    
        public AppMoudle(MyApp context) {
            this.context = context;
        }
    
        @Singleton
        @Provides
        public OkhttpUtils provideOkhttpUtils() {
            OkhttpUtils okService = new OkhttpUtils(context);
            Log.d("TAG", "provideApiService: " + okService);
            return okService;
        }
    }
    
    4.2.2 创建AppComponent
    @Singleton
    @Component (modules = AppMoudle.class)
    public interface AppComponent {
        /**
         * 全局单例。所以不用Inject Activity
         *
         * @return 向下返回ApiService实例
         */
        OkhttpUtils getApiService();
    }
    
    4.2.3在App中实例化出来AppComponent,确保是唯一的实例
    public class MyApp extends Application {
    
        private AppComponent mAppComponent;
    
        @Override
        public void onCreate() {
            super.onCreate();
            mAppComponent = DaggerAppComponent.builder().appMoudle(new AppMoudle(this)).build();
    
        }
    
        public AppComponent getAppComponent() {
            return mAppComponent;
        }
    }
    
    4.2.4 在子类的Component中使用:

    UserComponet 中的实现:

    @PreActivity
    @Component(modules = UserModule.class ,dependencies = AppComponent.class)
    public interface UserComponet {
        void inject(MainActivity activity);
    }
    

    LoginComponent 中的实现:

    @PreActivity
    @Component(modules = UserModule.class,dependencies = AppComponent.class)
    public interface LoginComponent {
        void  inject(LoginActivity activity);
    }
    

    这里我们为什么要用个@PreActivity,因为在AppComponent中使用了Singleton注解,没有scope的component不能依赖有scope的component,而且要不同于依赖的Component,所以这里我们需要自定义一个Scope。
    PreActivity的实现

    @Scope
    @Documented
    @Retention(RUNTIME)
    public @interface PreActivity {
    }
    
    4.2.5在Activity中的使用:
    public class MainActivity extends AppCompatActivity {
    
    
        @Inject
        OkhttpUtils okhttpUtils;
    
        @Inject
        OkhttpUtils okhttpUtils2;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button btnStart = (Button) findViewById(R.id.btn_start);
    
            DaggerUserComponet.builder().appComponent(((MyApp) getApplication()).getAppComponent()).build().inject(this);
    
            okhttpUtils.logId();
            okhttpUtils2.logId();
    
            btnStart.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(MainActivity.this, LoginActivity.class));
                }
            });
    
        }
    }
    

    LoginActivity中的实现和MainActivity中的实现完全一样。
    最后一步,运行一下程序,我们观察okhttpClient实例的内存地址:


    image.png

    这样我们实现了App中的单例操作。

    5.在使用Dagger2中我们经常会范的一下错误:

    1.component的inject方法接受父类型的参数,而调用的时候传入的子类型对象,对象则无法进行注入。

    比如:我们在Component使用的是BaseActivity,而实际要注入的是MainActivity,虽然MainActivity extends BaseActivity,但是在Dagger2中,依旧无法进行注入。

    2.component关联的Modules中不能有重复的Provide,因为Dagger2在注入的时候,是根据类型去匹配,然后创建对象,如果有多处Provide的话,就不知道去创建出哪一个实例。
    3.module的provide方法使用了scope,那么我们在Component中也使用该注解
    4.module的provide方法没有使用scope,那么component和module是否加注解没有影响
    5.component的dependencies与component自身的scope不能相同,即组件之间的scope不同
    6.Ssingleton的组件不能依赖其他scope的组件,只能其他的scope依赖singleton的组件
    7.没有scope的component不能依赖有scope的component
    8.一个component不能同时有多个scope
    9.singleton注解的生命周期是component,意思是说:在同一个Component中,标记了@singleton的注解是单例,但是在不同的Component中,就会创建出来不同的实例对象。

    相关文章

      网友评论

        本文标题:快速入门Dagger2

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