写点什么

Dagger2 入门系列一:基础使用,从基础到源码统统帮你搞定

用户头像
Android架构
关注
发布于: 13 小时前

buildTypes


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


{


release {


minifyEnabled false


proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'


}


}


compileOptions {


sourceCompatibility JavaVersion.VERSION_1_8


targetCompatibility JavaVersion.VERSION_1_8


}


}


dependencies {


implementation fileTree(dir: 'libs', include: ['*.jar'])


implementation 'androidx.appcompat:appcompat:1.1.0'


implementation 'androidx.constraintlayout:constraintlayout:1.1.3'


testImplementation 'junit:junit:4.12'


androidTestImplementation 'androidx.test.ext:junit:1.1.1'


androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'


implementation 'com.google.dagger:dagger:2.17'


annotationProcessor 'com.google.dagger:dagger-compiler:2.17'


// implementation 'com.google.dagger:dagger-android:2.17'


// implementation 'com.google.dagger:dagger-android-support:2.17'


// annotationProcessor 'com.google.dagger:dagger-android-processor:2.17'


//butterknife


implementation 'com.jakewharton:butterknife:10.2.1'


annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'


}


2、代码实战


======


2.1、创建一个 Student 类,并添加 Inject 注解




import javax.inject.Inject;


public class Student {


@Inject


public Student() {


}


}


备注 1:添加 @Inject 后,使用 Ctrl+F9(或者 rebuild)进行一次编译。


备注 2:javax.inject 包的目录如下所示:



2.2、编译后项目的变化




在目录 Dagger2Test1\app\build\generated\ap_generated_sources\debug\out\com\gs\dagtest1\bean 可以看到一个 Student_Factory 的类。



具体内容:


// Generated by Dagger (https://google.github.io/dagger).


package com.gs.dagtest1.bean;


import dagger.internal.Factory;


public final class Student_Factory implements Factory<Student> {


private static final Student_Factory INSTANCE = new Student_Factory();


@Override


public Student get() {


return provideInstance();


}


public static Student provideInstance() {


return new Student();


}


public static Student_Factory create() {


return INSTANCE;


}


public static Student newStudent() {


return new Student();


}


}


可以看到这是一个工厂类,而我们可以通过三种方式获取到 Student 对象,如下:


Student student1 = Student_Factory.create().get();


LogUtils.e(student1.toString());


Student student2 = Student_Factory.newStudent();


LogUtils.e(student2.toString());


Student student3 = Student_Factory.provideInstance();


LogUtils.e(student3.toString());


打印结果如下:



综上,我们通过 @Inject 注解了一个 Student 的构造方法后,可以让编译器帮助我们生成一个对应的工厂类 Student_Factory,通过该工厂类,我们可以通过三种方法获取到 Student 对象。


当然,通过这三种方式获取到 Student 对象我们自己就可以实现,这里只是让大家更方便地理解编译类。


2.3、获取 Student 对象




我们创建一个 Activity 来调用 Student


public class Test1Activity extends AppCompatActivity {


@Inject


Student student;


@Override


protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


setContentView(R.layout.activity_test1);


ButterKnife.bind(this);


}


@OnClick(R.id.btn1)


public void onViewClicked() {


Toast.makeText(this, student.toString(), Toast.LENGTH_SHORT).show();


}


}


接下来我们创建一个 Activity 类,在这个类中创建一个成员变量 Student,按照 Dagger2 给我们的指示,当我们需要一个 Student,我们只需要在这个成员变量上方加一个 @Inject 注解,编译器会自动帮我们产生对应的代码,我们就可以直接使用这个 Student 对象了!


本案例中我们设置一个 Button,点击 Button 后我们打印出这个 Student 对象。


事实真的如此吗?我们直接运行代码,并点击 Button,很遗憾,直接报空指针异常:



显然,和平常使用的结果一样,@Inject 并没有帮助我们初始化对应的 Student 对象,或者说,我们的 Activity 并没有使用刚才我们看到的 Student_Factory 类,不过也可以理解,我们并没有建立 Activity 和 Student_Factory 类之间的关系嘛。


2.4、创建 Module 类和 Component 接口




import dagger.Module;


@Module


public class Test1Module {


private Test1Activity activity;


Test1Module(Test1Activity activity) {


this.activity = activity;


}


}


import dagger.Component;


@Component(modules = Test1Module.class)


public interface Test1Component {


void inject(Test1Activity activity);


}


请注意,Module 类上方的 @Module 注解意味着这是一个提供数据的【模块】,而 Component 接口上方的 @Component(modules = Test1Module.class)说明这是一个【组件】(我更喜欢称呼它为注射器)。


突然出现的这两个类可以称得上是莫名其妙,因为我们从代码上来看并不知道这对于 Student 和 Activity 之间关系有什么实质性的进展,但假如我们这时在 Activty 中添加这一段代码:


public class Test1Activity extends AppCompatActivity {


@Inject


Student student;


@Override


protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


setContentView(R.layout.activity_test1);


ButterKnife.bind(this);


initDatas();


}


private void initDatas() {


DaggerTest1Component.builder()


.test1Module(new Test1Module(this))


.build()


.inject(this);


}


@OnClick(R.id.btn1)


public void onViewClicked() {


Toast.makeText(this, student.toString(), Toast.LENGTH_SHORT).show();


}


}


然后运行代码点击 Button,神奇的事情发生了:



显然,添加了两个看起来莫名其妙的 Module 和 Component 类,然后在 Activity 中添加一段代码,被 @Inject 的 Student 类被成功依赖注入到了 Activity 中,我们接下来就可以肆无忌惮使用这个 Student 对象了!


3、我们为什么使用依赖注入


=============


这时候不可避免的,有些同学会有些疑问,我们为什么要花费这么大的力气(时间成本)去学习这样一个看起来很鸡肋的 Dagger 呢?我们需要一个 Student 对象,完全可以直接通过 new 的方式创建一个嘛!


当然是有必要的,因为通常简单的代码具有耦合性,而要想降低这样的耦合就需要其他的辅助代码,其实少代码量和低耦合这两者并不能同时兼顾。


试想,我们如果通过这样的方式,在其他的文件中创建了这样若干个(心大一些,我们是一个大项目的唯一负责人),不,1000 个文件中使用到了 Student 对象,我们至少要 new 1000 个新的 Student 类对象,这时,新的需求到了,我们的 Student 需要添加一个 String 类型的参数 name。


what the fuck? 这意味我需要分别跑这 1000 个文件中逐个修改 new Student()的那行代码吗?


如果是 Dagger2,当然不需要,我们只需要在 Student 类中做出简单的修改即可,这在后文中将会提到(因为涉及参数问题),我们只需要知道只需轻松几步即可完成 Student 的构造修改问题,达到低耦合的效果即可。


4、Module 和 Component 作用详解


======================


现在我们具体的思考以下一个场景:


我们假设案例中的 Activity 代表家庭住址,Student 代表某个商品,现在我们需要在家(Activity)中使用商品(Student),我们网购下单,商家(代表着案例中自动生成的 Student_Factory 工厂类)将商品出厂,这时我们能够在家直接获得并使用商品吗?


当然不可能,虽然商品(Student)已经从工厂(Factory)生产出来,但是并没有和家(Activity)建立连接,我们还需要一个新的对象将商品送货上门,这种英雄级的人物叫做——快递员(Component,注入器)。


没错,我们需要这样的一种注入器,将已经生产的 Student 对象传递到需要使用该 Student 的容器 Activity 中,于是我们需要在 Activity 中增加这样几行代码:


DaggerTest1Component.builder()

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Dagger2入门系列一:基础使用,从基础到源码统统帮你搞定