Dagger2入门到放弃

添加依赖

 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必须手动执行)
DaggerApplicationComponent.create().inject(this)
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();
    }
}

注入:

MyApp.getApplicationComponent().inject(this)

这样就实现了全局单例。

作用域的使用规则

  • 在没有必要的情况下,尽量使用默认的作用域,即不指定作用域

  • 使用作用域注解的模块也只能在具有相同作用域注解的组件中使用

  • 在开发设计时,一定要有清晰的依赖图,不然很容易产生依赖死循环

再再复杂点?

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

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

public class User {
  ...
}

那就添加@Singleton作用域,由于ApplicationComponent是全局单例,因此User也成了全局单例了

@Singleton
public class User {
  ...
}

自定义作用域?搞起!

@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {
}
@UserScope
public class User {...}

在这里插入图片描述
哈哈,编译不过直接报错,不要忘记作用域规则哦,必须同组件的作用域保持一致

这个时候可以使用:

@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))

    }
}

在这里插入图片描述

使用@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();
}

注入:

 MyApp.getApplicationComponent().userComponent().create().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)

//        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的具体实现:AnalyticsServiceJsonAnalyticsServiceXML
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 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3RRdLLFO-1625577385899)(https://secure.wostatic.cn/static/5VXzHzs7BARNfc48WpPxyQ/image.png)]

@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配合使用,匹配所需实例
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值