前言
- 注解如同标签
- 反射:GreenDao、Butterknife、Dagger2 这些因为涉及到了反射处理,而反射处理相对于正常开发速度很慢,所以它们通常在编译时产生一些新的代码,然后才能在程序运行过程中使用,也就是说它们都把反射处理移动到编译器编译代码时的阶段,而程序运行时并不涉及到反射,这就是这些框架运用了反射技术,但是仍然高效的秘诀所在。
- 要先编译一次代码,不然就会报错。现在,可以解释了,编译代码是为了生成中间代码,然后在中间代码的基础上按照正常的流程开发。
依赖注入
class A {
B b;
C c;
public A() {
b = new B();
c = new C(3);
}
}
//————————————————
class A {
B b;
C c;
public A(B b, C c) {
this.b = b;
this.c = c;
}
}
/**
* 在上面代码中,A 不再直接创建 B 与 C,它把依赖的实例的权力移交到了外部,所以无论 B 和 C 怎么变* * 化,都不再影响 A 了。
* 这种实例化依赖的权力移交模式被称为控制反转(IoC)
* 而这种通过将依赖从构造方法中传入的手段就是依赖注入(DI)
*/
依赖注入的3种形式
构造方法注入
class A {
B b;
public A(B b) {
this.b = b;
}
}
Setter 注入
class A {
B b;
public void setB(B b) {
this.b = b;
}
}
接口注入
interface Setter {
void setB(B b);
}
class A implements Setter{
B b;
public void setB(B b) {
this.b = b;
}
}
Dagger2
依赖注入框架
通过注解标记,Dagger帮我们完成实例化和注入。
Dagger2本质就是, 一个依赖注入框架,用来解耦。
@Inject
标记
- 给属性做标记时:依赖的需求方(需求者)
- 给构造方法做标记时:依赖的提供方(依赖)
public class Man {
@Inject
FoodRice foodRice;
@Inject
public Man() {}
}
public class FoodRice {
@Inject
public FoodRice(){}
}
@Component
联系纽带,将 @inject 标记的需求方和依赖绑定起来,并建立了联系,
Dagger2 在编译代码时会依靠这种关系来进行对应的依赖注入
@Component()
public interface ManComponent {
Man getMan();
}
//build:makeBuild --> project模式 --> app --> build --> generated --> source --> apt --> debug
//DaggerManComponent.builder().build().getMan().toString();
@Provides 和 @Module
- Inject只能标记在我们自己写的类中,如果要提供第三方作为依赖对象就需要@Provides和@Module了
- @Provides 注解的依赖必须存在一个用 @Module 注解的类中
- 除了第三方无法Inject标记的在Module中提供,一些需要生成对象以后做其它初始化操作的也适合写到Module中
//没有提供Inject标记,视为无法改变的第三方类
public class LibClass {
@Override
public String toString() {
return "第三方类,不可修改\n";
}
}
@Module
public class ManModule {
@Provides
public LibClass provideLibClass(){
return new LibClass();
}
}
//在Component中关联该Module
@Component(modules = ManModule.class)
public interface ManComponent {
}
public class Man {
@Inject
LibClass libClass;
}
无法控制生成实例的类进行依赖
- 系统生成实例的类进行依赖,如Activity
/*
* 无法控制生成实例
*/
@Component(modules = ManModule.class)
public interface ManComponent {
void inject(DaggerMainActivity activity);
}
public class DaggerMainActivity extends BaseActivity {
@Inject
Man man;
protected void initData() {
DaggerManComponent.builder().build().inject(this);
man.toString();
}
}
实例需要特殊处理
public class FoodRiceEgg extends FoodRice {
private String mType = "";
public FoodRiceEgg() {}
public void setType(String type){
this.mType = type;
}
@Override
public String toString() {
return "食物:"+mType+"蛋炒饭;\n";
}
}
@Module
public class ManModule {
@Provides
public FoodRiceEgg provideFoodRiceRegg(){
FoodRiceEgg riceEgg = new FoodRiceEgg();
riceEgg.setType("金色的");
return riceEgg;
}
}
public class Man {
@Inject
FoodRiceEgg foodRiceEgg;
}
// 1. FoodRiceEgg extends FoodRice, 构造函数未改变,继承父类注解,所以这里不需要@Inject
// 2. Man中foodRiceEgg可以通Inject注解生成,也可通过Module获得,Module优先级高
提供依赖和注入的方式
提供依赖
- Inject标识构造方法
- 在Module中@Provide提供
(Inject标识和Module中提供相同的依赖,以Module中优先)
@Component注入依赖(纽带)
注入的方式有两种:
- 有Inject标识的返回该类型
- 无法控制生成实例的,作为参数传入,进行依赖(如Activity)
@Component()
public interface ComponentTest {
Man getMan();
void inject(Activity act);
}
@Singleton 引出 @Scope
@Singleton
public class SingleBean {
@Inject
public SingleBean() {
}
}
//另外也可在Module中以Provide的方式提供
@Module
public class SecondActivityModule {
@Provides
@Singleton
public TestSingleton provideTestSingleton(){
return new TestSingleton();
}
}
@Singleton
@Component(modules = ManModule.class)
public interface ManComponent {
Man getMan();
void inject(DaggerMainActivity activity);
}
// Man和Activity中Inject标注的实例就是单例的
@Scope
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
//@Singleton 所拥有的单例能力是以 Component 为范围的限定的
//@Singleton 起作用是因为它被 @Scope 注解。所以,我们也可以自己定义 Scope
//例如:以@PageScope来限定某一个依赖的作用域
@Scope
@Documented
@Retention(RUNTIME)
public @interface PageScope {}
@Named和@Qualifiers
同类型、不同实例:
在Module中Dagger是根据返回类型确定要提供的依赖
如果是同类型不同实例,那就会混乱了
public class Man {
@Inject
@Named("name")
String name;
@Inject
@StrPhone
String phone;
}
@Module
public class ManModule {
//1.@Named标注
@Provides
@Named("name")
public String provideName(){
return "Varmin";
}
//2. 自定义注解
@Provides
@StrPhone
public String providePhone(){
return "13439251326\n";
}
}
@Qualifier
//@Name 只是被 @Qualifier 注解的一个注解。所以,它能够有效完全是因为 @Qualifier
//所以,我们也可以@Qualifier为元注解自定义注解,来区分同类型不同实例提供依赖时的区分
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface StrPhone {
}
Component依赖
依赖:所以需要暴露接口
A依赖B,A想使用B的属性,就需要注入B的实例,并且B提供接口暴露出来
@Module
public class JobModule {
@Provides
public Coder provideCoder(){
return new Coder();
}
}
@Component(modules = JobModule.class)
public interface JobComponent {
//自用无需写,被依赖需显示提供
Coder getCoder();
}
//这里如果只是想复用Coder,也可以直接使用多个Module,不必使用依赖的概念
//@Component(modules = {ManModule.class, JobModule.class})
@Component(modules = ManModule.class, dependencies = JobComponent.class)
public interface ManComponent {
Man getMan();
}
public class Man {
@Inject
Coder coder;
}
DaggerManComponent.builder()
.jobComponent(jobComponent).build()
.getMan();
/**
* 如果只是在DaggerJobComponent中使用Coder,因为有JobModule,所以那直接用就好了,不用在Component中显示提供接口。
* 但是在DaggerManComponent中,Coder是来自于依赖的JobComponent所有
* DaggerManComponent中需注入JobComponent实例,并通过实例中的接口获得Coder
*/
Component继承
继承:子类可以获取到父类能提供的所有类型
子类:用@Subcomponent标记
父类:返回SubComponent getSubComponent()类型
@Component(modules = ManModule.class)
public interface ManComponent {
SubComponent getSubComponent();
}
@Subcomponent()
public interface SubComponent {
Man getMan();
void inject(DaggerMainActivity activity);
}
//Activity
@Inject
FoodRice foodRice;
...
@Inject
@Named("name")
String name;
DaggerManComponent.builder()
.getSubComponent()
.inject(this);
// 在Activity中getSubComponent可以获取到ManComponent可提供的所有类型
Component生命周期
onCreate()中inject()方法调用完成后,Component实例就会因为没有被引用而被垃圾回收器回收.
需要注意的是,使用Lazy和Provider时,与该依赖对象有关的Module实例会被Lazy和Provider引用,所以该Module实例不会被垃圾回收器回收
懒加载、强制刷新
@Inject
Lazy<LazyBean> lazyBean;
@Inject
Provider<RefreshBean> refreshBean;
lazyBean.get()
refreshBean.get()
refreshBean.get()