一、依赖注入是什么?
曾几何时,项目中每个依赖单例Manager的地方,getInstance()方法是必不可少的。
public class AccountManager {
private static class Holder {
private static final AccountManager instance = new AccountManager();
}
public static AccountManager getInstance() {
return Holder.instance;
}
private AccountManager() {
// Other instance
}
...
}
每次像这样写一遍真是挺烦人的,如果内部再有一些依赖的话,会变得更加扑朔迷离。
public class App extend Application {
private AccountManager mAccount;
private NetworkManager mNet;
private DatabaseManager mDb;
...
@Override
public void onCreate() {
super.onCreate();
mAccount = AccountManager.getInstance();
mNet= NetworkManager.getInstance(this, mAccount);
mDb= DatabaseManager.getInstance(this, "app.db", 1);
}
...
}
这只是简单的例子,遇到大型项目的时候,需要初始化的全局实例更多,并且显得更加复杂。
那么为了解决依赖相关的问题,减少每个项目开启时重复的样板代码构建,Dagger2就派上了用场。
有一个用来解释【依赖注入是什么】的最佳案例就是:ButterKnife
它是这样用的:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.button1) View button1;
@BindView(R.id.text1) View text1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
声明依赖的组件,通过注解确定id,使用bind方法传入this,完成依赖注入。
这就好像你告诉一个服务商,你需要什么,然后给出收货地址,而服务商则生产出来,逐一寄到提供的地址。
Dagger2的用法,也是上述的形式,唯一例外的是,你将同时扮演消费者和生产商。
这里不考虑复杂的情况,从入门的角度来看,对于使用Dagger2实现依赖注入,很有必要。
二、怎么使用Dagger2?
Dagger2是谷歌forked from square/dagger的一个分支,谷歌Dagger2开源框架的介绍是:
A fast dependency injector for Android and Java.
对于其与square的历史渊源,github上框架介绍已经写得非常清楚
——翻译过来,大概是:
- 消除所有反射,提升运行时性能
- 编译时处理,更快更好的构建速度
其实我是从square官网上发现的Dagger,然后在这个框架的github上看到介绍说,项目已经标记为不再维护,推荐使用Dagger2;而另一方面,网上找到的关于Dagger的资料,要么无法解释清楚为什么要用,要么就是用起来特别繁琐(还不如getInstance简单粗暴)。
因此转向了Dagger2,并且在很长的一段困惑期中,苦苦挣扎:
要用依赖注入吗?真的要用吗?为什么要用呢?用了有什么好处呀?……
后来干脆自己建立demo,一步步把玩,其他资料都不再作为参考,只留下官方sample作为注解的学习。迈出这一步之后,才终于发现Dagger2的神奇和便利。
1.打开你项目下的gradle文件,添加Dagger2项目的依赖管理
// 依赖注入框架
compile 'com.google.dagger:dagger:2.10'
annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
PS:这个版本并非最新版,有需要的话,可以去官网依赖最新的版本,这里为了稳定性,将不做升级。
2.继承Application创建RandallApp,然后开始构建Dagger2部件和模型
使用javax的注解@Singleton标记为单例,即所实现的类只存在一个实例:
import javax.inject.Singleton;
@Singleton
public interface AppComponent {
// add inject method
}
创建AppModule并依赖Application实例:
public final class AppModule {
private final Application application;
public AppModule(Application application) {
this.application = application;
}
@Provides @Singleton Application provideApplication() {
return application;
}
}
创建AndroidModule,因为其他框架还没有添加依赖,所以这里用Android SDK中的SystemService举例:
@Module
public final class AndroidModule {
@Provides @Singleton AudioManager provideAudioManager(Application application) {
return (AudioManager) application.getSystemService(Context.AUDIO_SERVICE);
}
@Provides @Singleton SensorManager provideSensorManager(Application application) {
return (SensorManager) application.getSystemService(Context.SENSOR_SERVICE);
}
@Provides @Singleton Sensor provideSensorAccelerometer(SensorManager sensorManager) {
return sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
@Provides @Singleton ConnectivityManager provideConnectivityManager(Application application) {
return (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
}
}
简单说明一下AndroidModule和AppModule的关系:
首先,AndroidModule是细节模型,事实上全部在AppModule中提供实例也没有关系,但为了明确功能和类型,所以就有了AndroidModule。
可以理解为,AndroidModule就是AppModule的分身、子模型,只要通过这样的语法就能导入:
@Module(includes = {
AndroidModule.class,
})
public final class AppModule {
...
}
@Module和@Provides都是Dagger2的注解。前者用于类注解,标记这个类是一个模型;后者则用于方法注解,标记返回的实例可以提供依赖。
这两个注解,可以让“服务商”知道自己有哪些物品可以生产:
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
// add inject method
}
AppComponent接口有两种形式的方法:
- 当需要注入的依赖很多时,可以创建inject方法,传入需要被注入依赖的对象实例;
- 当仅需要一个全局单例时,可以创建返回对应实例的方法。
如何抉择,当由具体需求所决定。
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
// add inject method
void inject(LoginActivity activity);
// create return method
Picasso picasso();
}
完成Dagger2部件与模型的构建后,你的项目结构应当是这样:
随后,你应该make一下工程,使得Dagger2编译出你所需要的接口实现类。
make完成后,没有错误的话,你可以在RandallApp中,重写onCreate方法,然后输入Dagger...就会发现已经有了DaggerAppComponent这个编译生成的类。
public class RandallApp extends Application {
@Override public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().build();
}
}
可以看看DaggerAppComponent的一些细节,其中可能存在一些困惑:
public final class DaggerAppComponent implements AppComponent {
private DaggerAppComponent(Builder builder) {
assert builder != null;
}
public static Builder builder() {
return new Builder();
}
public static AppComponent create() {
return new Builder().build();
}
public static final class Builder {
private Builder() {}
public AppComponent build() {
return new DaggerAppComponent(this);
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This
* method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder appModule(AppModule appModule) {
Preconditions.checkNotNull(appModule);
return this;
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This
* method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder androidModule(AndroidModule androidModule) {
Preconditions.checkNotNull(androidModule);
return this;
}
}
}
为什么Module都被“过时”了呢?因为当前工程中,没有任何地方发出依赖需求。
提供全局的部件实例
public class RandallApp extends Application {
private static AppComponent appcomponent;
@Override public void onCreate() {
super.onCreate();
appcomponent = DaggerAppComponent.builder().build();
}
public static AppComponent appComponent() {
return appcomponent;
}
}
使用部件实例注入实例
public class LoginActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {
...
// dependency injection
@Inject ConnectivityManager cm;
@Inject AudioManager am;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
RandallApp.appComponent().inject(this);
...
}
}
再make一下,此时所有依赖已经成功注入
public final class DaggerAppComponent implements AppComponent {
private Provider<Application> provideApplicationProvider;
private Provider<ConnectivityManager> provideConnectivityManagerProvider;
private Provider<AudioManager> provideAudioManagerProvider;
private MembersInjector<LoginActivity> loginActivityMembersInjector;
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.provideApplicationProvider =
DoubleCheck.provider(AppModule_ProvideApplicationFactory.create(builder.appModule));
this.provideConnectivityManagerProvider =
DoubleCheck.provider(
AndroidModule_ProvideConnectivityManagerFactory.create(
builder.androidModule, provideApplicationProvider));
this.provideAudioManagerProvider =
DoubleCheck.provider(
AndroidModule_ProvideAudioManagerFactory.create(
builder.androidModule, provideApplicationProvider));
this.loginActivityMembersInjector =
LoginActivity_MembersInjector.create(
provideConnectivityManagerProvider, provideAudioManagerProvider);
}
@Override
public void inject(LoginActivity loginActivity) {
loginActivityMembersInjector.injectMembers(loginActivity);
}
public static final class Builder {
private AppModule appModule;
private AndroidModule androidModule;
private Builder() {}
public AppComponent build() {
if (appModule == null) {
throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
}
if (androidModule == null) {
this.androidModule = new AndroidModule();
}
return new DaggerAppComponent(this);
}
public Builder appModule(AppModule appModule) {
this.appModule = Preconditions.checkNotNull(appModule);
return this;
}
public Builder androidModule(AndroidModule androidModule) {
this.androidModule = Preconditions.checkNotNull(androidModule);
return this;
}
}
}
看起来似乎有一个问题,当AppModule是null的时候,会抛出一个异常。原因在于,AppModule是需要Application实例去创建,但是Application是Android在应用打开时才被创建,因此需要在Applicaion的onCreate方法中,构建AppComponent时加入一个AppModule实例。
public class RandallApp extends Application {
private static AppComponent appcomponent;
@Override public void onCreate() {
super.onCreate();
appcomponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
public static AppComponent appComponent() {
return appcomponent;
}
}
AndroidModule已经自动new出来实例,无需多费功夫。
三、总结
这样就完成了整个Dagger2的构建工作,以后再有其他框架的类实例需要被依赖,只要建立对应的Module类,使用provides标记的方法提供对应的类实例,并包括在AppModule中,然后通过AppComponent添加inject方法注入需求类实例即可。
再说一点,维护期间,如果想改变框架,或者删除框架,只需要在AppModule中注释导入的对应Module,然后在需求类实例中,将Inject的依赖注释即可。
Dagger2的基本使用就到这里,后面开始建立基于DataBinding框架的MVVM设计模式。