繁体   English   中英

Android Dagger 2.14设置

[英]Android Dagger 2.14 setup

我想弄清楚如何用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实例。 我不知道如何做到这一点,有很多例子,但他们使用不同的Dagger 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) 

您可以将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;
}

最后使用@Inject SplashContract.Presenter presenter者在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的接口实现。

首先,添加一个自定义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);
}

最后,确保在super调用之前在您的活动上调用AndroidInjectioninject方法:

AndroidInjection.inject(this)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM