一、简述
Dagger2是一个Android目前较为主流的依赖注入框架,最早的版本Dagger1 由Square公司开发,现在Dagger2由谷歌接手开发,主要用于模块间解耦,提高代码的健壮性和可维护性。之前也断断续续看过好多文章,今天正式学习一下。并且额外介绍下如何在Kotlin中使用Dagger2,写的不好,还请见谅!
二、注解方法
@Inject
: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。@Module
: Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module
注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)。@Provides
: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。@Component
: Components从根本上来说就是一个注入器,也可以说是@Inject
和@Module
的桥梁,它的主要作用就是连接这两个部分。Components可以提供所有定义了的类型的实例,比如:我们必须用@Component
注解一个接口然后列出所有的@Modules
组成该组件,如 果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。@Named
:当有多个构造方法时,可以使用@Named
进行标记区分@Scope
: Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没必要让每个对象都去了解如何管理他们的实例@Singleton
:单例
三、入门例子
-
创建两个实例类
public class MoneyController {
public MoneyController(String s) {
Log.e("TAG", "MoneyController: 构造方法======" + s);
}
public String payMoney() {
return "payMoney";
}
}
// -------------------------------------------------
public class OrderController {
String s;
public OrderController(String s) {
this.s = s;
}
public void order() {
Log.e("OrderController", "order" + s);
}
} -
创建Component,连接Module与Activity之间的桥梁
// @Modules 类似于我们的模块,提供各种实例跟对象
@Module
public class UserModule {
private Context mContext;
private String s;
public UserModule(Context context,String s) {
this.mContext = context;
this.s = s;
}
// @Provides 在Modules中,我们定义的方法是用这个注解,以此来告诉Dagger2我们想要提供哪些实例和对象
@Provides
public OrderController providerOrderController() {
return new OrderController("lisi");
}
@Provides
public MoneyController providerMoneyController() {
return new MoneyController(s);
}
} -
Inject注入
// @Component:注入器,是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分
@Component(modules = {UserModule.class})
public interface UserComponet {
void inject(DaggerActivity daggerActivity);
} 在Activity中使用(需要先Rebuild一下,会自动生成一个Dagger开头的类)
@Inject
OrderController orderController;
@Inject
MoneyController moneyController;
---------------------------------
// 需要传参必须要用builder构建
DaggerUserComponet.builder().userModule(new UserModule(this,"sss")).build().inject(this);
// 如果构造方法中不需要参数可以直接用
// DaggerUserComponet.create()
btnMakeCoffee.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvCoffee.setText(moneyController.payMoney());
orderController.order();
}
});
- 如果UserModule中没有提供providerMoneyController这个方法可以怎么做呢?
它会自动调用MoneyController类中带有@Inject
注解的构造方法
四、区分不同实例
如果一个类有两个或多个构造方法该如何去区分使用呢?使用`@Named`
-
修改一下OrderController,给它添加一个构造方法
public class OrderController {
String s;
int age;
public OrderController(String s) {
this.s = s;
}
public OrderController(String s, int age) {
this.s = s;
this.age = age;
}
public void order() {
Log.e("OrderController", "order" + s);
}
public void age() {
Log.e("OrderController", "order" + s + "age" + age);
}
} -
修改
UserModule
,使用@Named
方法进行标记// @Named 有多个构造方法时使用它标注不同的名字
// @Provides 在Modules中,我们定义的方法是用这个注解,以此来告诉Dagger2我们想要提供哪些实例和对象
@Named("debug")
@Provides
public OrderController providerOrderController() {
return new OrderController("lisi");
}
@Named("release")
@Provides
public OrderController providerUserControllerNameAndAge() {
return new OrderController("lisi", 15);
} -
在Activity中也要这样子标注一下
@Named("debug")
@Inject
OrderController orderController1;
@Named("release")
@Inject
OrderController orderController2; -
如果不满足这样子处理,可以参照源码加一个自定义注解
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Debug {
} -
用4中的注解取代
@Named("debug")
@Debug
@Provides
public OrderController providerOrderController() {
return new OrderController("lisi");
}
----------------
@Debug
@Inject
OrderController orderController1;
五、模块化的三种方式
-
假设单独创建一个网络请求的OkHttp模块,MoneyController需要使用这个OkHttpClient
@Module
public class HttpModule {
// 在这里创建OkHttpClient(应该是用单例的)
@Provides
public OkHttpClient providerOkHttpClient(){
return new OkHttpClient().newBuilder().build();
}
} -
使用
@Module(includes = HttpModule.class)
引入@Module(includes = HttpModule.class)
public class UserModule {
... ...
// OkHttpClient来自HttpModule
@Provides
public MoneyController providerMoneyController(OkHttpClient okHttpClient) {
return new MoneyController(okHttpClient);
}
} -
在Activity中使用
DaggerUserComponet.builder()
.userModule(new UserModule(this))
.httpModule(new HttpModule()) //注意这个地方
.build().inject(this); -
第二种方法:可以将
HttpModule.class
添加到UserComponet的@Component
注解的modules中@Component(modules = {UserModule.class, HttpModule.class})
-
第三种方法是使用dependencies引入一个Component
@Component(dependencies =HttpComponent.class, modules = UserModule.class)
六、在Kotlin中使用Dagger2(修改项目build)
apply plugin: 'kotlin-kapt'
... ...
kapt {
generateStubs = true
}
... ...
dependencies {
// 使用kapt
// dagger2
compile "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
}
七、全局单例(使用Kotlin实现)
声明:例子比较复杂,可以看源码(KotlinAndroidSamples),这里简要列举一下代码
-
AppComponent
@Singleton
@Component(modules = arrayOf(AppModule::class, HttpModule::class))
interface AppComponent {
fun getContext(): App // 提供App的Context
fun getDataManager(): DataManager //数据中心
fun retrofitHelper(): RetrofitHelper //提供http的帮助类
} -
App(继承自Application)
class App : Application() {
// 伴生对象
companion object {
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent
.builder()
.appModule(AppModule(this))
.httpModule(HttpModule())
.build()
}
} -
在其它地方使用appComponent
DaggerFragmentComponent
.builder()
.appComponent(App.appComponent)
.fragmentModule(FragmentModule(this))
.build()
.inject(this) -
FragmentComponent
@FragmentScope
@Component(dependencies = arrayOf(AppComponent::class), modules = arrayOf(FragmentModule::class))
interface FragmentComponent {
val activity: Activity
fun inject(mingJiaFragment: MingJiaFragment)
}
八、源码地址
例子源码:https://github.com/sdwfqin/AndroidSamples
Kotlin例子源码:https://github.com/sdwfqin/KotlinAndroidSamples