简体   繁体   English

Dagger 2.11:使用@ContributesAndroidInjector时的Local Singleton

[英]Dagger 2.11: Local Singleton when using @ContributesAndroidInjector

I have a working Dagger setup using the @ContributesAndroidInjector annotation ( https://google.github.io/dagger/android.html ). 我使用@ContributesAndroidInjector注释( https://google.github.io/dagger/android.html )进行了有效的Dagger设置。

        Component Application
       /                     \
Subcomponent DrawerActivity   Subcomponent SubActivity
      |
Subcomponent DrawerFragment

In SubActivity and DrawerActivity I am using the same repository instance which is marked as @Singleton. 在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);
    }
}

I have now the need to add a user management. 我现在需要添加用户管理。 This requires a LoginActivity. 这需要LoginActivity。 I don't want the repository to be avaiblable in LoginActivity. 我不希望存储库在LoginActivity中可用。 So I'd like to create a local singleton scope "UserScope" for DrawerActivity/Subactivity/DrawerFragement 所以我想为DrawerActivity / Subactivity / DrawerFragement创建一个本地单例范围“UserScope”

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

How can I achieve this still using the new @ContributesAndroidInjector annotation? 如何使用新的@ContributesAndroidInjector注释实现此目的?

I want it to work like in this blogpost: http://frogermcs.github.io/building-userscope-with-dagger2/ 我希望它像在这篇博文中一样工作: http//frogermcs.github.io/building-userscope-with-dagger2/

I solved my problem by doing it like in this repo: 我在这个回购中解决了我的问题:

https://github.com/ragdroid/Dahaka https://github.com/ragdroid/Dahaka

Many thanks to its contributor! 非常感谢它的贡献者!

Update 1 : Code example added. 更新1 :添加了代码示例。

This graph gives a rough idea of the code example. 该图提供了代码示例的粗略概念。

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

Code Example (If somethings missing please let me know in the comments): 代码示例 (如果缺少某些内容请在评论中告诉我):

1. Dagger Setup 1.匕首设置

AppComponent is the root of Dagger graph: 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 which binds its SubComponents: 绑定其子组件的模块:

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

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

}    

UserComponent holds instances of classes that are used only when user is logged in. All classes provided in UserModule.class are available as "LocalSingletons" in further subcomponents like activities and fragment components. 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 defines which activity-subcomponents belong to UserComponent. UserBindingModule定义哪些活动子组件属于UserComponent。

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

    @ContributesAndroidInjector
    abstract SubActivity bindSubActivity();
}

DrawerBindingModule defines which fragment-subcomponents belong to DrawerActivityComponent. DrawerBindingModule定义哪些片段子组件属于DrawerActivityComponent。

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

The UserManager handles user login/logout and all further activity injections. 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 2. App + Activity + Fragment类

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. What is a scope and what it based on? 什么是范围及其基础? The scope is based on some android object with it's lifecycle. 范围基于一些具有生命周期的android对象。 Eg Application (there are default scope with annotation @Singleton already available), Activity , BroadcastReceiver , Fragment , Service , ContentProvider (this list I took from DaggerApplication ). 例如Application (默认范围内注释@Singleton已经可用), ActivityBroadcastReceiverFragmentServiceContentProvider (我从DaggerApplication这个列表)。 So depends on what object it will be based you'll get reusable dependency inside lifecycle of this object (it will be local singleton for it, eg for activity/fragment) 因此,取决于它将基于什么对象,您将在此对象的生命周期内获得可重用的依赖项(它将是本地单例,例如对于activity / fragment)
  2. How to make my interactor/repository to be local singleton? 如何使我的交互器/存储库成为本地单例? First of all you should choose the object with lifecycle that will be base for our scope. 首先,您应该选择具有生命周期的对象,该对象将作为我们的范围的基础。 For instance lets choose fragment with two child fragments inside it. 例如,让我们选择里面有两个子片段的片段。 And I want to use the same instance of CommonInteractor inside of those two child fragments. 我想在这两个子片段中使用相同的CommonInteractor实例。 We should create standalone Subcomponent for parent fragment. 我们应该为父片段创建独立的子组件。 We can use @ContributesAndroidInjector for generating @Subcomponent . 我们可以使用@ContributesAndroidInjector来生成@Subcomponent This subcomponent should be scoped so that's why we put @FragmentScope (created annotation by ourselves) on it. 这个子组件应该是作用域的,这就是为什么我们把@FragmentScope (我们自己创建了注释)放在它上面。 Two child fragments will belong to this subcomponent and that is the reason why we creating standalone module for child fragments and add it into generated Subcomponent by adding argument modules to @ContributesAndroidInjector of parent fragment subcomponent. 两个子片段将属于此子组件,这就是我们为子片段创建独立模块并将其添加到生成的子组件中的原因,方法是将参数modules添加到父片段子组件的@ContributesAndroidInjector中。

@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 }

I omit some details like describing AppComponent and adding AppBindingModule into it and showing how to inject with AndroidSupportInjection.inject(this) cause it is out of the topic. 我省略了一些细节,比如描述AppComponent并将AppBindingModule添加到其中,并展示如何使用AndroidSupportInjection.inject(this)注入它,因为它不在主题中。 But if clarification is necessary feel free to ask questions in comment. 但如果需要澄清,请随意在评论中提问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM