Android Dagger 2.14设置

时间:2018-03-05 08:52:37

标签: android dagger-2

我想弄清楚如何用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) 

2 个答案:

答案 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标记它确保只要提供它的组件存活,就会收到相同的对象。由于您的AppComponentApplication绑定,因此您的组件不会死亡,因此会传递相同的对象实例。

根据经验,只要有可能,就应该使用构造函数注入。通过使用它,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)
相关问题