我已经在我的应用程序中设置了dagger2依赖项,因为我理解它并通过许多示例。我没有找到的是在注入后使用所有依赖项的正确方法。
模块中的每个单例取决于之前单例的输出。如何在不调用每个单例的情况下使用整个依赖图来获得所需的输入?
鉴于以下内容:
AppComponent
@Singleton
@Component(modules = {
DownloaderModule.class
})
public interface AppComponent {
void inject(MyGameActivity activity);
}
DownloaderModule
@Module
public class DownloaderModule {
public static final String NETWORK_CACHE = "game_cache";
private static final int GLOBAL_TIMEOUT = 30; // seconds
public DownloaderModule(@NonNull String endpoint) {
this(HttpUrl.parse(endpoint));
}
@Provides @NonNull @Singleton
public HttpUrl getEndpoint() {
return this.endpoint;
}
@Provides @NonNull @Singleton @Named(NETWORK_CACHE)
public File getCacheDirectory(@NonNull Context context) {
return context.getDir(NETWORK_CACHE, Context.MODE_PRIVATE);
}
@Provides @NonNull @Singleton
public Cache getNetworkCache(@NonNull @Named(NETWORK_CACHE) File cacheDir) {
int cacheSize = 20 * 1024 * 1024; // 20 MiB
return new Cache(cacheDir, cacheSize);
}
@Provides @NonNull @Singleton
public OkHttpClient getHttpClient(@NonNull Cache cache) {
return new OkHttpClient.Builder()
.cache(cache)
.connectTimeout(GLOBAL_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(GLOBAL_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(GLOBAL_TIMEOUT, TimeUnit.SECONDS)
.build();
}
MyGameApp
public class MyGameApp extends Application {
private AppComponent component;
private static Context context;
public static MyGameApp get(@NonNull Context context) {
return (MyGameApp) context.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
component = buildComponent();
MyGameApp.context = getApplicationContext();
}
public AppComponent component() {
return component;
}
protected AppComponent buildComponent() {
return DaggerAppComponent.builder()
.downloaderModule(new DownloaderModule("https://bogus.com/"))
.build();
}
}
答案 0 :(得分:1)
我会尝试对此有所了解,但有几种方法可以解读这个。我更喜欢自下而上的方法 - 基本上从你的对象需要开始,然后开始工作。在这种情况下,我会从MyGameActivity
开始。不幸的是,你没有为此粘贴代码,所以我必须有点创意,但这对练习来说没问题。
因此,在您的应用中,您可能会收到AppComponent
并为inject
致电MyGameActivity
。所以我猜这个活动有一些可注射的字段。我不确定你是否直接在那里使用OkHttpClient
,但让我们说你这样做。类似的东西:
public class MyGameActivity extends SomeActivity {
@Inject
OkHttpClient okHttpClient;
// ...
}
我想这样想的方式如下。 Dagger知道你需要OkHttpClient
给出的AppComponent
。因此,它将研究如何提供它 - 它是否可以构建对象本身,因为您使用@Inject
注释了构造函数?它需要更多的依赖吗?
在这种情况下,它将查看提供此客户端的组件的模块。它将达到getHttpClient
并意识到它需要一个Cache
对象。它将再次查找如何提供此对象 - 构造函数注入,另一个提供程序方法?
它在模块中再次提供,因此它将到达getNetworkCache
并再次意识到它需要另一个依赖项。
此行为将继续执行,直到它到达不需要其他依赖项的对象,例如HttpUrl
中的getEndpoint
。
完成所有这些操作后,即可创建OkHttpClient
。
我认为很容易理解为什么你的依赖图中没有循环 - 如果对象A
取决于B
而B
取决于你,则无法创建对象A
getEndpoint
。因此,想象一下,出于某种奇怪的原因,您将达到方法OkHttpClient
,这取决于该模块中的@Singleton
。这不行。你会绕圈子永远不会走到尽头。
所以如果我理解你的问题:如何使用整个依赖图而不调用每个单例来获得所需的输入?
不是。它必须调用所有方法才能获得单身人士。至少第一次在相同的组件/范围内提供它们。之后,只要保留组件的相同实例,范围内的依赖项将始终返回相同的实例。匕首会确保这一点。如果由于某种原因破坏组件或重新创建它,那么依赖项将不是相同的实例。更多信息here。事实上,所有范围都是如此。不只是component()
s。
然而,据我所知,你做得对。创建应用程序后,您可以创建组件并对其进行缓存。之后,每次使用方法cildren()
时,总是返回相同的组件,并且范围的依赖关系始终是相同的。