Randall | 二、Dagge2

2017-08-27  本文已影响0人  mrzhqiang

一、依赖注入是什么?

曾几何时,项目中每个依赖单例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部件和模型
AppComponent接口

使用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接口有两种形式的方法:

如何抉择,当由具体需求所决定。

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
  // add inject method
  void inject(LoginActivity activity);
  // create return method
  Picasso picasso();
}

完成Dagger2部件与模型的构建后,你的项目结构应当是这样:

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设计模式。

上一篇下一篇

猜你喜欢

热点阅读