参考:
User’s Guide
https://dagger.dev/users-guide
Android - Dagger2使用详解:
https://www.jianshu.com/p/2cd491f0da01
秒懂依赖注入及 Dagger2 的实用技能(如何在Android中使用):
https://blog.csdn.net/ShuSheng0007/article/details/80950117
秒懂 Java注解类型(@Annotation):
https://blog.csdn.net/shusheng0007/article/details/80622035
是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫黄油刀,Dagger2被叫做利器啊,他的主要作用,就是对象的管理,其目的是为了降低程序耦合。
google在dagger2 官网定义如下:
Dagger is a fully static, compile-time dependency injection >framework for both Java and Android.Dagger 是一个可以用于Java及Android平台的依赖注入框架,其注入过程完全是静态的在编译时期完成的(通过编译时产生代码的方式,区别于Spring等框架的依赖反射的方式,反射是基于运行时的)。
第一:dagger 是一个依赖注入框架,首要任务当然是解决依赖注入的问题。
第二:dagger主要想通过编译时产生代码的方式来解决那些基于反射的依赖注入框架所存在的缺点,例如性能问题,开发过程中存在的问题。
In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state.[1] Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
依赖注入是一种给一个对象提供其依赖的对象的技术。例如A对象需要依赖B对象,那么此处关键点就是不能在A里面去new B对象,必须通过某种方式将B对象提供给(注入)A中,例如最简单的方式是通过一个 set(B b)方法
Dagger2不能将不同的组件往一个类里面注入,我们可以将多个组件打包成一个组件,然后再这个组件注入到需要的地方。
在app的build.gradle中添加依赖和注解处理器
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
新建object包,然后在包中新建两个类
public class HttpObject {
}
public class DatabaseObject {
}
新建package名为di,在di包下创建HttpModule和DatabaseModule,用来提供对象。
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
/**
* 用来提供对象
*/
@AppScope
@Module
public class HttpModule {
@AppScope
@Provides
public HttpObject providerHttpObject(){
return new HttpObject();
}
// @Provides
// public XXXObject providerXXXObject(){
// return new XXXObject();
// }
}
/**
* 用来提供对象
*/
@Module
public class DatabaseModule {
@Provides
public DatabaseObject providerDatabaseObject(){
return new DatabaseObject();
}
}
新建Component,并使Module和Component建立连接,并注入到Activity中
@Component(modules = {HttpModule.class, DatabaseModule.class})
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);
}
贴上DaggerMyComponent代码:
public final class DaggerMyComponent implements MyComponent {
private Provider providerHttpObjectProvider;
private Provider providerDatabaseObjectProvider;
private MembersInjector mainActivityMembersInjector;
private DaggerMyComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static MyComponent create() {
return builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.providerHttpObjectProvider =
HttpModule_ProviderHttpObjectFactory.create(builder.httpModule);
this.providerDatabaseObjectProvider =
DatabaseModule_ProviderDatabaseObjectFactory.create(builder.databaseModule);
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(
providerHttpObjectProvider, providerDatabaseObjectProvider);
}
@Override
public void injectMainActivity(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
public static final class Builder {
private HttpModule httpModule;
private DatabaseModule databaseModule;
private Builder() {}
public MyComponent build() {
if (httpModule == null) {
this.httpModule = new HttpModule();
}
if (databaseModule == null) {
this.databaseModule = new DatabaseModule();
}
return new DaggerMyComponent(this);
}
public Builder httpModule(HttpModule httpModule) {
this.httpModule = Preconditions.checkNotNull(httpModule);
return this;
}
public Builder databaseModule(DatabaseModule databaseModule) {
this.databaseModule = Preconditions.checkNotNull(databaseModule);
return this;
}
}
}
在MainActivity中通过 @Inject创建对象
public class MainActivity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Inject
DatabaseObject databaseObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMyComponent.create().injectMainActivity(this);
Log.i("MainActivity",httpObject.hashCode() + "");
Log.i("MainActivity",databaseObject.hashCode() + "");
}
}
打印:
MainActivity: 77306469
MainActivity: 241808186
HttpObject 和 DatabaseObject对象构建成功。
如果想修改HttpObject的构造方法,我们并不需要修改Activity中的代码,只需修改HttpModule即可,如下图:
上面MainActivity我们使用如下代码实现注入。
DaggerMyComponent.create().injectMainActivity(this);
我们也可以通过如下代码实现:
DaggerMyComponent.builder().httpModule(new HttpModule()).build().injectMainActivity(this);
build()方法如下图所示:
可以看到在build方法中实例化了DaggerMyComponent对象。
有了DaggerMyComponent对象,接下来看看书如何注入的。
在DaggerMyComponent的injectMainActivity调用了injectMembers方法,如下图:
injectMembers是MembersInjector接口里面的一个方法,如下图:
injectMembers方法具体实现是在MainActivity_MembersInjector这个类中,如下图:
从上图中可以看到,在MainActivity_MembersInjector类中的injectMembers方法中生成了我们需要的三个Object实例。
生成不同的Object实例是通过不同的Provider的get()方法得到的,那get()方法又是如何实现的呢?
在DaggerMyComponent的initialize方法中生成了不同的Provider,如下图:
Provider调用get方法最后还是调用了Module中的providerHttpObject方法,providerHttpObject方法是带有 @Provides 注解的。
在providerHttpObject上添加@Singleton注解,在对应的Module和Component上都要添加@Singleton注解。
@Singleton
@Module
public class HttpModule {
@Singleton
@Provides
public HttpObject providerHttpObject(){
return new HttpObject();
}
}
@Singleton
@Component(modules = {HttpModule.class, DatabaseModule.class})
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);
}
MainActivity代码不做修改,如下:
public class MainActivity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Inject
HttpObject httpObject2;
@Inject
DatabaseObject databaseObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMyComponent.create().injectMainActivity(this);
// DaggerMyComponent.builder().httpModule(new HttpModule()).build().injectMainActivity(this);
Log.i("MainActivity",httpObject.hashCode() + "");
Log.i("MainActivity",httpObject2.hashCode() + "");
Log.i("MainActivity",databaseObject.hashCode() + "");
}
}
运行查看结果httpObject和httpObject2是同一个对象。
public class SecActivity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sec);
DaggerMyComponent.create().injectSecActivity(this);
Log.i("hongxue","sec="+httpObject.hashCode()+"");
}
}
查看结果,发现SecActivity和MainActivity中httpObject并不是同一个对象。
why?
这就是局部单例。
查看DaggerMyComponent的initialize方法和之前有所不同,如下图箭头位置所示:
查看DoubleCheck的代码,这里贴上完整的代码:
public final class DoubleCheck implements Provider, Lazy {
private static final Object UNINITIALIZED = new Object();
private volatile Provider provider;
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider provider) {
assert provider != null;
this.provider = provider;
}
@SuppressWarnings("unchecked") // cast only happens when result comes from the provider
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = provider.get();
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
public static Provider provider(Provider delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Bind that delegates to a scoped
* binding, we shouldn't cache the value again. */
return delegate;
}
return new DoubleCheck(delegate);
}
/** Returns a {@link Lazy} that caches the value from the given provider. */
public static Lazy lazy(Provider provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy lazy = (Lazy) provider;
// Avoids memoizing a value that is already memoized.
// NOTE: There is a pathological case where Provider may implement Lazy, but P and L
// are different types using covariant return on get(). Right now this is used with
// DoubleCheck exclusively, which is implemented such that P and L are always
// the same, so it will be fine for that case.
return lazy;
}
return new DoubleCheck(checkNotNull(provider));
}
}
在MainActivity和SecActivity中我们创建了不同的DaggerMyComponent对象,所以在两个页面中通过单例获取到的实例也不同,如果想获取到相同的实例,可以在Application中创建DaggerMyComponent对象。
创建MyApplication:
public class MyApplication extends Application {
private MyComponent myComponent;
@Override
public void onCreate() {
super.onCreate();
myComponent = DaggerMyComponent.create();
}
public MyComponent getAppComponent() {
return myComponent;
}
}
分别在MainActivity和SecActivity中添加如下代码:
((MyApplication) getApplication()).getAppComponent().injectMainActivity(this);
((MyApplication) getApplication()).getAppComponent().injectSecActivity(this);
新建名为"presenter_di" 的 package,然后在里面新建Presenter、PresenterModule和PresenterModule:
public class Presenter {
}
@Module
public class PresenterModule {
@Provides
public Presenter providePresenter() {
return new Presenter();
}
}
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
void injectMainActivity(MainActivity mainActivity);
// Presenter providePresenter();
}
在MainActivity中添加注入
@Inject
Presenter presenter;
编译后报错,说明一个Activity中不能直接使用两个不同的Component。
可以通过依赖解决这个问题。
修改PresenterComponent:
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
// void injectMainActivity(MainActivity mainActivity);
Presenter providePresenter();
}
PresenterComponent不再提供注入方法,只是提供了一个Presenter对象。
在MyComponent上将PresenterComponent添加为dependencies:
@Singleton
@Component(modules = {HttpModule.class, DatabaseModule.class},
dependencies = {PresenterComponent.class})
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);
void injectSecActivity(SecActivity secActivity);
}
此时可以编译通过了。
修改MyApplication,将DaggerPresenterComponent对象添加到DaggerMyComponent中,如下:
public class MyApplication extends Application {
private MyComponent myComponent;
@Override
public void onCreate() {
super.onCreate();
// myComponent = DaggerMyComponent.create();
myComponent = DaggerMyComponent
.builder()
.httpModule(new HttpModule())
.presenterComponent(DaggerPresenterComponent.create())
.build();
}
public MyComponent getAppComponent() {
return myComponent;
}
}
我们如果在PresenterComponent上添加@Singleton注解,那么程序也不能编译通过。
@Singleton
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
// void injectMainActivity(MainActivity mainActivity);
Presenter providePresenter();
}
也就是说:一个Component使用了@Singleton注解,另一个Component也使用了@Singleton注解就会编译报错。
那怎么处理这个问题会比较好呢?
可以看到@Singleton注解上使用了@Scope注解,@Scope限定了注解作用域。
我们可以自己自定义Singleton。
在di包下面新建scope包,并新建AppScope注解,如下:
@Scope
@Documented
@Retention(RUNTIME)
public @interface AppScope {
}
将使用@Singleton地方替换为@Scope,运行后查看结果与使用@Singleton结果一样,如下:
@Singleton只是一个模板,我们需要scope都自定义 dependencies:组件依赖
我们在定义一个UserScope注解,在Presenter上使用
@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {
}
@UserScope
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
// void injectMainActivity(MainActivity mainActivity);
Presenter providePresenter();
}
@UserScope
@Module
public class PresenterModule {
@UserScope
@Provides
public Presenter providePresenter() {
return new Presenter();
}
}
编译后运行得到了相同的结果。
在上面使用dependencies来确定两个component的依赖关系,我们还可以使用@Subcomponent注解。
为了演示方便,这里重新新建一个项目。
1 在object包下面新建DatabaseObject和HttpObject:
public class DatabaseObject {
}
public class HttpObject {
}
2 在di包下新建DatabaseModule和HttpModule
/**
* 用来提供对象
*/
@Module
public class DatabaseModule {
@Provides
public DatabaseObject providerDatabaseObject() {
return new DatabaseObject();
}
}
@Module
public class HttpModule {
@Provides
public HttpObject providerHttpObject() {
return new HttpObject();
}
}
3 在di包下新建DatabaseComponent和HttpComponent。其中DatabaseComponent使用了@Subcomponent注解
/**
* 这就是一个子组件
*/
@Subcomponent(modules = {DatabaseModule.class})
public interface DatabaseComponent {
void injectMainActivity(MainActivity mainActivity);
}
@Component(modules = {HttpModule.class})
public interface HttpComponent {
DatabaseComponent buildDatabaseComponent();
}
4 使用
public class MainActivity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Inject
HttpObject httpObject2;
@Inject
DatabaseObject databaseObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerHttpComponent.create().buildDatabaseComponent().injectMainActivity(this);
Log.i("hongxue", httpObject.hashCode() + "");
Log.i("hongxue", httpObject2.hashCode() + "");
Log.i("hongxue", databaseObject.hashCode() + "");
}
}
运行后正常打印结果。
在项目中,可能会有多个网络请求地址,我们可以通过如下设置。
1 HttpObject中添加baseUrl属性;
2 在HttpModule中定义一个List用于保存请求地址的集合。在获取HttpObject对象的方法上使用@Named注解,使用的时候就通过这个注解获取HttpObject对象。
public class HttpObject {
public String baseUrl;
public HttpObject(String baseUrl) {
this.baseUrl = baseUrl;
}
public HttpObject() {
}
}
@Module
public class HttpModule {
ArrayList baseUrl;
public HttpModule(ArrayList baseUrl) {
this.baseUrl = baseUrl;
}
@Named("base1")
@Provides
public HttpObject providerHttpObject1() {
return new HttpObject(baseUrl.get(0));
}
@Named("base2")
@Provides
public HttpObject providerHttpObject2() {
return new HttpObject(baseUrl.get(1));
}
}
public class MainActivity extends AppCompatActivity {
String url1 = "www.sina.com.cn";
String url2 = "www.163.com.cn";
@Inject
@Named("base1")
HttpObject httpObject1;
@Inject
@Named("base2")
HttpObject httpObject2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList url = new ArrayList<>();
url.add(url1);
url.add(url2);
DaggerHttpComponent.builder().httpModule(new HttpModule(url)).build()
.buildDatabaseComponent().injectMainActivity(this);
// DaggerHttpComponent.create().buildDatabaseComponent().injectMainActivity(this);
Log.i("hongxue", httpObject1.baseUrl + "");
Log.i("hongxue", httpObject2.baseUrl + "");
}
}
使用dependencies ,还是会生成不同的DaggerXXXComponent文件,并且DaggerPresenterComponent最为被依赖的对象,在构造方法中可以传参。
使用Subcomponent,DatabaseComponentImpl只是一个内部类,我们发现DatabaseComponentImpl构造方法中不能传参。
所以我们尽量使用dependencies保持可扩展性。
代码Github下载:
https://github.com/345166018/AndroidIOC/tree/master/HxDagger2
https://github.com/345166018/AndroidIOC/tree/master/HxDagger2Sub