依赖注入

依赖注入咯

依赖注入

原理:

依赖注入是实现程序解耦的一种方式。如果通过百度搜索可以找到如下答案:
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题.控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。

个人理解:

在程序中,一个对象中的方法需要依赖另一个对象,该对象中保存其所依赖对象的实例,生成依赖对象的方式不再该对象中通过new创建,而是调用者外部创建依赖对象,通过一定的方式进行传入。

依赖注入的三种实现方式(原生方式):
  1. 构造方法注入 该方式是通过构造方法将其所依赖的外部类对象传入进来,是我认为的最简单的方式。其实现方式如下,我们修改之前的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Classes {
// 依赖类
private Boy boy;
/**
* 构造方法注入,通过构造方法传入该对象
* @param boy
*/
public Classes(Boy boy) {
this.boy = boy;
}
public void run() {
boy.run();
}
  1. Setter 注入:
1
2
3
4
5
6
7
8
9
 public class Classes {
//....
private Boy boy;

public void setBoy(Boy boy){
this.boy = boy;
}
//....
}
  1. 接口方式 接口方式是定义一个接口,该接口中声明一个注入的方法,而需要注入的类实现该接口,实现接口中定义的方法。定义接口,声明注入方法:
1
2
3
4
5
6
7
8
9
10
11
public interface BoyInjection {
void inject(Boy boy);
}

public class Classes implements BoyInjection {
//.... private Boy boy;
@Override public void inject(Boy boy) {
//实现接口中的方法 this.boy = boy;
}
//....
}
Android Dagger2依赖注入:
  • 引入依赖库:
1
2
3
compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

版本号以官网为准:https://github.com/google/dagger

  • 创建一个对象:
1
2
3
4
5
public class A {
public void eat() {
System.out.print("吃饭了");
}
}
  • 创建Moudule:
1
2
3
4
5
//第一步 添加@Module 注解
@Module
public class MainModule {
}

  • 创建具体的示例:
1
2
3
4
5
6
7
8
9
//第一步 添加@Module 注解
@Module
public class MainModule {
//第二步 使用Provider 注解 实例化对象
@Provides
A providerA() { // providerA只是个名称
return new A();
}
}
  • 创建一个Component:
1
2
3
4
5
6
7
//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class}) //与刚刚创建的Moudule连接起来
public interface MainComponent {
//第三步 写一个方法 绑定Activity /Fragment
void inject(MainActivity activity);
}

之后Rebuild Project一下,Make Project也行,之后系统会自动创建几个类,用来与activity连接。(这一步操作可能会要等待很长一段时间,10分钟左右)

  • 将Component与Activity/Fragment绑定关系:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MainActivity extends AppCompatActivity {
/***
* 第二步 使用Inject 注解,获取到A 对象的实例
*/
@Inject //记得在需要的参数前加@Inject
A a;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

/***
* 第一步 添加依赖关系
*/
//第一种方式与Activity/Fragment进行绑定
DaggerMainConponent.create().inject(this);
//第二种方式与Activity/Fragment进行绑定
DaggerMainConponent.builder().build().inject(this);
/***
* 第三步 调用A 对象的方法
*/
a.eat();
}
}

之后就可以了,接下来介绍Dagger2的单例模式.

单例模式:

@Singleton 这个注释可以使Dagger进入单例模式,要在Moudule和Component的上方添加上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Module
public class ActivityModule {
@Singleton
@Provides
Student privadeStu(){
return new Student();
}
}

@Singleton
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity mainActivity);
void inject(UserActivity userActivity);
}

添加@Singleton就变成单例了,在MainActivity里 声明两个Student不同名字的对象分别打印他的地址,你会发现地址是一样的
但是你再新建立一个Activity 再次声明 Student stu ,注入对象,你会发现这时候 地址就不一样了。因为持有的对象是Activity。
到此已经能在一个Activity里,声明两个对象变量,用@Inject注入,得到的是 同一个对象

@Singleton的分析:

接下来我要推翻刚才说的@Singleton,看到英文Singleton 是单例的意思,在Dagger2里他就是个命名的一个注解而已
他叫什么名字都行 @ABC、@ABB、@ABCC,都行。点开Singleton(ctrl+B)

1
2
3
4
@Scope   //作用域的注释,代表着被包括的都是在一个作用域之中
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

Singleton 用了@Scope(作用域) 标记。 也就是说被 @Scope包裹的Component 都是在一个作用域里的, 作用域!不叫单例
好 那直接用Scope包裹Component。编译器提示 直接Remove了这个注解。因为他的目标是 @Target(ANNOTATION_TYPE) 这个表示这个Scope只用于注解,不能用在 类上面

那我们声明一个注解:
1
2
3
4
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

接下来我们把刚才加@Singleton的地方全换成@ActivityScope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Module
public class ActivityModule {
@ActivityScope
@Provides
Student privadeStu(){
return new Student();
}
}

@ActivityScope
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity mainActivity);
void inject(UserActivity userActivity);
}

打印两个对象

1
2
tag: com.mydagger.bean.Student@96575e0
tag: com.mydagger.bean.Student@96575e0

刚才说了持有对象的是Activity,那只能在一个Activity中实现单例,那要全局单例怎么做呢?
通常要全局使用的对象,要么写个static的类,要么Application。我们直接Application

  • 全局单例:

    1. 先定义个全局的注解,刚讲了名字无所谓(这里要创建第二个的注解是因为,一个注解只能作用到一个作用域,简单来说就是不能与等下要创建的的另一个Component的注解相同)

      1
      2
      3
      4
       @Scope  //作用域的意思
      @Retention(RetentionPolicy.RUNTIME)
      public @interface AppScope {
      }
    2. 修改代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      @ActivityScope
      //先绑定自身的Module,在绑定要依赖的Component
      @Component(modules = {ActivityModule.class},dependencies = AppComponent.class)
      public interface ActivityComponent {
      //第一个activity的绑定方法(XXXActivity就是填写你自己的activity的名字)
      void inject(XXXActivity testActivity);
      //第二个activity的绑定方法
      void inject(XXXActivity testActivity);
      }

      这里增加了一个 dependencies(依赖),当前是ActivityComponent 依赖 AppComponent
      新建一个 AppComponent, 连接一个Module,提供一个方法 提供 给 MainActivity @Inject使用
      还有就是dependencies只是依赖与另一个Component,每个Component都必须要有一个不同的Modules(Modules中的bean类不能重复)

      1
      2
      3
      4
      5
      6
      7
      //注意这里的注解与上面注解不同(尽管效果是一样的,但也不能一样)
      @AppScope
      @Component(modules = {AppModule.class})
      public interface AppComponent {
      //这里要重新创建一个School类(这里写的方法要与Module中的类一致,否则就会报错)
      School getSchool();
      }

新建 AppModule, 提供一个方法 用AppScope和Provides标记

1
2
3
4
5
6
7
8
9
@Module
public class AppModule {
//这里的注解要与Component相同
@AppScope
@Provides
School provideSchool(){ //也是返回与上一个Module不一样的Bean类
return new School();
}
}

继续点绿锤子编译修改Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//创建一个自身的单例类
public static Application app;
//在创建一个Component类
AppComponent applicationComponent;

@Override
public void onCreate() {
super.onCreate();
//获取自身的实例
app = this;
//在onCreate中进行获取Component
applicationComponent = DaggerAppComponent
.builder()
//每次调用系统创建的Component类进行绑定的时候都要绑定属于自己的Module类
.appModule(new AppModule())
.build();
//因为这个Component是给上一个Component(MainComponent)进行依赖的,也就是扩展,所以没有写绑定的方法}

public static Application getInstance(){
//返回自身的实列
return app;
}

这里返回一个Application的实例对象。定一个AppComponent(这个是准备给DaggerActivityComponent依赖的)也是Dagger生成的代码。链式编程进行build
增加一个School类 和Student类一样 用@Inject修饰构造函数。
修改MainActivity,增加

1
2
@Inject
School school;

然后在onCreate后面增加(与activity和Fragmemt进行绑定)

1
2
3
4
5
6
7
8
DaggerActivityComponent.builder()
//绑定依赖Component(通过Application获取早就创建好的Component)
.appComponent(Application.getInstance().applicationComponent)
//绑定自身的Module
.activityModule(new ActivityModule())
.build()
//绑定Activity
.inject(this);

第二行DaggerActivityComponent 依赖于 Application的 AppComponent 这样就进行了关联
到此 全局单例 搞定,测试一下 写两个Activity 声明相同的对象就会发现 两个对象地址 一样的(创建的第二个activity/Fragment中也要进行Component绑定)

1
2
tag: com.mydagger.bean.School@393c7e3
tag: com.mydagger.bean.School@393c7e3
  • 扩展:
    通常会项目中使用MVP 增加网络配置等 需要全局单例的内容,可以继续建类(Module)进行扩展,得到单例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @AppScope
    @Component(modules = {AppModule.class,BModule.class....})
    public interface AppComponent {
    School getSchool();
    //这里需要继续提供方法,给Activity 进行对象的 @Inject
    }

    applicationComponent = DaggerAppComponent
    .builder()
    .appModule(new AppModule())......
    .build();