学习资料在这
日常开发中使用到了 Dagger2 框架,一直知识按照别人的代码“依葫芦画瓢”,没有自己去好好的了解这个框架。最近花了时间去仔细学习了一下这个框架,然后把所学的东西总结一下。
为什么会有Dagger2这个框架产生?
一般我们在我们写的代码中,会这样实例化
public class A {
public A() {
}
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
A mA = new A();
Log.i(TAG, mA.toString());
}
}
这样写一般来讲是没有什么大问题的,但是我们考虑下面的这个情况。由于业务需求我们需要在 A 中添加一个成员变量 mUser
public class User {
public User(){
}
}
public class A {
private User mUser;
public A(User user) {
mUser = user;
}
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
A mA = new A(new User());
Log.i(TAG, mA.toString());
}
}
如果 A 被 100个地方实例化,那估计改程序的人要骂娘了。。。
可能会有人说重载构造方法,也可以解决这个问题。但是如果下一次需要你添加一个 Person 成员变量,下下次需要再添加... 显然这个解决方案并不利于代码维护。
顺便说一下另一个问题,这么多的 new 操作也是很浪费体力的一件事。
注入式框架就是为了解决这个问题而生的(Dagger是注入式框架的一种)。Dagger2 与其它注入式框架不同的是 dagger2 是编译时生成相关代码。而大部分其它注入式框架是运行时注入。
下面先来预先体验一下 用Dagger2 改造后的代码
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
}
@Module
public class AppModule {
@Provides
public User provideUser(){
return new User();
}
@Provides
public A provideA(User user){
return new A(user);
}
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Inject
A mA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerAppComponent.create().inject(this);
Log.i(TAG, mA.toString());
}
}
可以看到我们省去了 具体的实例化过程。可能暂时还看不懂,但是我可以告诉你,不管以后需求需要添加什么成员变量,MainActivity中的代码“不需要动”。下面开始正式进入 Dagger2 !!!
引入 Dagger2 (Android Studio)
compile 'com.google.dagger:dagger:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
Dagger 2的几个重要知识点
0.@Inject
1.@Module
2.@Provides
3.@Component
4.@Qualifier
5.@Scope
6.@SubComponent
7.@Binds
以下内容,全部取自这个系列的Blog ,非常感谢原作者。
作者先是从日常开发中最常用的单例模式开始讲解
1.单例创建
@Module
public class AppModule {
private Context mContext;
public AppModule(@NonNull Context context) {
mContext = context;
}
@Provides
@Singleton
Context provideContext(){
return mContext;
}
}
@Module
public class ReceiversModule {
@Provides
@NonNull
@Singleton
public NetworkChannel provideNetworkChannel(){
return new NetworkChannel();
}
}
@Module
public class UtilsModule {
@Provides
@NonNull
@Singleton
public RxUtilsAbs provideRxUtils(Context context){
return new RxUtils(context);
}
@Provides
@NonNull
@Singleton
public NetworkUtils provideNetworkUtils(Context context, NetworkChannel networkChannel){
return new NetworkUtils(context, networkChannel);
}
}
正如我们知道的 @Module 注解是用来指明某个类用来提供依赖。我将会称这些类 为 Modules,并且我会这些提供依赖的方法为 provide method。例如 ReceiverModules 中有 provideNetworkChannel 方法。@Provides 标记的方法重要的是它的返回类型,返回类型可以是抽象类型,具体方法名是什么其实不那么重要,但是我们习惯以 provide开头。
看一下 UtilsModule 中的public NetworkUtils provideNetworkUtils(Context context, NetworkChannel networkChannel),他的方法中有两个参数,它会向 Dagger 表明我需要这两个参数创建 NetworkUtils 对象。这也是为什么我们会写了 AppModule 和 ReceiversModule的原因(提供这两个参数的依赖)。
下面创建Component
@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
void inject(DaggerActivity daggerActivity);
}
@Component 是 @Module 和 @Inject 之间的一座桥梁。@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})这行代码向Dagger 表明AppComponent 由三个 Module组成。 每个 Module提供的依赖都能被其它Module使用,这三个模块被 AppComponent紧紧的连接在了一起!下面的图很好的表明了三者之间的关系
1*gdJbgBd-LqSf1KOtUiriOw.png
这张图很好的表明了 Dagger2 获取 Context 和 NetwrokChannel 去创建 NetworkUtils对象。举个例子,假设我们去掉 AppModule 那么Dagger 2就会报错,因为它不知道如何去获取 Context 对象。当然我们也可以把依赖注入到多个对象。
@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
void inject(MainActivity mainActivity);
void inject(SecondActivity secondActivity);
}
同样注入参数的名字不重要,类型才重要。注入的类型不能是通用类型,下面这种写法是不允许的!一定要是具体的类型!
@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
void inject(Object object);
}
最后一步实现对象注入
public class AApplication extends Application {
private static AApplication sAApplication;
private static AppComponent sAppComponent;
public static AApplication getAApplication(){
return sAApplication;
}
public static AppComponent getAppComponent(){
return sAppComponent;
}
@Override
public void onCreate() {
super.onCreate();
Utils.init(this);
sAApplication = this;
sAppComponent = buildAppComponent();
}
protected AppComponent buildAppComponent(){
return DaggerAppComponent.builder().appModule(new AppModule(getApplicationContext())).build();
}
}
public class DaggerActivity extends AppCompatActivity {
@Inject
RxUtilsAbs mRxUtilsAbs;
@Inject
NetworkUtils mNetworkUtils;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AApplication.getAppComponent().inject(this);
System.out.println(mRxUtilsAbs.toString());
System.out.println();mNetworkUtils.toString();
}
}
out
System.out: com.zsy.androidtraining.dagger2.entity.RxUtils@33bcd43
System.out: com.zsy.androidtraining.dagger2.entity.NetworkUtils@dd2ffc0
从输出的结果可以看出,对象注入成功。
注入的过程图
1*ZbjJyCisnVFAgtfxjkIZQA.png可以把 AppComponent 理解成一个大的对象池,里面为你提供了所有你想获得的对象,所有实例由它管理。当然前提是你必须提供这些 实例化的方法,这就是 Module类的作用(当然@Inject 也可以提供依赖,后面会讲到)。
下面继续讲单例,这里就不跟随原作者的脚步了。一是因为作者的图上有一个小小的错误。二是因为这部分个人感觉从代码角度讲解更容易理解。
先看一下 @Singleton 的源码
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
@Scope 是一个特别重要的注解,利用这个注解可以实现我们自己的单例如:
@Scope
@Documented
@Retention(RUNTIME)
public @interface MyValidScope {}
作用完全一样!所以这里可以看出 其实名字不重要,还是那句话。
@Scope 表明的是一个有效作用域。就好比 任何变量都有一个作用域,如方法中 变量的作用域是只在这个方法中有效。方法外部是无法 获取 和 感知的。Dagger 2中同样有这一理念,Dagger 2 中同样需要“变量”有作用域。@Provides 标记的方法上 加上 @ MyValidScope 表明 在这个 我在“ MyValidScope”范围内有效。那这个“ MyValidScope”到底是什么范围呢?先看下面代码
@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@MyValidScope
public interface AppComponent {
void inject(Object object);
}
@MyValidScope 被标记在了 AppComponent 上,表明我的有效范围是在 AppComponent 中, 在这个范围内有效当然是单例。就好像一个类中的成员变量在这个类范围内当然是“单例”。
@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
void inject(Object object);
}
如果AppModule, UtilsModule, ReceiversModule 中任何 一个 方法 被 @Singleton 标记,那么上面的代码中的 @Singleton 必须要 写上。否则 实例将会为自己的作用范围而感到“困惑”,就是报错啦!你可以试一下!
下面来看一下编译成功后生成的代码(省略掉一些非必要的代码)。这个代码是加上@Singleton后生成的代码
@Singleton
public NetworkChannel provideNetworkChannel(){
return new NetworkChannel();
}
public final class DaggerAppComponent implements AppComponent {
private Provider<NetworkChannel> provideNetworkChannelProvider;
...
private DaggerAppComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideNetworkChannelProvider =
DoubleCheck.provider(
ReceiversModule_ProvideNetworkChannelFactory.create(builder.receiversModule));
...
}
...
}
我们需要重点关注一下这个 DoubleCheck.provider方法,继续看源码(去掉不必要的注释)
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();
private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider<T> 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) {
result = provider.get();
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result + ". This is likely "
+ "due to a circular dependency.");
}
instance = result;
provider = null;
}
}
}
return (T) result;
}
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
public static <T> Provider<T> provider(Provider<T> delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
return delegate;
}
return new DoubleCheck<T>(delegate);
}
public static <T> Lazy<T> lazy(Provider<T> provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy<T> lazy = (Lazy<T>) provider;
return lazy;
}
return new DoubleCheck<T>(checkNotNull(provider));
}
}
可以看到 其实现实例的方法
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result + ". This is likely "
+ "due to a circular dependency.");
}
instance = result;
provider = null;
}
}
}
第一次获取会去“创建”,并且会保存结果,之后每次获取都是返回之前创建好的值。
再来看一下没加@Singleton后的代码
加@Singleton
this.provideNetworkChannelProvider =
DoubleCheck.provider(
ReceiversModule_ProvideNetworkChannelFactory.create(builder.receiversModule));
没加@Singleton
this.provideNetworkChannelProvider =
ReceiversModule_ProvideNetworkChannelFactory.create(builder.receiversModule);
代码变成了这样,这个可以真正看出 @Singleton的作用所在!!
可能有点迷糊,后面会写一篇注入原理的笔记。暂时先理解这个作用域的概念很重要。
网友评论