文章目录
添加依赖
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'
简单场景的依赖注入
- 使用
@Inject
注解在构造方法上,告知Dagger可以通过构造方法获取这个对象的实例
public class User {
@Inject
public User() {
}
public String name = "唐人";
@Override
public String toString() {
return name;
}
}
- 新建
Component
组件,声明注入的目标
@Component
public interface ApplicationComponent {
void inject(MainActivity activity);
}
- 声明变量并添加
@Inject
注解
@Inject
public user: User
- 执行注入(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)
}
}
}
}
小结
声明需要注入的对象有两种方式:
-
构造函数声明
@Inject
注解 -
通过
@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限定符的使用
场景:
-
接口有多个实现类时
-
某个类的实例有多种创建方式(比如有多个构造方法,空参、有参等等)
可以通过自定义@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
@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 配合使用,匹配所需实例 |