匕首2:从组件或子组件注入类

时间:2018-07-10 10:27:11

标签: android dependency-injection dagger-2

我的应用程序中有两个不同的匕首组件:MainComponentUserComponentUserComponentMainComponent的子组件,@UserScope仅在用户登录后才存在。

当我需要注入仅在用户登录或注销时才存在的类时,它可以正常工作。但是有两种情况根本无法奏效:

当用户登录时某些类可以存在,也可以不存在。例如,这可以是一个片段,其中显示商品列表和可选的“购买”按钮,这些按钮仅在用户登录后才可见。或者推送通知服务-推送通知可能会到达,无论用户是否登录,您都可以需要针对不同的用户以不同的方式处理它们。 在这种情况下,类在初始化期间必须注入不同的组件。

也有一些类的寿命超过@UserScope。例如,带有侧面菜单的活动。侧边菜单在顶部显示用户头像和名称,但在底部也包含“退出”按钮。当用户按下退出键时,活动应更改菜单标题。 在这种情况下,必须在创建类后将类重新注入。

如何处理这种情况? 想到的唯一可能的解决方案是在用户注销后用空用户创建UserComponent,然后从模块中所有@Provides方法中返回空值,但这是很多工作。此外,我希望UserComponent在用户注销时为空,而不是使用已失效的版本。

我真的很喜欢这样的东西:

if (Injector.userComponent() != null) {
    Injector.userComponent().inject(this);
} else {
    Injector.mainComponent().inject(this);
}

但是此代码给出错误:IUser cannot be provided without an @Provides-annotated method.,因为MainComponent不提供IUser。但这确实不应该。

没有办法告诉匕首只留下无法找到空值的字段吗?

以下是我当前的实现方式:

@Singleton
@Component(modules = {
        ApplicationModule.class,
        AuthorizationModule.class
})
public interface MainComponent {

    void inject(MyClass item);

    ...

    UserComponent.Builder userComponentBuilder();

}

@Scope
public @interface UserScope {

}

@UserScope
@Subcomponent(modules = {
        UserModule.class
})
public interface UserComponent {

    void inject(MyClass item);

    ...

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

}

@Module
public class UserModule {

    private User mUser;

    public UserModule(User user) {
        mUser = user;
    }

    @Provides
    @UserScope
    IUser user() {
        return mUser;
    }

}

public class Injector {

    private static MainComponent sMainComponent;
    private static UserComponent sUserComponent;

    public static void createMainComponent(MainApplication application) {
        sMainComponent = DaggerMainComponent.builder()
                .applicationModule(new ApplicationModule(application))
                .build();
    }

    @NonNull
    public static MainComponent mainComponent() {
        return sMainComponent;
    }

    public static void createUserComponent(User user) {
        if (user == null) {
            throw new NullPointerException("Cannot create user component without a user");
        }

        sUserComponent = sMainComponent.userComponentBuilder()
                .userModule(new UserModule(user))
                .build();
    }

    public static void destroyUserComponent() {
        sUserComponent = null;
    }

    @Nullable
    public static UserComponent userComponent() {
        return sUserComponent;
    }

}   

public class MyClass {

    // Provided by MainComponent
    @Inject
    IAuthorizationManager mAuthorizationManager;
    // Provided by UserComponent
    @Inject
    IUser mUser;

    public MyClass() {

        // Works, but not if userComponent is currently null (user is not logged in):
        Injector.userComponent().inject(this);

        // Doesn't work, but I'd prefer something like that instead of creating invalid UserComponent:
        if (Injector.userComponent() != null) {
            Injector.userComponent().inject(this);
        } else {
            Injector.mainComponent().inject(this);
        }

    }

}

1 个答案:

答案 0 :(得分:0)

最后,我决定只让UserModule提供的依赖项为Nullable,同时使UserComponent为NonNull。

这意味着在使用UserModule的依赖项的任何地方,都必须将其声明为Nullable。这通常会引起误解,因为如果类是由UserModule创建的,并且仅在用户存在时才存在,则其内部的依赖项肯定是NonNull,但是您不能强制这样做。

如果有人感兴趣,请附加修改后的示例代码。

@Singleton
@Component(modules = {
        ApplicationModule.class,
        AuthorizationModule.class
})
public interface MainComponent {

    void inject(MyClass item);

    ...

    UserComponent.Builder userComponentBuilder();

}

@Scope
public @interface UserScope {

}

@UserScope
@Subcomponent(modules = {
        UserModule.class
})
public interface UserComponent {

    void inject(MyClass item);

    ...

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

}

@Module
public class UserModule {

    @Nullable
    private User mUser;

    public UserModule(@Nullable User user) {
        mUser = user;
    }

    @Provides
    @Nullable
    @UserScope
    IUser user() {
        return mUser;
    }

}

public class Injector {

    private static MainComponent sMainComponent;
    private static UserComponent sUserComponent;

    public static void createMainComponent(MainApplication application) {
        sMainComponent = DaggerMainComponent.builder()
                .applicationModule(new ApplicationModule(application))
                .build();
    }

    @NonNull
    public static MainComponent mainComponent() {
        return sMainComponent;
    }

    public static void createUserComponent(@Nullable User user) {
        sUserComponent = sMainComponent.userComponentBuilder()
                .userModule(new UserModule(user))
                .build();
    }

    @NonNull
    public static UserComponent userComponent() {
        return sUserComponent;
    }

}   

public class MyClass {

    @Inject
    IAuthorizationManager mAuthorizationManager;

    @Inject
    @Nullable
    IUser mUser;

    public MyClass() {
        Injector.userComponent().inject(this);
    }

}