Dagger 2重新初始化单例

11

我正在尝试使用Dagger 2将我的用户对象存储为单例。

    @Provides
    @Named("me")
    @Singleton
    User provideUser(PrefsUtil prefsUtil, UserDao userDao) {
        int id = prefsUtil.getFromPrefs("me", 0);
        if (id == 0){
            return new User();
        }
        try {
            return userDao.queryForId(id);
        } catch (SQLException e) {
            return new User();
        }
    }

它运行良好并使用User对象注入我的类。

然而,在登录并从服务器获取用户并将其存储在上述方法查询的位置之后,它不会生效,因为它是单例。它将向我提供空用户对象。为了使它生效,您必须退出应用程序并重新打开它...

问题是如何在实际数据更改后更新/重新初始化使用@Name("me")注释的用户对象,以便它将当前用户对象注入我的其他类?


你需要重置你的模块并重新初始化它。 - Rod_Algonquin
2个回答

10

我不会直接回答你的问题,但是会给你一个建议,告诉你如何正确实现你所需要的功能。

你基本上是在试图实现某种UserManager的功能。但是,你没有将这个逻辑封装在一个专门的类中,而是试图将用户管理责任委托给DI框架。

这是滥用DI框架的行为,也是一条非常懒惰的路。

你所需要的只是这个:

@Provides
@Singleton
UserManager provideUserManager(PrefsUtil prefsUtil, UserDao userDao) {
    return new UserManager(prefUtils, userDao);
}

同时在UserManager中公开所需的功能:

public class UserManager {

    private final PrefsUtil mPrefsUtil;
    private final UserDao mUserDao;

    public UserManager(PrefsUtil prefsUtil, UserDao userDao) {
        mPrefsUtil = prefsUtil;
        mUserDao = userDao;
    }

    public User getCurrentUser() {
        int id = mPrefsUtil.getFromPrefs("me", 0);
        if (id == 0){
            return new User();
        }
        try {
            return mUserDao.queryForId(id);
        } catch (SQLException e) {
            return new User();
        }
    }
}
你可以查看答案,以获取有关DI框架滥用的更多内容。
你可能还想阅读这篇文章:Android中的依赖注入

非常好的答案。谢谢Vasily。有一个问题,如果UserManager将应用程序上下文作为实例变量保留,那么会存在内存泄漏的风险吗?在某些服务需要上下文创建实例的情况下,上下文是必需的。 - RobertoAllende

2

那么它就不能再用Singleton进行注释了。您需要创建自定义Scope

然后,您需要对使用自定义范围注释的对象负责。一旦更新了您的User,您就会摆脱先前提供User对象的组件,即将其设置为null。然后,您将创建一个新组件,并在下一次要求组件获取User时它会创建一个新的对象。

请注意,模块中任何其他使用您的自定义范围注释的提供程序方法也将返回新创建的对象。

这里是一篇描述如何做到这一点的博客文章。


1
这个答案是不正确的 - 自定义作用域在功能上与@Singleton相同。请阅读此文档:http://www.techyourchance.com/dagger-2-scopes-demystified/。 - Vasiliy
@Vasiliy,请问您能否澄清哪里不正确吗?因为我可以解释为什么答案是正确的。一个组件只能有一个作用域,它不能有多个作用域。@Singleton只是Dagger提供的一种作用域,它可以被称为任何名称,它没有任何特定的功能附加在上面。因此,如果您创建了一个具有特定作用域的组件,则该组件提供的对象始终是相同的(即单例)。您需要做的是摆脱该组件并创建一个新组件,然后提供一个新对象,这就是答案。 - azizbekian
你在这个评论中说的接近正确,但它并不是你回答开头所陈述的:“然后它可能不再被Singleton注释。您必须创建自定义范围”。为什么我说“接近正确”?组件的重新实例化将“重置”该组件提供的所有注释对象。如果没有提到这样一个毁灭性的副作用,给出这样的建议就有点不完整了,我认为。 - Vasiliy
@Vasiliy,“那么它就不能再用Singleton进行注释了。你必须创建自定义作用域。”这是正确的,因为你最终会得到组件依赖关系,并且只要UserModule的父组件具有@Singleton作用域,你就不能再使用该作用域来注释子组件UserModule中的提供方法,因此你必须创建自定义作用域,在这里我看不到任何不正确之处。 - azizbekian
@Vasiliy,“在没有提到这种毁灭性的副作用的情况下给出这样的建议有点不完整,我认为。” 我接受你的观点。但这不是你应该澄清评论的方式。正如你所说,这是你的谦虚意见,这还不能证明答案是错误的。感谢您的建议,将编辑答案以包括“副作用”。 - azizbekian
我不知道你所说的“父组件”和“子组件”(子组件,组件依赖关系,其他?)是什么意思,但问题中并没有提到这一点。无论如何,没有任何个人冒犯之意,但从技术角度来看,我认为你的答案是不正确的(尽管我可以使用不太直接的语言)。问题是“如何更新/重新初始化使用@Name(“me”)注释的用户对象”,对我来说很明显OP只想重新初始化一个单一的对象。 - Vasiliy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接