Dagger 2.11:使用@ContributesAndroidInjector时的Local Singleton

时间:2017-11-13 18:59:00

标签: java android dagger-2

我使用@ContributesAndroidInjector注释(https://google.github.io/dagger/android.html)进行了有效的Dagger设置。

        Component Application
       /                     \
Subcomponent DrawerActivity   Subcomponent SubActivity
      |
Subcomponent DrawerFragment

在SubActivity和DrawerActivity中我使用标记为@Singleton的相同存储库实例。

@Singleton
@Component(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        ActivityBuilderModule.class
})
public interface AppComponent {
    @Component.Builder
    interface Builder{
        @BindsInstance Builder application(Application application);

        AppComponent build();
    }
    void inject(App app);
}

@Module
public abstract class ActivityBuilderModule {
    @PerActivity
    @ContributesAndroidInjector(modules = {DrawerActivityModule.class, 
    FragmentBuilderModule.class})
    abstract DrawerActivity bindDrawerActivity();

    @PerActivity
    @ContributesAndroidInjector(modules = {DrawerActivityModule.class})
    abstract SubActivity bindSubActivity();
}

@Module
public abstract class FragmentBuilderModule {
    @PerFragment
    @ContributesAndroidInjector(modules = DrawerFragmentModule.class)
    abstract DrawerFragment provideDrawerFragment();
}


@Singleton
public class Repository{
    private SomeClass mSomeClass;

    @Inject
    public VehicleRepositoryImpl(SomeClass someClass) {
        mSomeClass = someClass;
    }
}


public class App extends Application implements HasActivityInjector{
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        if (BuildConfig.DEBUG) {
            Timber.plant(new Timber.DebugTree());
        }
        AppComponent component = DaggerAppComponent.builder().application(this)
                .build();
        component.inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }
}    

public class DrawerActivity extends AppCompatActivity implements HasSupportFragmentInjector{
    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

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

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

public class DrawerFragment extends Fragment {
    @Inject
    ViewModelFactory mViewModelFactory; //repository gets injected into factory

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

public class SubActivity extends AppCompatActivity{
    @Inject
    Repository mRepository;

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

我现在需要添加用户管理。这需要LoginActivity。我不希望存储库在LoginActivity中可用。所以我想为DrawerActivity / Subactivity / DrawerFragement创建一个本地单例范围“UserScope”

              Component Application
           /                          \
   @UserScope                       @LoginScope 
   Subcomponent DrawerActivity      SubComponent LoginActivity
      |                        \
Subcomponent DrawerFragment   Subcomponent SubActivity

如何使用新的@ContributesAndroidInjector注释来实现这一点?

我希望它能像在这篇博文中一样工作:http://frogermcs.github.io/building-userscope-with-dagger2/

2 个答案:

答案 0 :(得分:2)

我在这个回购中解决了我的问题:

https://github.com/ragdroid/Dahaka

非常感谢它的贡献者!

更新1 :已添加代码示例。

此图提供了代码示例的粗略概念。

                 Component Application
           /                               \
   @UserScope                          @LoginScope 
   Subcomponent UserComponent          SubComponent LoginActivity
      |                      \
Subcomponent DrawerActivity   Subcomponent SubActivity
      |
SubComponent DrawerFragment

代码示例(如果缺少某些内容,请在评论中告诉我们):

<强> 1。匕首设置

AppComponent是Dagger图的根:

@Singleton
@Component(modules = {
        AndroidSupportInjectionModule.class,
        AppModule.class,
        AppBindingModule.class 
})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
    @Component.Builder
    interface Builder{
        @BindsInstance Builder application(Application application);

        AppComponent build();
    }

    void inject(MyApp app);

    UserComponent.Builder userBuilder();

    UserManager getUserManager();
}

绑定其子组件的模块:

@Module(subcomponents = UserComponent.class)
public abstract class AppBindingModule {

    @ContributesAndroidInjector(modules = LoginModule.class)
    @LoginScope
    abstract LoginActivity loginActivity();

}    

UserComponent保存仅在用户登录时使用的类的实例。在UserModule.class中提供的所有类在其他子组件(如活动和片段组件)中以“LocalSingletons”的形式提供。

@UserScope
@Subcomponent(modules = {
        UserBindingModule.class,
        UserModule.class,
        AndroidSupportInjectionModule.class
})
public interface UserComponent extends AndroidInjector<DaggerApplication> {
    void inject(UserManager userManager);

    @Subcomponent.Builder
    interface Builder{
        UserComponent build();
    }
}

UserBindingModule定义哪些活动子组件属于UserComponent。

@Module
public abstract class UserBindingModule {
    @ContributesAndroidInjector(modules = {DrawerBindingModule.class, AndroidSupportInjectionModule.class})
    abstract DrawerActivity bindDrawerActivity();

    @ContributesAndroidInjector
    abstract SubActivity bindSubActivity();
}

DrawerBindingModule定义哪些片段子组件属于DrawerActivityComponent。

@Module
public abstract class DrawerBindingModule {
    @DrawerFragmentScope
    @ContributesAndroidInjector(modules = DrawerFragmentModule.class)
    abstract DrawerFragment provideDrawerFragment();
}

UserManager处理用户登录/注销以及所有进一步的活动注入。

@Singleton
public class UserManager implements HasActivityInjector {
    private final UserComponent.Builder userComponentBuilder;
    @Inject
    DispatchingAndroidInjector<Activity> activityInjector;

    private UserComponent userComponent;

    @Inject
    public UserManager(UserComponent.Builder builder) {
        this.userComponentBuilder = builder;
    }

    public void logIn(){
        createUserSession();
    }

    private void createUserSession() {
        userComponent = userComponentBuilder.build();
        userComponent.inject(this);
    }

    public boolean isLoggedIn() {
        return userComponent != null;
    }

    public void logOut() {
        userComponent = null;
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return activityInjector;
    }
}

<强> 2。 App + Activity + Fragment classes

public class MyApp extends Application implements HasActivityInjector{
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Inject
    UserManager mUserManager;

    @Override
    public void onCreate() {
        super.onCreate();
        if (BuildConfig.DEBUG) {
            Timber.plant(new Timber.DebugTree());
        }
        AppComponent component = DaggerAppComponent.builder().application(this)
                .build();
        component.inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return mUserManager.activityInjector();
    }
}


public class LoginActivity extends AppCompatActivity {
    Intent mOpenDrawerActivity;
    private ActivityLoginBinding binding;

    @Inject
    UserManager mUserManager;

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

    void openDrawerActivity(){
        mUserManager.logIn();
        mOpenDrawerActivity = new Intent(this, DrawerActivity.class);
        startActivity(mOpenDrawerActivity);
        finish();
    }
}


public class DrawerActivity extends BaseUserActivity implements HasSupportFragmentInjector{
    @Override
    protected void onCreate(Bundle savedInstanceState) {         
        super.onCreate(savedInstanceState);
        ...
    }        

    private void onLogout(){
        logoutUser();
    }  
}


public abstract class BaseUserActivity extends BaseActivity {
    @Inject
    UserManager userManager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!userManager.isLoggedIn()) {
            finishView();
        }
    }

    @Override
    protected void androidInject() {
        AndroidInjection.inject(this);
    }

    protected void logoutUser() {
        userManager.logOut();
        finishView();
    }    
}


public abstract class BaseActivity extends AppCompatActivity implements HasSupportFragmentInjector {
    @Inject
    DispatchingAndroidInjector<Fragment> injector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        androidInject();
        super.onCreate(savedInstanceState);
    }

    protected void androidInject() {
        AndroidInjection.inject(this);
    }

    public void finishView() {
        startActivity(new Intent(this, LoginActivity.class));
        finish();
    }

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

答案 1 :(得分:2)

  1. 什么是范围及其基础?范围基于一些具有生命周期的android对象。例如。 Application(默认范围内注释@Singleton已经可用),ActivityBroadcastReceiverFragmentServiceContentProvider (我从DaggerApplication获取的这个列表)。所以取决于它将基于什么对象,你将在这个对象的生命周期内获得可重用的依赖(它将是本地单例,例如活动/片段)
  2. 如何使我的交互器/存储库成为本地单例?首先,您应该选择具有生命周期的对象,该对象将作为我们的范围的基础。例如,让我们选择里面有两个子片段的片段。我想在这两个子片段中使用相同的CommonInteractor实例。 我们应该为父片段创建独立的子组件。我们可以使用@ContributesAndroidInjector生成@Subcomponent。这个子组件应该是作用域的,这就是我们在其上放置@FragmentScope(由我们自己创建注释)的原因。两个子片段将属于此子组件,这就是我们为子片段创建独立模块并将其添加到生成的子组件中的原因,方法是将父级片段子组件的modules添加到@ContributesAndroidInjector
  3. @Module(includes = [AndroidInjectionModule::class])
    abstract class AppBindingModule {
        //there are a lot of other android stuff binding here
        //activities, fragments, etc.

    @FragmentScope @ContributesAndroidInjector(modules = [ParentFragmentModule::class]) abstract fun bindParentFragment(): ParentFragment } @Module abstract class ParentFragmentModule { //we should not annotate this by any scope annotation @ContributesAndroidInjector abstract fun bindFirstChildFragment(): FirstChildFragment @ContributesAndroidInjector abstract fun bindSecondChildFragment(): SecondChildFragment } @FragmentScope class CommonInteractor @Inject constructor() { //we can inject this interactor into presenters of those two child fragments //it will be the same instance for both presenters } class FirstChildPresenter @Inject constructor( private val commonInteractor: CommonInteractor ) : Presenter<FirstView>() class SecondChildPresenter @Inject constructor( private val commonInteractor: CommonInteractor ) : Presenter<SecondView>() class FirstChildFragment: Fragment(), FirstView { @Inject lateinit var firstChildPresenter: FirstChildPresenter } class SecondChildFragment: Fragment(), SecondView { @Inject lateinit var secondChildPresenter: SecondChildPresenter }

    我省略了一些细节,例如描述AppComponent并将AppBindingModule添加到其中,并展示了如何使用AndroidSupportInjection.inject(this)注入它,因为它不在主题中。但如果需要澄清,请随时在评论中提问。