Skip to content

Latest commit

 

History

History
443 lines (337 loc) · 17.9 KB

7.Dagger2之dagger-android(七).md

File metadata and controls

443 lines (337 loc) · 17.9 KB

Dagger2之dagger-android(七)

> Android Gradle
> // Add Dagger dependencies
> dependencies {
>   compile 'com.google.dagger:dagger:2.x'
>   annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
> }
> If you're using classes in dagger.android you'll also want to include:

> compile 'com.google.dagger:dagger-android:2.x'
> compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
> annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'

Dagger2的官网上说如果你要使用dagger.android里面的东西,就需要添加dagger-android相关的依赖。那这里面都提供了些什么呢?

Android应用使用Dagger最主要的困难就是一些Framework类(如ActivityFragment)是由操作系统实例化的,而Dagger更好工作的前提是它可以构建所有的注入对象。所以,你只能(不得不)在生命周期方法中进行成员变量注入,这就意味着一些类可能会写成这个样子:

public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // DO THIS FIRST. Otherwise frombulator might be null!
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
    // ... now you can write the exciting code
  }
}

这样的确可以实现Android的依赖注入,但还有两个问题需要我们去面对:

  • 类似的复制性的代码以后会很难维护。而且随着越来越多的开发者去复制粘贴这些代码,很少会有人知道它的作用。
  • 更重要的是,它要求注射类型(FrombulationActivity)知道其注射器。 即使这是通过接口而不是具体类型完成的,它打破了依赖注入的核心原则:一个类不应该知道如何实现依赖注入。

于是dagger.android诞生了...dagger.android中的类提供了一种简洁化的实现方式。

注入Activity对象

  • 1.在应用的ApplicationComponent中注入AndroidInjectModule,来确保可以绑定Android的基础组件。一般Android应用都会提供一个

ApplicationComponent,其他的Component依赖该ApplicationComponent即可。
java @Module public class AppModule { @Provides LogUtil provideLogUtil() { return new LogUtil(); } } @Singleton // 必须要singleton @Component(modules = AndroidInjectionModule.class, AppModule.class) public interface AppComponent { void inject(AppApplication application); }

  • 2.通过@Subcomponent声明一个实现AndroidInjector<YourActivity>的接口,并且提供一个继承AndroidInjector.Builder<YourActivity>@Subcomponent.Builder

     @Subcomponent(modules = AndroidInjectionModule.class)
     public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
         @Subcomponent.Builder
         abstract class Builder extends AndroidInjector.Builder<MainActivity> {
         }
     }
  • 3.定义完上面的Subcomponent后,要通过module将该Module注入到Applicationmodules列表中

     @Module(subcomponents = MainActivitySubcomponent.class)
     abstract class MainActivityModule {
         @Binds
         @IntoMap
         @ActivityKey(MainActivity.class)
         abstract AndroidInjector.Factory<? extends Activity> bindYourActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
         @Singleton // 必须用signelton,不然报错错误: A @Module may not contain both non-static @Provides methods and abstract @Binds or @Multibinds declarations
         @Provides
         static MainActivityPresenter providePresenter() {
             return new MainActivityPresenter();
         }
     }
  • 4.将刚才定义的YourActivityModule.class添加到ApplicationComponent

     @Singleton // 必须要singleton
     @Component(modules = {AndroidInjectionModule.class, AppModule.class, MainActivityModule.class})
     public interface AppComponent {
         void inject(AppApplication application);
     }
  • 5.自定义Application实现HasActivityInjector并且通过@Inject注入一个activityInjector()方法返回的DispathcingAndroidInJector<Activity>实例

     public class AppApplication extends Application implements HasActivityInjector {
         @Inject
         DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
    
         @Override
         public void onCreate() {
             super.onCreate();
             DaggerAppComponent.builder().build().inject(this);
         }
    
         @Override
         public AndroidInjector<Activity> activityInjector() {
             return dispatchingActivityInjector;
         }
     }
  • 6.最后,在Activity.onCreate()方法中在super.onCreate()之前调用AndroidInjection.inject(this)

     public class MainActivity extends AppCompatActivity {
         @Inject
         LogUtil mLog;
         @Inject
         MainActivityPresenter mPresenter;
    
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             AndroidInjection.inject(this);
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
             mLog.d(mPresenter.toString());
         }
     }

    执行结果:04-23 19:46:38.443 17096-17096/? E/@@@: com.charon.daggerandroiddemo.MainActivityPresenter@e480d98

但是它是怎么实现的呢? AndroidInjection.inject()会从你的Application中拿到一个DispatchingAndroidInjector<Activity>并通过inject(Activity)Activity传了过去。这个DispatchingAndroidInjector会查找一个你的Activity类的AndroidInjector.Factory(YourActivitySubcomponent.Builder),构建AndroidInjector(YourActivitySubcomponent),然后把你的Activity传给inject(YourActivity)

注入Fragment实例

注入Fragment实例和注入Activity类似。同样的去声明subcomponent,不同的是:

  • 需要将Activity类型参数替换成Fragment
  • @ActivityKey替换成@FragmentKey
  • HasActivityInjector替换成HasFragmentInjector
  • 和在Activity.onCreate()方法执行注入不同的Fragment要通过onAttach()方法注入。
  • FragmentModule的添加位置,与Activity定义的Module添加不同,其取决于Fragment内部所需要的其他绑定的依赖注入;

Unlike the modules defined for Activitys, you have a choice of where to install modules for Fragments. You can make your Fragment component a subcomponent of another Fragment component, an Activity component, or the Application component — it all depends on which other bindings your Fragment requires. After deciding on the component location, make the corresponding type implement HasFragmentInjector. For example, if your Fragment needs bindings from YourActivitySubcomponent, your code will look something like this:

public class YourActivity extends Activity
    implements HasFragmentInjector {
  @Inject DispatchingAndroidInjector<Fragment> fragmentInjector;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
    // ...
  }

  @Override
  public AndroidInjector<Fragment> fragmentInjector() {
    return fragmentInjector;
  }
}

public class YourFragment extends Fragment {
  @Inject SomeDependency someDep;

  @Override
  public void onAttach(Activity activity) {
    AndroidInjection.inject(this);
    super.onAttach(activity);
    // ...
  }
}

@Subcomponent(modules = ...)
public interface YourFragmentSubcomponent extends AndroidInjector<YourFragment> {
  @Subcomponent.Builder
  public abstract class Builder extends AndroidInjector.Builder<YourFragment> {}
}

@Module(subcomponents = YourFragmentSubcomponent.class)
abstract class YourFragmentModule {
  @Binds
  @IntoMap
  @FragmentKey(YourFragment.class)
  abstract AndroidInjector.Factory<? extends Fragment>
      bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Builder builder);
}

@Subcomponent(modules = { YourFragmentModule.class, ... }
public interface YourActivityOrYourApplicationComponent { ... }

上面主要用到了这几个类:

  • AndroidInjection:注入Android核心库的基本类型的实例
  • AndroidInjector<T>:注入Android库的类型的接口,TAndroid库的基本类型T,比如ActivityFragmentBroadcastReceive等;
  • AndroidInjector.Factory<T>:AndroidInjector<T>的工厂类接口
  • DispatchingAndroidInjector<T>:其为AndroidInjector<T>接口的实现类,将Android核心库的的基本类型T的实例注入Dagger,该操作是由Android核心库的类的实例本身执行,而不是Dagger

卧草,好麻烦。activity搞一遍、fragment搞一遍、service搞一遍....

弄到这里我已经迷糊了,在dagger.android这里我废了老半天劲,中间一度不想看了...

  • 每个Activity都要写一个YourActivityModule,并且添加到AppComponent中:

     @Singleton // 必须要singleton
     @Component(modules = {AndroidInjectionModule.class, AppModule.class, MainActivityModule.class})
     public interface AppComponent {
         void inject(AppApplication application);
     }

    那么多Activity都添加进来,这是简洁个锤子啊

  • 每个YourActivityModule还要有一个YourActivitySubComponent文件,同事还需要建立多个SubComponent

     @Module(subcomponents = MainActivitySubcomponent.class)
     abstract class MainActivityModule {

官网中有这样一段话:

Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use @ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with @ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well.

也就是如果您的subcomponent及其构建器没有第2步中提到的其他方法或超类型,您可以使用@ContributesAndroidInjector为您生成它们。我们就不需要步骤2和3,取而代之的是添加一个抽象模块方法,该方法返回您的activity,使用@ContributesAndroidInjector对其进行注解,并指定要安装到子组件中的模块。 如果子组件需要scopes,则也可以用@scopes注解到该方法。

@ActivityScope
@ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourActivityInjector();

我们可以封装一下:

  • 提供BaseActivity在其onCreate()方法前调用AndroidInjection.inject(this);
  • 提供BaseActivityComponent
  • 然后将所有的ActivityModuleAllActivityModule统一管理

dagger当然不会让你这么费劲,他们提供了更简单的实现

基本Framework

由于DispatchingAndroidInjector会在运行时查找合适的AndroidInjector,基类需要像调用AndroidInjection.inject()一样实现HasDispatchingActivityInjector/HasDispatchingFragmentInjector,子类需要做的就是绑定一个对应的@Subcomponent。如果你的类层次不是很复杂的话Dagger会提供一些基类(如[DaggerActivity][DaggerFragment])。同时Dagger也会提供[DaggerApplication],你需要做的就是继承它并覆写applicationInjector()方法以返回应该注入Application的component

还有下面的类型:

  • DaggerService and DaggerIntentService
  • DaggerBroadcastReceiver
  • DaggerContentProvider

注意:[DaggerBroadcastReceiver]只适用于在AndroidManifest.xml文件中静态注册的BroadcastReceiver,如果你在代码中手动创建注册BroadcastReceiver,最好采用构造器注入。

就是dagger提供给了DaggerActivityDaggerFragmentDaggerAppliation等类,你可以直接去继承该类,实现对应的方法就可以了,不用按照上面一步一步的。

那究竟是怎么用呢? 直接上代码了:

  • 首先写好ApplicationModule类:
@Module
public class ApplicationModule {
    @Provides
    LogUtil provideLogUtil() {
        return new LogUtil();
    }
}
  • 定义好MainActivityModule:
@Module
public class MainActivityModule {
    MainPresenter provideMainPresenter() {
        return "main activity module";
    }
}
  • 把所有的ActivityModule都放到一个统一的管理类中(为什么必须要有这个类去统一声明对应的Module,后面讲),这里叫ActivityBuilder不太合适,应该叫ActivityBindingModule更切合一些:
@Module
public abstract class ActivityBuilder {
    @ContributesAndroidInjector(modules = MainActivityModule.class)
    abstract MainActivity bindMainActivity();
    // @ContributesAndroidInjector(modules = Main2ActivityModule.class)
    // abstract Main2Activity bindMain2Activity();
    // ...
    // 以后再写别的Activity只要将其对应的Module添加到这里就好了
}
  • AndroidInjectionModuleApplicationModuleActivityBuilder都注册到AppComponent中(这三是必须的),然后提供Builder
@Component(modules = {AndroidInjectionModule.class, ApplicationModule.class, ActivityBuilder.class})
public interface AppComponent extends AndroidInjector<AppApplication> {
    @Component.Builder // 这个是什么鬼? 为什么要声明它,下面讲
    interface Builder {

        @BindsInstance
        Builder application(AppApplication application);

        AppComponent build();
    }

    void inject(AppApplication application);  
}
  • 自定义Application类继承DaggerApplication
public class AppApplication extends DaggerApplication {

    @Override
    public void onCreate() {
        super.onCreate();
    }


    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
        return appComponent;
    }
}

继承后要实现applicationInjector()方法,也就是制定哪个是应用的injector(the main component AppComponent). 这就是为什么要让AppComponent继承AndroidInjector <DaggerApplication>的原因。

  • Activity继承DaggerActivity,并且调用方法.
public class MainActivity extends DaggerActivity {
    @Inject
    LogUtil mLog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	// 看到没,这里不用写inject了
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLog.d("@@@");
    }
}

好了,走你:

04-23 21:19:29.591 21395-21395/? E/@@@: @@@

卧草,方便了没有,好像还可以啊

别忘了上面我们还遗留了两个问题:

  • 为什么要声明ActivityBuilder并且在里面把Module列出,并声明ContributesAndroidInjector
  • 为什么要通过@Component.Builder声明Builder接口

第一个问题:

ActivityBuilder : We created this module. This is a given module to dagger. We map all our activities here. And Dagger know our activities in compile time. In our app we have Main and Detail activity. So we map both activities here.

这个问题其实我们在前面讲过。 在讲DaggerActivityDaggerFragment之前。

第二个问题:

@Component.Builder: We might want to bind some instance to Component. In this case we can create an interface with @Component.Builder annotation and add whatever method we want to add to builder. In my case I wanted to add Application to my AppComponent.

Note: If you want to create a Builder for your Component, your Builder interface has to has a build(); method which returns your Component.

说白了就是讲ApplicationAppComponent绑定。

public class AppApplication extends DaggerApplication {

    @Override
    public void onCreate() {
        super.onCreate();
    }


    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
        return appComponent;
    }
}

support包的支持

像使用Android support library一样,dagger.android.support包下面也会提供相应的类。但要注意,当support Fragment用户需要绑定AndroidInjector.Factory<? extends android.support.v4.app.Fragment>时,AppCompat用户应该继续实现AndroidInjector.Factory<? extends Activity>而不是<? extends AppCompatActivity>或<? extends FragmentActivity>

何时注入

要尽可能的采用构造器注入,因为javac将确保被set之前没有字段被引用,这有利于避免空指针异常。但如果你一定要注入成员变量,最好尽早进行注入(越早越好)。也正是因为这样,DaggerActivity才要在onCreate()方法中立刻调用AndroidInjection.inject()再调用super.onCreate()DaggerFragment的onAttach()也是一样,也是为了防止Fragment重新attach产生的矛盾。

下一篇:Dagger2与MVP(八)