我想弄清楚如何用dagger 2设置android项目,但我做错了。 Dagger 2和android依赖注入对我来说是相对较新的概念。
这是我的设置。
// App.java root of the application
public class App extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
AppComponent appComponent = DaggerAppComponent.create();
appComponent.inject(this);
return appComponent;
}
}
// AppComponent.java component
@Component(modules = {
AndroidSupportInjectionModule.class,
AppBuilder.class,
SplashModule.class,
AppModule.class})
public interface AppComponent extends AndroidInjector<App> {
}
// AppBuilder.java module which bootstraps activities
@Module
public abstract class AppBuilder {
@ContributesAndroidInjector
abstract SplashActivity splashActivityInjector();
}
我还有扩展DaggerAppCompatActivity和SplashModule的活动,它们在SplashActivity中处理依赖注入
现在这个设置正常运行,我可以在我的SplashActivity中注入MVP模式演示器,如下所示:
// inside SplashActivity
@Inject
SplashContract.Presenter mPresenter;
演示者中的MVP视图
// SplashPresenter
@Inject
SplashContract.View view;
到目前为止一切顺利。但现在我想在演示者中进行额外的注射:
@Inject
AppDataManager appDataManager;
AppDataManager类需要构造应用程序上下文。而且每个应用程序我只需要一个AppDataManager实例。我不知道该怎么做,有很多例子,但他们使用的不同 匕首2版本,因为我没有真正的经验,这让人感到困惑。
我尝试设置额外的AppModule类,该类应该处理AppDataManager,但它在运行时崩溃。
// AppModule.java
@Module
public class AppModule {
private Context mContext;
public AppModule(Context context) {
mContext = context;
}
@Provides
@Singleton
Context provideContext() {
return mContext;
}
@Provides
@Singleton
AppDataManager provideAppDataManager(Context context) {
return new AppDataManager(context);
}
}
我做错了什么? (我怀疑我应该以不同方式定义AppModule。)
编辑Logcat:
FATAL EXCEPTION: main
Process: com.domain.application, PID: 16075
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.domain.application/com.domain.application.ui.splash.SplashActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.domain.application.data.AppDataManager.isUserLoggedIn()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2984)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.domain.application.data.AppDataManager.isUserLoggedIn()' on a null object reference
at com.domain.application.ui.splash.SplashPresenter.openNextActivity(SplashPresenter.java:25)
at com.domain.application.ui.splash.SplashActivity.onCreate(SplashActivity.java:20)
at android.app.Activity.performCreate(Activity.java:6955)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2927)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
答案 0 :(得分:3)
您可以按照以下方式定义AppComponent
:
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppBuilder.class, AppModule.class})
interface AppComponent : AndroidInjector<App>
让App
使用它:
public class App extends Application implements HasActivityInjector, HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
@Inject
DispatchingAndroidInjector<Fragment> dispatchingFragmentInjector;
@Override
public void onCreate() {
// Pay attention here: you have to initialize your `AppModule` with the context.
// Without it, Dagger won't be able to provide a `Context`.
// Since your AppDataManager needs a context to be provided, Dagger will also fail to provide it.
DaggerAppComponent.builder()
.appModule(AppModule(this)) // <-- don't forget this line
.build()
.inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingFragmentInjector;
}
}
您的AppBuilder
应如下所示:
@Module
public abstract class AppBuilder {
@ContributesAndroidInjector(modules = {SplashModule.class})
abstract SplashActivity splashActivityInjector();
}
和您的AppModule
:
@Module
public class AppModule {
private Context mContext;
public AppModule(Context context) {
mContext = context;
}
@Provides
@Singleton
Context provideContext() {
return mContext;
}
@Provides
@Singleton
AppDataManager provideAppDataManager(Context context) {
return new AppDataManager(context);
}
}
您的SplashModule
:
@Module
abstract class SplashModule {
@Binds
abstract SplashContract.View provideView(SplashActivity activity)
@Binds
abstract SplashContract.Presenter providePresenter(SplashPresenter presenter)
}
对演示者使用构造函数注入:
@Inject
public SplashPresenter(SplashContract.View view, AppDataManager manager) {
mView = view;
mManager = manager;
}
最后使用SplashActivity
在@Inject SplashContract.Presenter presenter
内注入您的演示者。为此,请确保SplashActivity
扩展DaggerAppCompatActivity
。此外,SplashActivity
必须实施SplashContract.View
以及SplashPresenter
必须实施SplashContract.Presenter
。
如果您的AppComponent
未声明为Singleton
,则每次注入AppDataManager
时都会有不同的实例。用@Singleton
标记它确保只要提供它的组件存活,就会收到相同的对象。由于您的AppComponent
与Application
绑定,因此您的组件不会死亡,因此会传递相同的对象实例。
根据经验,只要有可能,就应该使用构造函数注入。通过使用它,Dagger将注入创建对象所需的每个参数。此外,使用@Inject
标记构造函数使其成为依赖关系图的一部分:现在可以在不必创建@Provides
方法的情况下注入它。
您必须手动初始化AppModule
,因为它使用非空构造函数。此时,Dagger对如何提供Context
(用于创建AppModule
)没有任何线索。如果AppComponent
上安装的模块使用默认的空构造函数,则不必自行初始化它。
@Binds
注释用于减少进一步的样板。它必须应用于具有单个参数的抽象函数,该参数的类型可分配给返回类型。在您的情况下,参数SplashScreenActivity
实现了返回类型SplashContract.View
:每当您注入SplashContract.View
时,Dagger将提供SplashScreenActivity
的接口实现。
答案 1 :(得分:1)
首先,添加一个自定义Builder
界面和接收inject
的方法App
作为AppComponent
的参数:
@Component.Builder
interface Builder {
@BindsInstance
Builder application(App app);
AppComponent build();
}
void inject(App app);
另外,将@Singleton
注释添加到AppComponent
界面。
然后构建您的应用。在App
课程中,使用自定义构建器注入App
实例:
AppComponent appComponent = DaggerAppComponent
.builder()
.application(this)
.build();
appComponent.inject(this);
更新您的AppModule
以正确提供上下文:
@Provides
Context providesContext(App app) {
return app.getApplicationContext();
}
@Singleton @Provides
AppDataManager provideAppDataManager(Context context) {
return new AppDataManager(context);
}
最后,请确保在inject
电话会议之前就您的活动调用AndroidInjection
super
方法:
AndroidInjection.inject(this)