Dagger 2 on Android.存储和访问@Singleton组件的不同方式

2022-09-04 22:30:44

这是关于如何存储@Singleton范围的 Dagger 2 组件的第 N 个问题,这些组件的生存期应等于应用程序的生存期。

在使用 Dagger 2 的 Android 应用中,通常至少有一个组件@Singleton作用域,并且应该在应用程序的所有生命周期内持续存在:由于这些要求,它通常被初始化并存储在自定义 Application 类中。

由于此组件的实例必须在应用程序的所有部分中可访问,因此我见过这样的代码:

1. 将组件存储在应用程序类内的公共静态变量中。

public class App extends Application {

    public static AppComponent appComponent;

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

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)).build();
    }
}

通过这种方式,可以使用以下方式在其他任何地方访问它:

App.appComponent.inject(this);

2. 将组件存储在应用程序实例内的私有变量中,并为其创建静态访问器。

public class App extends Application {

    private static AppComponent appComponent;

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

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)).build();
    }

    public static AppComponent getAppComponent() {
        return appComponent;
    }
}

通过这种方式,可以使用以下方式在其他任何地方访问它:

App.getAppComponent().inject(this);

3. 将组件存储在应用程序实例内的私有变量中,并为其创建非静态访问器。

public class App extends Application {

    private AppComponent appComponent;

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

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)).build();
    }

    public AppComponent getAppComponent() {
        return appComponent;
    }
}

这样,只能从包含对上下文引用的类实例访问它:

// From within an Activity.
((App) getApplication()).getAppComponent().inject(this);

// From within a Fragment.
((App) getActivity().getApplication()).getAppComponent().inject(this);

// From within any other class which holds a reference to a Context. 
((App) context.getApplicationContext()).getAppComponent().inject(this);

最后一种方法使得将上下文引用传递给任何愿意访问组件的类几乎是强制性的(即使该类不需要该上下文用于任何其他目的)。

恕我直言,必须“手动注入”上下文实例才能访问注入器本身听起来有点违反直觉。

另一方面,许多人建议不要使用静态变量,但是:为什么?如果一个对象必须在应用程序的生存期内(这意味着在 JVM 实例的整个生存期)内保留在内存中,那么如果它存储在静态变量中会有什么问题呢?

其他人说静态的东西不能在测试中被嘲笑,这是真的,尽管我不确定我是否完全理解这一点,因为它是DI模式,它可以轻松模拟/测试,而不是注入器本身,所以我们为什么要模拟注入器本身呢?

这些替代方案的优缺点是什么?除了这里已经提到的那些之外,还有其他可能的替代方案吗?


答案 1

对于 1 和 2,您使用的是静态引用。这是一个关于为什么要避免它们的好线索

为什么静态变量被认为是邪恶的?

所以剩下的唯一选择是第3个。这就是我在项目中使用的内容。关于是否应该将上下文作为参数传递,取决于项目的体系结构以及设计 Dagger 依赖项的方式。就个人而言,我没有这个问题,因为我只是在活动/片段中注入。你能给我一个例子,你需要传递上下文来注入依赖关系吗?


答案 2

我使用方法#2。方法 #1 的主要问题是您公开了一个可变字段。如果您的模块不需要构造,则可以创建字段 。但作为风格问题,我仍然不喜欢公开字段。Contextfinal

您通常应该避免全局状态,尤其是在 Android 中,因为组件和 VM 本身的生命周期复杂且有时不直观。但这是此规则的例外。每个 VM 只存在一个实例,并且在创建任何其他组件之前,其方法仅被调用一次。这使其成为创建和存储静态单例的可接受位置。ApplicationonCreate()


推荐