美文网首页
Randall | 二、Dagge2

Randall | 二、Dagge2

作者: mrzhqiang | 来源:发表于2017-08-27 19:40 被阅读0次

    一、依赖注入是什么?

    曾几何时,项目中每个依赖单例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接口有两种形式的方法:

    • 当需要注入的依赖很多时,可以创建inject方法,传入需要被注入依赖的对象实例;
    • 当仅需要一个全局单例时,可以创建返回对应实例的方法。

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

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

    相关文章

      网友评论

          本文标题:Randall | 二、Dagge2

          本文链接:https://www.haomeiwen.com/subject/vjtidxtx.html