Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dagger2 - injecting presenter gives Caused by: java.lang.NullPointerException: Presenter returned from createPresenter() is null. Activity is ... #298

Open
j2emanue opened this issue Dec 22, 2017 · 22 comments

Comments

@j2emanue
Copy link

i was successfully using mosby2 for a long time now and changed to mosby3. at runtime the presenter cannot be created. Tested on Oreo api 26 emulator xxhdpi.

Here are the dependencies:

implementation 'com.hannesdorfmann.mosby3:mvp:3.1.0' // Plain MVP
implementation 'com.hannesdorfmann.mosby3:viewstate:3.1.0' // MVP + ViewState support

If i do the following which is creating the presenter without dependency injection there is no issue:
@nonnull
@OverRide
public WelcomePresenter createPresenter() {
return new WelcomePresenter();
}

but the following gives a NPE when using dagger2 in android:

public class AuthenticationActivity extends MyappBaseMvpActivity<AuthenticationView, AuthenticationPresenter> implements AuthenticationView {


    @Inject
    AuthenticationPresenter presenter; //confirmed this value is not null

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ((MyappApplication) getApplicationContext()).getAppComponent().inject(this); //injecting presenter which works in Mosby2
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_authentication);
        ButterKnife.bind(this);
        presenter.loadScreen(); 
        //blah blah
    }

@NonNull
    @Override
    public AuthenticationPresenter createPresenter() {
        return presenter; //confirmed through debugger this value is NOT null
    }
  }

Here is what i have tried: moving the " ((MyAppApplication) getApplicationContext()).getAppComponent().inject(this);" call after the super call and even after setnContentView. Same issues.

Here is the exact error i recieve :

FATAL EXCEPTION: main
Process: com.mobile.myapp.labs, PID: 2680
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mobile.myapp.labs/com.mobile.myapp.ui.login.activities.AuthenticationActivity}: java.lang.NullPointerException: Presenter returned from createPresenter() is null. Activity is com.mobile.myapp.ui.login.activities.AuthenticationActivity@8ba807c
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.NullPointerException: Presenter returned from createPresenter() is null. Activity is com.mobile.myapp.ui.login.activities.AuthenticationActivity@8ba807c
at com.hannesdorfmann.mosby3.mvp.delegate.ActivityMvpDelegateImpl.createViewIdAndCreatePresenter(ActivityMvpDelegateImpl.java:92)
at com.hannesdorfmann.mosby3.mvp.delegate.ActivityMvpDelegateImpl.onCreate(ActivityMvpDelegateImpl.java:142)
at com.hannesdorfmann.mosby3.mvp.MvpActivity.onCreate(MvpActivity.java:42)
at com.mobile.myapp.ui.base.MyappBaseMvpActivity.onCreate(MyappBaseMvpActivity.java:88)
at com.mobile.myapp.ui.login.activities.AuthenticationActivity.onCreate(AuthenticationActivity.java:56)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
at android.app.ActivityThread.-wrap11(Unknown Source:0) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
at android.os.Handler.dispatchMessage(Handler.java:105) 
at android.os.Looper.loop(Looper.java:164) 
at android.app.ActivityThread.main(ActivityThread.java:6541) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
12-22 08:58:10.863 1811-1823/system_process E/memtrack: Couldn't load memtrack module

@sockeqwe
Copy link
Owner

sockeqwe commented Dec 22, 2017 via email

@j2emanue
Copy link
Author

ok. could you make it so that it accepts the presenter i am returning on onCreatePresenter ? I'll have to call this in all of our classes which is a huge task. Dagger2 does not work in base abstract classes. So each concrete class i'll have to override the getters/setters for the presenter. we never use to have to do this before , im wondering if you can change it to not do that ?

@sockeqwe
Copy link
Owner

Does getPresenter() and setPresenter() actually solves your problem?

@j2emanue
Copy link
Author

j2emanue commented Dec 22, 2017 via email

@jshvarts
Copy link
Contributor

jshvarts commented Dec 22, 2017

If Presenter is the only thing you inject into your View, you can have the Component expose it so you can inject the Presenter itself in createPresenter().

    override fun createPresenter() = DaggerSearchComponent.builder()
        .appComponent(GithubApp.component)
        .searchModule(SearchModule())
        .build()
        .presenter()

See example https://github.com/jshvarts/MosbyMVP/blob/master/conductor-dagger-sample/src/main/java/com/jshvarts/mosbymvp/searchrepos/SearchViewController.kt

Also, you don't need to include both mvp and viewstate Gradle dependency. The latter is sufficient.

@sockeqwe
Copy link
Owner

Yeah I definitely recommend to create the dependency graph in createPresenter() and have a component like this:

@Component
interface MyApplicationComponent {
   MyPresenter myPresenter();
}

Rather than

@Component
interface MyApplicationComponent {
   void inject(MyActivity activityWherePresenterGetsInjected);
}

Nevertheless, the way you would like to do it (inject presenter into activity) should also work if you override getPresenter() and setPresenter() to use your local variable.

@j2emanue
Copy link
Author

j2emanue commented Dec 22, 2017 via email

@sockeqwe
Copy link
Owner

sockeqwe commented Dec 22, 2017 via email

@jshvarts
Copy link
Contributor

If you look at my example above, you will see that my Presenter gets instantiated by Dagger and it takes parameters via constructor injection.

@j2emanue
Copy link
Author

I see no difference if I do this. I'm calling dagger inject before super.oncreate . Therefore my presenter already has a reference. I don't think createpresenter method is called before onCreate , right? I guess you guys changed the implementation a bit. How is exposing the method in the component bringing different results then annotating the presenter constructor with @Inject. I tried overriding set/getters as recommended but same issue.

@sockeqwe
Copy link
Owner

sockeqwe commented Dec 22, 2017

Is your source code available somewhere so that I can try to debug it?
createPresenter() is called in super.onCreate() (see here )


I think either you or we are misunderstanding something, so here is a full example implementation what @jshvarts and I are talking about is the following one:

public class AuthenticationActivity extends MvpActivity<AuthenticationView, AuthenticationPresenter> implements AuthenticationView {
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_authentication);
        ButterKnife.bind(this);
        presenter.loadScreen();  // presenter variable is a Mosby internal variable from MvpActivity
        //blah blah
    }
  
    @Override
    public AuthenticationPresenter createPresenter() {
        return    ((MyappApplication) getApplicationContext()).getAppComponent().authenticationPresenter();
    }
  }
@Singleton
class AuthenticationPresenter extend MvpBasePresenter<AuthenticationView> {
   
    @Inject
    public AuthenticationPresenter(UserManager userManager) { // Some dependencies passed as constructor parameter
        ...
    }
}
@Module
class AppModule {

    @Provides 
    @Singleton
    public UserManager provideUserManager() {
       ...
    } 
}
@Singleton
@Component(modules = {AppModule.class}
interface AppComponent {
    AuthenticationPresenter authenticationPresenter();
}

Please note that we are not manually instantiating AuthenticationPresenter nor specifying it in AppModule.

How is exposing the method in the component bringing different results then annotating the presenter constructor with @Inject.

You are not removing @Inject method from presenter constructor, this is still the same as you see in the cose snipped above. The difference is really just how you define your dagger component: instead of asking dagger to inject presenter into activity (via inject(AuthenticationActicity activity), you ask dagger to give you a presenter (by exposing a method like authenticationPresenter()).

The later is the recommended way for Mosby

@j2emanue
Copy link
Author

j2emanue commented Dec 22, 2017

here is what i have done:

My app component has the following declared:

LandingPagePresenter landingPagePresenter();

and in the landingPageActivity i took out the @Inject annotation so now the presenter declartion looks like this:

// @Inject
LandingPagePresenter presenter;

here is the onCreate method:

@OverRide
protected void onCreate(Bundle savedInstanceState) {
((MyApplication) getApplicationContext()).getAppComponent().inject(this);
super.onCreate(savedInstanceState);
presenter= getPresenter(); //now i am calling your getPresenter method but i also tried without this
setContentView(R.layout.activity_homepage);
//blah blah
}

here is the createpresenter override:

@NonNull
    @Override
    public LandingPagePresenter createPresenter() {
//confirmed dagger did return a presenter here through debugger
       return ((MyApplication) getApplicationContext()).getAppComponent().landingPagePresenter();

    }

i also tried the following:

@nonnull
@OverRide
public LandingPagePresenter createPresenter() {
//confirmed dagger did return a presenter here through debugger
presenter= ((MyApplication) getApplicationContext()).getAppComponent().landingPagePresenter();
return presenter;
}

here is the constructor signature for the presenter::

@Inject
public LandingPagePresenter( GatewayFactory gatewayFactory, GetFeedsUsecase feedsUsecase, MySharedPrefRepo sharedPrefRepo) { //...blah blah
}

i still get the same error:

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.mobile.my.labs, PID: 5100 java.lang.NullPointerException: Presenter returned from createPresenter() is null. Activity is com.mobile.my.ui.landing.LandingPageActivity@b0c662e at com.hannesdorfmann.mosby3.mvp.delegate.FragmentMvpDelegateImpl.createViewIdAndCreatePresenter(FragmentMvpDelegateImpl.java:104) at com.hannesdorfmann.mosby3.mvp.delegate.FragmentMvpDelegateImpl.onCreate(FragmentMvpDelegateImpl.java:268) at com.hannesdorfmann.mosby3.mvp.MvpFragment.onCreate(MvpFragment.java:101) at android.support.v4.app.Fragment.performCreate(Fragment.java:2246) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1377) at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1187) at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:1070) at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:115) at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2374) at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2332) at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2209) at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:649) at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:167) at android.support.v4.view.ViewPager.populate(ViewPager.java:1238) at android.support.v4.view.ViewPager.populate(ViewPager.java:1086) at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1616) at android.view.View.measure(View.java:22002) at android.widget.LinearLayout.measureVertical(LinearLayout.java:958) at android.widget.LinearLayout.onMeasure(LinearLayout.java:685) at android.view.View.measure(View.java:22002) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)

in all cases above i have not overriden setPresenter or getPresenter.
i guess i'll have to see if anyone else gets these errors and for now return to mosby2 unless you guys see something im not doing or missed .

@jshvarts
Copy link
Contributor

jshvarts commented Dec 22, 2017

I believe all you need to do is to have this done:

@Override
public LandingPagePresenter createPresenter() {
    return ((MyApplication) getApplicationContext()).getAppComponent().landingPagePresenter();
}

Do not have any Dagger code in the onCreate() and do not declare LandingPagePresenter presenterinstance variable in your Activity (View). Whenever you need to reference the Presenter in your Activity, just call presenter.foo()

@j2emanue
Copy link
Author

j2emanue commented Dec 22, 2017 via email

@jshvarts
Copy link
Contributor

That's why I asked in my first reply above if the Presenter was the only one dependency that needs to be injected.

Something like this should work (I am converting it from Kotlin as I type):

In your Dagger Component:

void inject(LandingPageActivity activity);

In your LandingPageActivity:

@Inject
LandingPagePresenter myPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ((MyappApplication) getApplicationContext()).getAppComponent().inject(this);
    super.onCreate(savedInstanceState);
    ...
}

@Override
void setPresenter(LandingPagePresenter presenter) {
    this.presenter = presenter
}

@Override
LandingPagePresenter createPresenter() {
    return myPresenter;
}

@j2emanue
Copy link
Author

j2emanue commented Dec 23, 2017

jshvarts ,
i assume that you meant myPresenter variable in the setter and not this.presenter :

@Override
void setPresenter(LandingPagePresenter presenter) {
    this.myPresenter = presenter
}

i treied exactly as you put and i also tried it in another class just to make sure. its the same error. i have the code exactly how you wrote it. lets take a look more in depth at my code:

here is my activity:

`public class LandingPageActivity extends BaseMvpActivity<LandingPageView, LandingPagePresenter> implements LandingPageView {

        @Inject
    LandingPagePresenter presenter;

    @Inject
    ApplicationMode appMode;

    @Inject
    EventBus bus;


    
    @Override
    public void setPresenter(LandingPagePresenter presenter) {
        this.presenter = presenter;
    }

    @NonNull
    @Override
    public LandingPagePresenter createPresenter() {
        return this.presenter;
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ((MyApplication) getApplicationContext()).getAppComponent().inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_homepage);
        ButterKnife.bind(this);
        initView();
    }

    }`

and here is the BaseActivity class definition:

public abstract class BaseMvpActivity<V extends BaseMvpView, P extends MvpPresenter<V>>
        extends MvpActivity<V, P>  {........}

and here is BaseMvpView:

public interface BaseMvpView extends MvpView {

    void showError(@StringRes int e);

    void showError(String msg);
//....blah blah
}

again the environment im running on is Oreo api 26 but i also tried it on api 23 emulator.
with dependency: implementation 'com.hannesdorfmann.mosby3:viewstate:3.1.0'

for completeness below is my dagger2 component:

@Singleton
@Component(modules = {AppModule.class, NetworkModule.class, ActivityModule.class})
public interface AppComponent {

    void inject(MyApplication target);

    void inject(LandingPageActivity target);

//......

}

@sockeqwe
Copy link
Owner

sockeqwe commented Dec 23, 2017 via email

@lebeshev
Copy link

lebeshev commented May 25, 2018

@j2emanue Hi!
It's probably too late, but since an issue hasn't been closed I want to tell you about how I make dependency injection for mosby. I use MVI instead of MVP but it doesn't change a lot.

I have a LoginFragment class with corresponding LoginPresenter and LoginComponent.
Code of LoginComponent:

@Subcomponent
public interface LoginComponent {

    @Subcomponent.Builder
    interface Builder {

        LoginComponent build();

    }

    void inject(LoginFragment loginFragment);

    // Important!
    LoginPresenter getLoginPresenter();

}

I have a separate class called Injection, where I handle lifecycle of all my components and subsomponents. It looks kinda like this:

public class Injection {

    private Injection() {
    }

    private static AppComponent appComponent;

    private static LoginComponent loginComponent;

    public static void init(Application application) {
        appComponent = DaggerAppComponent.builder()
                .application(application)
                .build();
    }

    public static LoginComponent inject(LoginFragment loginFragment) {
        loginComponent = appComponent.loginComponentBuilder().build();
        loginComponent.inject(loginFragment);
        return loginComponent;
    }

    public static void clear(LoginFragment loginFragment) {
        loginComponent = null;
    }

}

And here is how all this stuff is being used in LoginFragment:

class LoginFragment : MviFragment<LoginView, LoginPresenter>(), LoginView {

    private lateinit var loginComponent: LoginComponent

    override fun onCreate(savedInstanceState: Bundle?) {
        loginComponent = Injection.inject(this)
        super.onCreate(savedInstanceState)
    }

   // Other lifecycle methods

    override fun createPresenter(): LoginPresenter = loginComponent.loginPresenter

}

This way I can inject all my fragment's dependensies using members-injection (calling Injection.inject(this)) and get my presenter using provision method (the LoginPresenter getloginPresenter() one). You can read more about members-injection and provision methods in docs.

Using this method I get no runtime exceptions. Hope I helped!

@j2emanue
Copy link
Author

do you guys want to set up a phone call or screen share so we can fix this issue ?

@pawelnbd1992
Copy link

@j2emanue Did you fix this issue ?

@leslieam
Copy link

I've got the same issue. Using Dagger 2.16 and Mosby 3.1.0.

@engi2nee
Copy link

engi2nee commented Apr 26, 2019

I had no problems injecting the presenter then setting it manually to the Mosby's internal presenter
So in the activity:

@Inject lateinit var myPresenter: MyPresenter
override fun createPresenter(): MyPresenter = myPresenter

MyPresenter uses dagger's constructor injecting of course

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants