您的位置:首页 > 移动开发 > Android开发

Dagger2使用详解

2017-04-27 21:27 169 查看

Dagger2使用详解

什么是依赖注入?

依赖注入就是将调用者依赖的对象实例通过一定的方式从外部传入,解决了各个类之间的耦合问题。

这个外部,正是dagger2容器。需要什么对象从容器中取就行了,调用和被调用方被隔离开,通过一个容器将他们联系起来,从而实现了解耦。

Dagger2是Google出的依赖注入框架。Dagger2的原理是在编译期生成相应的依赖注入代码。其他框架是在运行时期反射获取注解内容,影响运行效率。

引入Dagger2

在app的build.grade中引入android-apt,并添加dagger2依赖

apply plugin: 'com.neenbedankt.android-apt'
dependencies {
apt "com.google.dagger:dagger-compiler:2.11-rc1"
provided 'org.glassfish:javax.annotation:10.0-b28'
compile "com.google.dagger:dagger:2.11-rc1"
}


在project级别的build.grade中添加android-apt

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}


Dagger2基本用法

@Inject

通常在需要依赖的地方使用这个注解,换句话说,你用它告诉dagger2这个类或这个字断需要依赖注入,这样dagger2就会构造一个这个类的实例去满足他们的依赖

@Module

Module类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样dagger2在构造类实例的时候就知道去哪里找这些依赖。

@Provide

在Module中,我们定义的方法使用这个注解,告诉dagger2我们想要构造对象并提供这些依赖

@Component

Component是一个注入器,也可以说是连接Inject和Module的桥梁



下面看个例子,通过例子来看下dagger2的基本用法:

// MainActivity.java
public class MainActivity extends AppCompatActivity {
@Inject LoginManager loginManager;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

DaggerLoginModuleComponent.create().inject(this);
loginManager.login();
}
}


调用LoginManager的login接口,因此需要将LoginManager对象注入到MainActivity中,注入的操作由DaggerLoginModuleComponent调用inject方法完成的,LoginModuleComponent是怎么和LoginManager关联上的呢?

// LoginModuleComponent.java
@Component(modules = {LoginModule.class})
public interface LoginModuleComponent {
void inject(MainActivity activity);
}


事实上,LoginModuleComponent只是LoginModule和MainActivity的连接器,可以想象成注射器,把LoginModule提供的对象注射到MainActivity中,完成依赖注入的过程。再看下LoginModule中提供了哪些对象?

// LoginModule.java
@Module
public class LoginModule {
@Provides
public LoginManager providerLoginManager() {
return new LoginManager();
}
}
// LoginManager.java
public class LoginManager {
public void login() {
Log.i("dagger2", "--- login ---");
}
}


可以看到是通过Provides注解来提供LoginManager对象的,用起来还是很简单的,运行一下:

04-27 18:38:05.893 2343-2343/com.testdes.des I/dagger2: --- login ---


Dagger2模块化

LoginManager中执行login操作,需要OkHttpClient对象,我们从构造方法中传入,如下:

// LoginManager.java
public class LoginManager {
OkHttpClient client;
public LoginManager(OkHttpClient client) {
this.client = client;
}
public void login() {
Log.i("dagger2", "--- login:" + client);
}
}
// LoginModule.java
@Module
public class LoginModule {

@Provides
public LoginManager providerLoginMgr(OkHttpClient client) {
Log.i("dagger2", "--providerLoginMgr");
return new LoginManager(client);
}

@Provides
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}


考虑到OkHttpClient不仅仅在登录的时候使用,其它网络操作也需要这个对象,因此把OkHttpClient做成公共模块,通过HttpModule提供出来。

// HttpModule.java
@Module
public class HttpModule {
@Provides
public OkHttpClient providerOKHTTP() {
return new OkHttpClient();
}
}
// LoginModule.java
@Module(includes = {HttpModule.class})
public class LoginModule {

@Provides
public LoginManager providerLoginMgr(OkHttpClient client) {
Log.i("dagger2", "--providerLoginMgr");
return new LoginManager(client);
}
}


在LoginModule中通过includes属性把HttpModule引入进来就可以了,实现了简单的HttpModule模块化,当然还有其他的方式,比如在LoginModuleComponent中引入HttpModule

@Component(modules = {LoginModule.class, HttpModule.class})
public interface LoginModuleComponent {
void inject(MainActivity activity);
}


或者创建HttpComponent,通过dependencies依赖,如下:

@Component(modules = {LoginModule.class}, dependencies = HttpComponent.class)
public interface LoginModuleComponent {
void inject(MainActivity activity);
}


Dagger2单例

单例只需要使用@Singleton注解来声明,当HttpModule的providerOKHTTP使用了Singleton,LoginModuleComponent也需要使用Singleton来标注,如下:

// HttpModule.java
@Module
public class HttpModule {
@Provides
public OkHttpClient providerOKHTTP() {
return new OkHttpClient();
}
}
// LoginModuleComponent.java
@Component(modules = {HttpModule.class})
@Singleton
public interface LoginModuleComponent {
void inject(MainActivity activity);
}

// MainActivity.java
public class MainActivity extends AppCompatActivity {
@Inject OkHttpClient okHttpClient;
@Inject OkHttpClient okHttpClient2;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

DaggerLoginModuleComponent.create().inject(this);
Log.i("dagger2", ""+okHttpClient);
Log.i("dagger2", ""+okHttpClient2);
}
}


运行一下,确实是单例

04-28 14:47:03.553 8497-8497/com.testdes.des I/dagger2: com.testdes.des.dagger2.OkHttpClient@3ef1ff
04-28 14:47:03.553 8497-8497/com.testdes.des I/dagger2: com.testdes.des.dagger2.OkHttpClient@3ef1ff


我们在增加一个页面SettingActivity,假设有个登出的功能,也需要用到OkHttpClient,代码不贴了,运行一下:

04-28 15:29:47.999 9781-9781/com.testdes.des I/dagger2: MainActivity:com.testdes.des.dagger2.OkHttpClient@3ef1ff
04-28 15:29:48.068 9781-9781/com.testdes.des I/dagger2: SettingActivity:com.testdes.des.dagger2.OkHttpClient@ba0d601


哇擦,又发现不单例了!!这是什么鬼?

这里解释下,dagger2的单例和传统java单例不一样,我们通常说的单例是在静态域里初始化的,只要程序不退出就一直保持单例,而dagger2单例是依附于component,即使声明了singleton,不同的component也不是单例,参考文章末尾的注意事项第7条。

所以我们如果要实现单例,需要怎么做呢?

思路是定义一个HttpComponent,作为其他component的dependencies component,具体如下:

// HttpComponent.java
@Component(modules = {HttpModule.class})
@Singleton
public interface HttpComponent {
}

// LoginModuleComponent.java
@Component(dependencies = {HttpComponent.class})
@Singleton
public interface LoginModuleComponent {
void inject(MainActivity activity);
}


运行一下,发现报错了

Error:(11, 1) error: This @Singleton component cannot depend on scoped components:
@Singleton com.testdes.des.dagger2.HttpComponent


错误原因参考文章末尾注意事项第4条,Singleton是一个scope, LoginModuleComponent和HttpComponent的scope不能相同,那我们只好给LoginModuleComponent自定义一个scope了,怎么定义可以参考Singleton

// ActivityScope.java
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {
}
// LoginModuleComponent.java
@Component(dependencies = {HttpComponent.class})
@ActivityScope
public interface LoginModuleComponent {
void inject(MainActivity activity);
}


运行一下,终于还是报错了

Error:(13, 8) error: com.testdes.des.dagger2.OkHttpClient cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
com.testdes.des.dagger2.OkHttpClient is injected at
com.testdes.des.MainActivity.okHttpClient
com.testdes.des.MainActivity is injected at
com.testdes.des.dagger2.LoginModuleComponent.inject(activity)


LoginModuleComponent没有办法得到HttpComponent提供的OkHttpClient对象,这是需要做一个桥接,增加一个get方法(名字任意取),返回一个OkHttpClient对象,如下

@Component(modules = {HttpModule.class})
@Singleton
public interface HttpComponent {
OkHttpClient get();
}


BUILD SUCCESSFUL

在MainActivity中注入的方法修改下:

// MainActivity.java
DaggerLoginModuleComponent.builder()
.httpComponent(DaggerHttpComponent.create()).build().inject(this);


需要构建一个httpComponent对象,按照上面的写法,每次用到都要调用DaggerHttpComponent.create()显然不合理,因此我们把httpComponent构建操作放到Application初始化的时候执行一次,如下:

// TestApplication.java
public class TestApplication extends Application{

HttpComponent httpComponent;
@Override public void onCreate() {
super.onCreate();
System.out.println("start TestApplication");

httpComponent = DaggerHttpComponent.create();
}

public HttpComponent getHttpComponent() {
return httpComponent;
}

}

// MainActivity.java
DaggerLoginModuleComponent.builder()
.httpComponent(((TestApplication)getApplication()).getHttpComponent()).build().inject(this);


SettingActivity中的写法和MainActivity保持一致,然后运行一下:

04-28 16:25:50.715 11228-11228/com.testdes.des I/dagger2: MainActivity:com.testdes.des.dagger2.OkHttpClient@39f1d88
04-28 16:25:50.769 11228-11228/com.testdes.des I/dagger2: SettingActivity:com.testdes.des.dagger2.OkHttpClient@39f1d88


终于单例了,以上是dagger2的基本用法和单例的实现,谢谢~

注意事项

component的inject方法接收父类型参数,而传入的是子类型的对象则无法注入

component关联的modules中不能有重复的provide

module中的provide方法使用了scope,则component需要使用同一个注解

component的dependencies与component自身不能使用同一个scope,即组件之间的scope不能相同

singleton的组件不能依赖其他scope组件,只能其他scope组件依赖singleton

没有scope的组件不能依赖有scope的组件

@singleton的生命周期依附于component,同一个module provide singleton,不同的component也不是单例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息