快速入门Dagger2

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之间的桥梁,它的作用是连接这两部分。
这里只是先做一个概念的解释和阐述

@Subcomponent

作用有些类似 Component 中的 dependencies 作用。特点: Subcomponent 同时具备两种不同生命周期的 scope, SubComponent 具备了父 Component 拥有的 Scope,也具备了自己的 Scope。 SubComponent 的 Scope 范围小于父 Component

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中,就会创建出来不同的实例对象。

猜你喜欢

转载自blog.csdn.net/qq_15988951/article/details/79602820