“注入一切”在Android中是一个不好的做法吗?

4
在学习依赖注入时,我发现有些方法建议注入所有内容,而其他方法则认为不必如此
在我的当前项目中,我对于依赖注入的原则是“如果该类是由我创建的,则使其可注入”。换句话说,只有像SimpleDateFormatArrayListHashMap这样的类是可实例化的。我采用这种方法的目的是,一旦在Activity中调用Injector.getApplicationComponent().inject(this),就可以在任何地方@Inject任何类。基本上,我的所有类都具有带有@Inject的无参构造函数。
我最初使用DI,因为我认为它可以提高性能和内存使用率,一旦new操作符只由Dagger生成的类使用。但我读到了Dagger 1开发人员的post,说DI对性能没有影响,基本上是为了减少样板代码。
第一个问题是:
  • Dagger 2在Android应用程序中没有任何性能优势吗?
我的项目运行良好,我认为“注入所有内容”的方法有助于更好地组织,尽管存在一些缺点。
这种方法的一个示例是以下类:
public class TimelineEntryAdapter {

@Inject
Provider<TwitterEntry> mTwitterProvider;

@Inject
Provider<InstagramEntry> mInstagramProvider;

@Inject
Provider<FacebookEntry> mFacebookProvider;

@Inject
TimelineEntryComparator mComparator;

@Inject
public TimelineEntryAdapter() {
}

第二个问题是:
注入一切在Android中是否是一个不好的实践?
如果第二个问题的答案为“否”,是否有更好的方法来处理非参数构造函数以创建类?因为当我创建一个带有@Inject注释的无参构造函数并且一个类需要一些参数才能工作时,我必须使用setters:
public class SavelArtist {

private MusicBrainzArtist mMusicBrainzArtist;

private DiscogsArtist mDiscogsArtist;

private List<SavelTweet> mTweetList;

private SpotifyArtist mSpotifyArtist;

private List<SavelInstagram> mInstaTimeline;

private List<SavelFacebook> mFacebookTimeline;

private List<SavelRelease> mReleases;

@Inject
Provider<SavelRelease> mReleaseProvider;

@Inject
public SavelArtist() {
}

public void setMusicBrainzArtist(MusicBrainzArtist mbArtist) {
    mMusicBrainzArtist = mbArtist;
}

public void setDiscogsArtist(DiscogsArtist discogsArtist) {
    mDiscogsArtist = discogsArtist;
}

public void setTweetList(List<SavelTweet> tweetList) {
    mTweetList = tweetList;
}

public void setSpotifyArtist(SpotifyArtist spotifyArtist) {
    mSpotifyArtist = spotifyArtist;
}

public void setInstaTimeline(List<SavelInstagram> instaTimeline) {
    mInstaTimeline = instaTimeline;
}

public void setFacebookTimeline(List<SavelFacebook> fbTimeline) {
    mFacebookTimeline = fbTimeline;
}

所有参数都可以在构造函数中设置,一次性在流程中获取。

2
你需要提供所有你想要模拟的东西。你不必提供那些你不需要模拟的东西(例如LinearLayoutManager)。 - EpicPandaForce
就像Epic所说的那样,它是用于轻松切换类的。它可能不是模拟,可能是“这个记录日志,这个不记录日志”。您可以在运行时热交换,也可以在编译时交换。只需记住,如果您使用Dagger,请确保您实际上正在“使用”它,即有多个可以放置在一个位置的类。否则,您只是为了没有真正好处而添加了一个DI框架(我曾经接手过一个这样的项目)。 - Stephen J
2个回答

7
研究依赖注入时,我发现有些方法建议注入所有内容,而有些则认为这并不必要。
您引用的froger_mcs博客文章并没有主张注入所有内容。它非常明确地指出:
本文的目的是展示我们能做什么,而不是我们应该做什么。
并且它继续说明注入所有内容的一个缺点:
如果你想在你的项目中几乎使用Dagger 2来完成所有工作,你很快就会发现64k方法计数限制的大块生成代码被用于注入。
现在,回答您的问题:
Dagger 2在Android应用程序中没有任何性能优势吗?
尽管Dagger 2相对于其他基于反射的DI框架(如Guice)提供了性能优势,但它并不声称相对于手动调用构造函数构建对象图形提供任何性能优势。您可以自己检查生成的类,以确保这些类最终仍然调用构造函数。

在Android中注入所有内容是否是一种不好的做法?

好吧,让我们看一下以下非常常见的Android代码:

Intent nextActivity = new Intent(this, NextActivity.class);
startActivity(nextActivity);

我们是否应该提取一个IntentFactory并使用Dagger 2注入它,仅仅是为了避免在这里使用new关键字?此时,容易陷入学究式的方法。你引用的其他答案中关于可注入和新建对象之间区别的建议更加灵活和优雅。
接下来回答你的下一个问题:

如果第二个问题的答案是“否”,有没有更好的方法处理非参数构造函数以创建类?因为当我创建一个带@Inject注释的非参数构造函数,并且一个类需要一些参数才能工作时,我必须使用setter:

使用setter来传递参数是错误的方法。您应该区分依赖项参数。依赖项通常具有与对象本身相同的生命周期。在对象的生命周期内,可能会使用不同的参数调用针对该对象公开的方法。

非常好的详细回答。我从来没有想过在Java-EE依赖策略和Android之间实际上存在差异。谢谢 :) (我在Java-EE中使用CDI,我的答案基于这种方法) - Nico
很棒的回答,David。感谢你的关注。 - Igor Escodro

3

虽然我不是依赖管理专业人员,但我在公司中使用它,以下是我的一些见解:

注入所有东西基本上是好的。我将所有用于其他模块、类、包的东西都可注入(injectable),只将静态事物(带有隐藏构造函数的静态类)和仅在内部使用的非注入式。

这样做的好处是什么呢? 依赖系统将负责获取实例并且自动清理。另外,在类上使用作用域可以让一个类始终在整个应用程序中只有一个实例(多线程访问它),或者使它可重用(每个需要它的线程都有自己的实例)。

此外,我认为您可以这样简化您的类:

@Reusable (Maybe a good Idea?)
public class SavelArtist {

private MusicBrainzArtist mMusicBrainzArtist;

private DiscogsArtist mDiscogsArtist;

private List<SavelTweet> mTweetList;

private SpotifyArtist mSpotifyArtist;

private List<SavelInstagram> mInstaTimeline;

private List<SavelFacebook> mFacebookTimeline;

private List<SavelRelease> mReleases;

private Provider<SavelRelease> mReleaseProvider;

public SavelArtist() {
}

@Inject
public void init(Provider<SavelRelease> mReleaseProvider) {
  this.mReleaseProvider = mReleaseProvider;
}

始终声明类的范围。它是单例吗?是否可重用?一旦应用程序变得复杂和庞大,这个小细节可以为您节省不少麻烦。

使用init方法声明所有注入变量的优点是具有整洁的代码外观,易于维护,因为所有注入的变量都在此位置。但这实际上是个人喜好 :)


谢谢你的回复! 我尝试了你的解决方案,它有效果了!:D 但是现在,我有另一个疑问:你从我的构造函数中删除了@Inject,并且我明白你希望我使用@Provides代替。 如果我不想为它添加@Provides怎么办?我需要调用new SavelArtist()吗?这样会不会打破“注入一切”的链条? - Igor Escodro
如果您不想使用@Provides,可以再次将其添加到构造函数中。那只是我的一个想法,甚至可能是一种混淆,因为我使用的是Java CDI而不是dagger-2,在那里不需要注释构造函数。 - Nico
太好了!我明白了。谢谢。 - Igor Escodro

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