IOC 架构设计之 Dagger2 架构设计(三)
buildscript {repositories {jcenter()}dependencies {//添加(2)classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'}}
android {compileSdkVersion 23buildToolsVersion "23.0.0"
defaultConfig {applicationId "com.demo.zejun.repodragger2"minSdkVersion 15targetSdkVersion 23versionCode 1versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}}
dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:23.0.0'//添加(3)apt 'com.googl
e.dagger:dagger-compiler:2.0'//添加(4)compile 'com.google.dagger:dagger:2.0'}
第二步:User
作为目标类中需要实例化的成员对象,给其构造函数添加@Inject
标签:
public class User {
public String name;
@Injectpublic User() {name = "lizejun";}
public String getName() {return name;}}
第三步:声明Component
:
@Component()public interface OnlyInjectComponent {void inject(AnnotationActivity annotationActivity);}
第四步:在目标类中添加注解@Inject
,并根据我们第 3 步中声明的Component
,调用DaggerXXX
方法来进行注入:
public class AnnotationActivity extends AppCompatActivity {
@Injectpublic User mUser;
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_dragger2);//在第 3 步声明的 Component 接口或者抽象类的基础上,添加 Dagger 前缀。DaggerOnlyInjectComponent.builder().build().inject(this);}
}
上面这个例子有两个缺点:
只能标记一个构造方法,因为如果标记两个以上,不知道要用哪一个构造提供实例。
不能标记其它我们不能修改的类,例如第三方库。
如果用
@Inject
标记的构造函数如果有参数,那么这个参数也需要其它地方提供依赖,而类似于String
这些我们不能修改的类,只能用@Module
中的@Provides
来提供实例了。
四、采用@Module
来提供依赖
采用@Module
标记的类提供依赖是常规套路,@Module
标记的类起管理作用,真正提供依赖实例靠的是@Provides
标记的带返回类型的方法。第一步:和上面类似,我们定义一个依赖类,但是它的构造方法并不需要用@Inject
标记:
public class Person {
private String name;
public Person() {this.name = "lizejun";}
public String getName() {return name;}}
第二步:我们需要定义一个@Module
来管理这些依赖类的实例:
@Modulepublic class PersonDataModule {
@Providespublic Person providePerson() {return new Person();}}
第三步:定义一个@Component
,它指向上面定义的@Module
@Component(modules = {PersonDataModule.class})public interface PersonInjectComponent {void inject(PersonInjectActivity injectActivity);}
第四步:在目标类中进行依赖注入
public class PersonInjectActivity extends Activity {
@InjectPerson mPerson;
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);DaggerPersonInjectComponent.create().inject(this);System.out.println("Person name=" + mPerson.getName());}}
这里注入的方式有两种,一种是像上面这样的,它适合于PersonDataModule
中只有一个无参的构造方法,否则我们需要这样调用:
DaggerPersonInjectComponent.builder().personDataModule(new PersonDataModule()).build().inject(this);
五、初始化依赖实例的步骤
查找
Module
中是否存在创建该类型的方法(即@Component
标记的接口中包含了@Module
标记的Module
类,如果没有则直接查找@Inject
对应的构造方法)。如果存在创建类方法,则查看该方法是否有参数
如果不存在参数,直接初始化该类的实例,一次依赖注入到此结束。
如果存在参数,则从步骤 1 开始初始化每个参数。
如果不存在创建类方法,则查找该类型的类中有
@Inject
标记的构造方法,查看构造方法是否有参数:如果不存在参数,则直接初始化该类实例,一次依赖注入到此结束。
如果存在参数,则从步骤 1 开始初始化每个参数。
六、@Qualifier
限定符
在Dagger
中,有一个已经定义好的限定符,@Name
,下面我们也自己定义一个限定符:
@Qualifier@Retention(RetentionPolicy.RUNTIME)public @interface PeopleThreeQualifier {}
第一步:和前面类似,我们先定义一个需要实例化的依赖类:
public class People {
private int count;
public People() {count = 0;}
public People(int count) {this.count = count;}
public int getCount() {return count;}}
第二步:我定义一个DataModule
,和前面不同的是,在它的provideXXX
方法的注解中,我们添加了@Name(xxx)
和自定义的注解PeopleThreePeople
:
@Modulepublic class PeopleDataModule {
@Provides@Named("Five People")People provideFivePeople() {return new People(5);}
@Provides@Named("Ten People")People provideTenPeople() {return new People(10);}
@Provides@PeopleThreeQualifierPeople provideThreePeople() {return new People(3);}}
第三步:定义Component
@Component(modules = PeopleDataModule.class)public interface PeopleInjectComponent {void inject(PeopleInjectActivity peopleInjectActivity);}
第四步:在目标类中进行依赖注入,在提供@Inject
注解时,我们还需要声明和PeopleDataModule
中对应的限定符,这样Dagger
就知道该用那个函数来生成目标类中的依赖类实例:
public class PeopleInjectActivity extends Activity {
@Inject@Named("Five People")People mFivePeople;
@Inject@Named("Ten People")People mTenPeople;
@Inject@PeopleThreeQualifierPeople mThreePeople;
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);DaggerPeopleInjectComponent.builder().peopleDataModule(new PeopleDataModule()).build().inject(this);System.out.println("Five People=" + mFivePeople.getCount() + ",Ten People=" + mTenPeople.getCount() + ", Three People=" + mThreePeople.getCount());}}
七、@Scope
@Scope
的作用主要是在组织Component
和Module
的时候起到一个提醒和管理的作用,在Dagger
中,有一个默认的作用域@Singleton
。@Scope
的作用是:Dagger2
可以通过自定义Scope
注解,来限定通过Module
和Inject
方式创建的类的实例的生命周期能够与目标类的生命周期相同。Scope
的真正作用在与Component
的组织:
更好的管理
Component
之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定的Scope
注解标注这些Component
,而且编译器会检查有依赖关系或包含关系的Component
,若发现有Component
没有用自定义Scope
注解,则会报错。更好地管理
Component
与Module
之间地关系,编译器会检查Component
管理的Module
,若发现Component
的自定义Scope
注解与Module
中的标注创建类实例方法的注解不一样,就会报错。提高程序的可读性。
下面是一个使用@Singleton
的例子:第一步:定义需要实例化的类:
public class AnSingleObject {
private String objectId;
public AnSingleObject() {objectId = toString();}
public String getObjectId() {return objectId;}}
第二步:定义DataModule
,在它的provideXXX
方法,提供了@Singletion
注解:
@Modulepublic class AnSingleObjectDataModule {
@Provides@SingletonAnSingleObject provideAnSingleObject() {return new AnSingleObject();}}
第三步:定义Component
,和前面不同的是,需要给这个Component
添加@Singleton
注解:
@Component(modules = {AnSingleObjectDataModule.class})@Singletonpublic abstract class AnSingleObjectInjectComponent {
private static AnSingleObjectInjectComponent sInstance;
public abstract void inject(AnSingleObjectInjectActivity anSingleObjectInjectActivity);
public static AnSingleObjectInjectComponent getInstance() {if (sInstance == null) {sInstance = DaggerAnSingleObjectInjectComponent.create();}return sInstance;}}
第四步:在目标类中进行依赖注入,每次启动Activity
的时候,我们可以发现打印出来的hash
值都是相同的:
public class AnSingleObjectInjectActivity extends Activity {
@InjectAnSingleObject object;
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);AnSingleObjectInjectComponent.getInstance().inject(this);System.out.println("AnSingleObject id=" + object.getObjectId());}}
八、组织Component
Component
有三种组织方式:
依赖:一个
Component
依赖一个或多个Component
,采用的是@Component
的dependencies
属性。包含:这里就用到了
@SubComponent
注解,用它来标记接口或者抽象类,表示它可以被包干。一个Component
可以包含一个或多个Component
,而且被包含的Component
还可以继续包含其它的Component
。继承:用一个
Component
继承另外一个Component
。
评论