天天看点

Dagger2入门到放弃添加依赖简单场景的依赖注入复杂点?再复杂点?再再复杂点?@Binds的使用@Qualifier限定符的使用

文章目录

  • 添加依赖
  • 简单场景的依赖注入
  • 复杂点?
  • 再复杂点?
    • 首次改造-局部单例
    • 再次改造-全局单例
  • 再再复杂点?
    • 为@Componet添加dependencies参数,指定改组件依赖新的组件
    • 使用@Subcomponent注解创建新的组件,并装载到父组件中
  • @Binds的使用
  • @Qualifier限定符的使用
    • 自定义限定符注解
    • @Named注解
    • 总结

添加依赖

implementation 'com.google.dagger:dagger:2.30.1'
 kapt 'com.google.dagger:dagger-compiler:2.30.1' 
 #java中使用
 #annotationProcessor 'com.google.dagger:dagger-compiler:2.30.1' 
           

简单场景的依赖注入

  1. 使用

    @Inject

    注解在构造方法上,告知Dagger可以通过构造方法获取这个对象的实例
public class User {
    @Inject
    public User() {
    }
    public String name = "唐人";
    @Override
    public String toString() {
        return name;
    }
} 
           
  1. 新建

    Component

    组件,声明注入的目标
@Component
public interface ApplicationComponent {
    void inject(MainActivity activity);
}
           
  1. 声明变量并添加

    @Inject

    注解
@Inject
public user: User
           
  1. 执行注入(Dagger必须手动执行)
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user: User
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerApplicationComponent.create().inject(this)

        tv.text = user.toString()

    }
}
           

复杂点?

无法提供构造函数的如何处理?

比如

Retrofit

,必须通过

Builder

构建者模式来构建其实例,这个时候就需要用到:

@Module

@Provider

@Module
public class NetModule {

    //方法2:告诉dagger可以通过调用该方法获取注入对象的实例
    //当存在无法通过构造函数构建实例的可以使用这种方式
    @Provides
    public Retrofit providerRetrofit() {
        return new Retrofit.Builder()
                .baseUrl("https://xxstudy.cn")
                .build();
    }
}
           

将Module装载到Component组件中:

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

           

我们知道Retrofit的使用需要我们创建APIService接口,定义请求抽象方法

public interface APIService {
    @GET("/info")
    Call<ResponseBody> getInfo();
}
           

我们在NetModule中提供APIService的具体实现,同时也可以提供OkhttpClient的实例

@Module
public class NetModule {

    //方法2:告诉dagger可以通过调用该方法获取注入对象的实例
    @Provides
    public Retrofit providerRetrofit(OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .baseUrl("https://xxstudy.cn")
                .client(okHttpClient)
                .build();
    }

    @Provides
    public OkHttpClient providerOkHttpClient() {
        return new OkHttpClient.Builder()
                .callTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .build();
    }

    //因为NetModule知道怎么为我们提供Retrofit实例,因此我们可以将Retrofit 作为参数传递
    @Provides
    public APIService providerApiService(Retrofit retrofit) {
        return retrofit.create(APIService.class);
    }
}
           

如果存在多个

Module

@Component(modules = {NetModule.class,NetModule.class})

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user: User
    
    @Inject
    lateinit var retrofit: Retrofit
    
    @Inject
    lateinit var apiService: APIService
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerApplicationComponent.create().inject(this)

        tv.text = user.toString()

        tv.append("\n")

        tv.append(retrofit?.baseUrl()?.toString() ?: "NULL")

        tv.append("\n")

        thread(true) {
            val code = apiService.info.execute().code().toString()
            runOnUiThread {
                tv.append(code)
            }
        }

    }
}
           

小结

声明需要注入的对象有两种方式:

  1. 构造函数声明

    @Inject

    注解
  2. 通过

    @Module

    @Provider

    注解

再复杂点?

上一节我们实现了网络请求的简单注入,但是有个很大的问题,我们都知道Retrofit、OkHttpClient、APIService实例的创建内部都非常复杂而且耗时,通常我们都是用单例的方式实例化,但是上一节的实现却无法做到这一点。

即下面这种写法Retrofit会有两个不同的实例

@Inject
lateinit var retrofit: Retrofit

@Inject
lateinit var retrofit2: Retrofit 
           

这个时候

作用域Scope

就派上用场了

什么是Dagger作用域?

  • 使用作用域注解,可以将某个对象的生命周期限定为其组件的生命周期。这样也就意味着,在作用域范围内使用到的是同一实例。
  • @Singleton

    是Dagger提供的一种默认的作用域注解,其意义表示一个单例对象,也就是实例的生命周期和程序的运行的生命周期保持一直
  • 使用

    @Scope

    实现自定义作用域注解
  • 作用域注解使用在

    @Inject

    @Providers

    @Binds

    @Module

    @Component

    注解上,表示其产生作用的范围。

首次改造-局部单例

@Module
public class NetModule {

    //方法2:告诉dagger可以通过调用该方法获取注入对象的实例
    @Singleton
    @Provides
    public Retrofit providerRetrofit(OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .baseUrl("https://xxstudy.cn")
                .client(okHttpClient)
                .build();
    }

    @Singleton
    @Provides
    public OkHttpClient providerOkHttpClient() {
        return new OkHttpClient.Builder()
                .callTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .build();
    }

    //因为NetModule知道怎么为我们提供Retrofit实例,因此我们可以将Retrofit 作为参数传递
    @Singleton
    @Provides
    public APIService providerApiService(Retrofit retrofit) {
        return retrofit.create(APIService.class);
    }
} 
           
@Singleton
@Component(modules = NetModule.class)
public interface ApplicationComponent {
    void inject(MainActivity activity);
}
           
@Inject
lateinit var retrofit: Retrofit
@Inject
lateinit var retrofit2: Retrofit

//注入
DaggerApplicationComponent.create().inject(this)
           

这个时候两个

Retrofit

对象在当前的Activity内就单例的了,注意是当前Activity内

不信?

我们创建一个Activity做一个测试

class SecondActivity : AppCompatActivity() {
    @Inject
    lateinit var retrofit3: Retrofit
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DaggerApplicationComponent.create().inject(this)
        Log.d(
            "SecondActivity",
            "onCreate(SecondActivity.java:21): ${retrofit3.hashCode()}"
        )
    }
}
           
@Singleton
@Component(modules = NetModule.class)
public interface ApplicationComponent {
    void inject(MainActivity activity);

    void inject(SecondActivity activity);
}
           

经测试,果然实例是不一样的,其实这就是所谓的局部单例

如何实现全局单例呢?

到现在我们应该已经知道了添加@Singleton并不能实现单例,它只是一个标示,使用了作用域后组件内的实例对象与组件的生命周期进行了绑定,由于我们组件是在Activity内进行

create

的所以其生命周期就与Activity保持了一致,这个时候我们就有了思路,我们在Application内创建这个组件

再次改造-全局单例

public class MyApp extends Application {
    private static ApplicationComponent applicationComponent = DaggerApplicationComponent.create();

    public static ApplicationComponent getApplicationComponent() {
        return applicationComponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
           

注入:

这样就实现了全局单例。

作用域的使用规则:

  • 在没有必要的情况下,尽量使用默认的作用域,即不指定作用域
  • 使用作用域注解的模块也只能在具有相同作用域注解的组件中使用
  • 在开发设计时,一定要有清晰的依赖图,不然很容易产生依赖死循环

再再复杂点?

我们当前有个需求,希望上面的User在当前的Activity内单例

当前User,默认作用域,每次声明都会创建实例

public class User {
  ...
}
           

那就添加

@Singleton

作用域,由于

ApplicationComponent

是全局单例,因此

User

也成了全局单例了

@Singleton
public class User {
  ...
}
           

自定义作用域?搞起!

@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {
}
           
@UserScope
public class User {...}
           
Dagger2入门到放弃添加依赖简单场景的依赖注入复杂点?再复杂点?再再复杂点?@Binds的使用@Qualifier限定符的使用

哈哈,编译不过直接报错,不要忘记作用域规则哦,必须同组件的作用域保持一致

这个时候可以使用:

@Component

添加

dependencies

参数,或者使用

@Subcomponent

组件

为@Componet添加dependencies参数,指定改组件依赖新的组件

  • 自定义作用域
@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {
}
           
  • User类需要去除@Inject 通过@Provider的方式提供实例
public class User {
    public User() {
    }

    public String name = "唐人";

    @Override
    public String toString() {
        return name;
    }
}
           
  • 创建一个提供User实例的Module,并且添加@UserScope 注解
@Module
public class UserModule {
    @UserScope
    @Provides
    User providerUser(){
        return new User();
    }
}
           
  • 创建一个@Component组件,并且依赖ApplicationComponent组件
@UserScope
@Component(modules = UserModule.class, dependencies = ApplicationComponent.class)
public interface UserComponent {
    //由子组件提供注入,ApplicationComponent中不在需要
    void inject(MainActivity activity);
}
           
  • 修改ApplicationComponent
@Singleton
@Component(modules = NetModule.class)
public interface ApplicationComponent {
    //void inject(MainActivity activity);

    void inject(SecondActivity activity);

    //为依赖ApplicationComponent的其他组件,提供NetModule中的实例,名字可任意,返回类型一定要对
    //从而实现复用
    Retrofit createRetrofit();

    OkHttpClient createOkHttp();

    APIService createAPIService();
}
           
  • 修改注入方式
//之前的注入方式
MyApp.getApplicationComponent().inject(this)

//增加组件依赖后
DaggerUserComponent.builder().applicationComponent(MyApp.getApplicationComponent())
            .build().inject(this)
           

测试

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user: User

    @Inject
    lateinit var user2: User

    @Inject
    lateinit var retrofit: Retrofit

    @Inject
    lateinit var retrofit2: Retrofit

    @Inject
    lateinit var apiService: APIService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //MyApp.getApplicationComponent().inject(this)

        DaggerUserComponent.builder().applicationComponent(MyApp.getApplicationComponent())
            .build().inject(this)


        tv.text = "${user.toString()}  ${user.hashCode()} 2-> ${user2.hashCode()} "
        tv.append("\n")

        tv.append(retrofit?.baseUrl()?.toString() ?: "NULL")


        thread(true) {
            val code = apiService.info.execute().code().toString()
            runOnUiThread {
                tv.append("\n")
                tv.append(code)
            }
        }

        tv.apply {
            append("\n")
            append("retrofit.hashCode=${retrofit.hashCode()}")
            append("\n")
            append("retrofit2.hashCode=${retrofit2.hashCode()}")
        }

        //startActivity(Intent(this, SecondActivity::class.java))

    }
}

           
Dagger2入门到放弃添加依赖简单场景的依赖注入复杂点?再复杂点?再再复杂点?@Binds的使用@Qualifier限定符的使用

使用@Subcomponent注解创建新的组件,并装载到父组件中

  • 修改UserComponent
//定义子组件
@UserScope
@Subcomponent(modules = UserModule.class)
public interface UserComponent {

    //告诉Dagger去创建UserComponent的实现
    @Subcomponent.Factory
    interface Factory {
        UserComponent create();
    }

    void inject(MainActivity activity);
}
           
  • 定义依赖子组件的Module
@Module(subcomponents = UserComponent.class)
public class SubComponentModule {
} 
           
  • 与ApplicationComponent进行关联
@Singleton
@Component(modules = {NetModule.class, SubComponentModule.class})
public interface ApplicationComponent {
    //void inject(MainActivity activity);

    void inject(SecondActivity activity);

 //与依赖方式的不同:子组件可以直接使用父组件的Module实例,而无需在父组件内声明
//    Retrofit createRetrofit();
//
//    OkHttpClient createOkHttp();
//
//    APIService createAPIService();
//
//    Context getContext();

    UserComponent.Factory userComponent();
}
           

注入:

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user: User

    @Inject
    lateinit var user2: User

    @Inject
    lateinit var retrofit: Retrofit

    @Inject
    lateinit var retrofit2: Retrofit

    @Inject
    lateinit var apiService: APIService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
//        MyApp.getApplicationComponent().inject(this)

//        DaggerUserComponent.builder().applicationComponent(MyApp.getApplicationComponent())
//            .build().inject(this)

//        DaggerApplicationComponent.builder().build().userComponent().create().inject(this)
        //注入方式也发生了改变
        MyApp.getApplicationComponent().userComponent().create().inject(this)

        tv.text = "${user.toString()}  ${user.hashCode()} 2-> ${user2.hashCode()} "
        tv.append("\n")

        tv.append(retrofit?.baseUrl()?.toString() ?: "NULL")

        thread(true) {
            val code = apiService.info.execute().code().toString()
            runOnUiThread {
                tv.append("\n")
                tv.append(code)
            }
        }

        tv.apply {
            append("\n")
            append("retrofit.hashCode=${retrofit.hashCode()}")
            append("\n")
            append("retrofit2.hashCode=${retrofit2.hashCode()}")
        }

        //startActivity(Intent(this, SecondActivity::class.java))

    }
}

           

@Binds的使用

当我们需要给接口提供具体实现类的实例时就需要用到

@Binds

注解

示例:

  • 定义一个接口
public interface AnalyticsService {
    String analyticsMethods();
}
           
  • 创建接口的具体实现类,并且

    @Inject

    标记实例创建的方式(也可以在

    Module

    中通过

    @Provides

    提供实例)
public class AnalyticsServiceImpl implements AnalyticsService {
    @Inject
    public AnalyticsServiceImpl() {
    }

    @Override
    public String analyticsMethods() {
        return UUID.randomUUID().toString();
    }
}
           
  • 创建抽象module
@Module
public abstract class AnalyticsModule {

    @Binds
    public abstract AnalyticsService binderAnalyticsService(AnalyticsServiceImpl analyticsServiceImpl);
} 
           
  • 在ApplicationComponent中添加module
@Singleton
@Component(modules = {NetModule.class, SubComponentModule.class, AnalyticsModule.class})
public interface ApplicationComponent {...}
           
  • 使用
@Inject
lateinit var analyticsService: AnalyticsService
           

@Qualifier限定符的使用

场景:

  1. 接口有多个实现类时
  2. 某个类的实例有多种创建方式(比如有多个构造方法,空参、有参等等)

可以通过自定义

@Qualifier

注解或者使用

Dagger

提供的

@Named

注解

自定义限定符注解

  • 首先创建两个注解
/**
 * JSON解析限定符
 */
@Qualifier
@Retention(RUNTIME)
public @interface JSON {
}

           
/**
 * XML解析限定符
 */
@Qualifier
@Retention(RUNTIME)
public @interface XML {
}
           
  • 创建两个

    AnalyticsService

    的具体实现:

    AnalyticsServiceJson

    AnalyticsServiceXML

public class AnalyticsServiceJson implements AnalyticsService {
    @Inject
    public AnalyticsServiceJson() {
    }

    @Override
    public String analyticsMethods() {
        return String.format("%s:%s", String.valueOf(System.currentTimeMillis()), getClass().getCanonicalName());
    }
}
           
public class AnalyticsServiceXML implements AnalyticsService {
    @Inject
    public AnalyticsServiceXML() {
    }

    @Override
    public String analyticsMethods() {
        return String.format("%s:%s", String.valueOf(System.currentTimeMillis()), getClass().getCanonicalName());
    }
}
           
  • Module改造
@Module
public abstract class AnalyticsModule {

    @Binds
    @XML
    public abstract AnalyticsService binderAnalyticsServiceXML(AnalyticsServiceXML analyticsServiceXML);

    @Binds
    @JSON
    public abstract AnalyticsService binderAnalyticsServiceJSON(AnalyticsServiceJson analyticsServiceJson);

} 
           
  • 使用
@Inject
@JSON
lateinit var analyticsServiceJson: AnalyticsService

@Inject
@XML
lateinit var analyticsServiceXml: AnalyticsService 
           
Dagger2入门到放弃添加依赖简单场景的依赖注入复杂点?再复杂点?再再复杂点?@Binds的使用@Qualifier限定符的使用

@Named注解

public class Student {
    public String name = "唐人";

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }
}
           
@Module
public class StudentModule {

    @Named("student")
    @Provides
    public Student providerStudent() {
        return new Student();
    }

    @Named("student2")
    @Provides
    public Student providerStudent2() {
        return new Student("StudentNO2");
    }
}
           
@Inject
@Named("student")
lateinit var student: Student

@Inject
@Named("student2")
lateinit var student2: Student
           

总结

Dagger2

的使用到此已经结束,接下来会进行源码上面的学习。

Dagger2

常用注解总结如下:

注解 说明

@Inject

标记在构造方法、需要被注入的变量中

@Component

组件

@Module

被其标注的类内部通过

@Provides

提供所需要的实例

@Provides

提供实例

@Singleton

标示单例(局部、全局)

@Binds

提供实例

@Named

@Binds

配合使用,匹配所需实例