How to get dagger to inject upon onCreate in Presenter
fredagsfys opened this issue · 7 comments
I've got a Activity with corresponding Presenter. I'm using Dagger 2 to inject a component which calls various API methods with Retrofit.
However, in my presenter I can't figure out how to make Dagger 2 inject the component directly onCreate().
I've done something like this below, but it isn't good enough as i get a null reference because I don't got a reference to the view(Activity) before onTakeView()
Yes I'm new to this, both RxJava and MVP pattern. Please help me figure out how to solve this.
I want the data to get fetched directly upon onCreate(), as soon as the activity loads.
Activity
@RequiresPresenter(AssignmentPresenter.class)
public class AssignmentActivity extends NucleusAppCompatActivity<AssignmentPresenter> implements NavigationView.OnNavigationItemSelectedListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_assignment);
ButterKnife.bind(this);
if (savedInstanceState == null)
getPresenter().request("some-token-xx");
}
Presenter
public class AssignmentPresenter extends RxPresenter<AssignmentActivity> {
public static final int REQUEST_ASSIGNMENTS = 1;
private String mAccessToken;
@Inject Retrofit mRetrofit;
private AssignmentActivity mView;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(REQUEST_ASSIGNMENTS,
new Func0<Observable<List<Assignment>>>() {
@Override
public Observable<List<Assignment>> call() {
return mRetrofit.create(AssignmentService.class)
.GET(mAccessToken)
.subscribeOn(Schedulers.newThread())
.observeOn(mainThread());
}
},
new Action2<AssignmentActivity, List<Assignment>>() {
@Override
public void call(AssignmentActivity assignmentActivity, List<Assignment> response) {
assignmentActivity.onSuccess(response);
}
}, new Action2<AssignmentActivity, Throwable>() {
@Override
public void call(AssignmentActivity assignmentActivity, Throwable throwable) {
assignmentActivity.onError(throwable);
}
}
);
}
@Override
protected void onTakeView(AssignmentActivity view) {
super.onTakeView(view);
mView = view;
((App) mView.getApplication()).getRestApiComponent().inject(this);
}
void request(String accessToken) {
mAccessToken = accessToken;
start(REQUEST_ASSIGNMENTS);
}
}
You don't need to have a view reference to access global dependency graph. The simplest solution is to call App.getInstance().getRestApiComponent().inject(this)
instead of your current call. You just need to implement static getInstance method in your App (Application is a natural singleton, so don't worry to store a static reference to App inside the App itself). The disadvantage of this is that your code is still coupled with god object App
.
Another option is here: #32 . Instead of Presenter self-injection, you can make View (Activity, Fragment or Layout) to inject all the dependencies needed. The advantage is that Presenter does not even know who'll provide these dependencies for him (Hollywood Principle: Don't call us, we'll call you). The disadvantage is that you need to remember not call getPresenter()
before onCreate()
(no field initializations like clickListener = getPresenter()::doSomething
), or you'll got crash otherwise.
Awesomeeee!! Thank you so much. I wish I could buy you a beer 😄
Cheers mate, here's a virtual one 🍺
@yankeppey Would you suggest to make a base Activity to do as the second suggestion you gave and inherit from it on each Activity? Maybe even for fragments...
I tried to make a BaseActivity to prevent repeating the presenter injection onCreate() everywhere.
It doesn't seem to work, altho when doing the same with a BaseFragment it works pretty well.
Looking like this below
BaseActivity
public class BaseActivity<P extends Presenter> extends NucleusAppCompatActivity<P> {
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
final PresenterFactory<P> superFactory = super.getPresenterFactory();
setPresenterFactory(new PresenterFactory<P>() {
@Override
public P createPresenter() {
P presenter = superFactory.createPresenter();
((Injector) getApplication()).inject(presenter);
return presenter;
}
});
super.onCreate(savedInstanceState, persistentState);
Icepick.saveInstanceState(this, savedInstanceState);
}
}
Activity
@RequiresPresenter(AssignmentPresenter.class)
public class AssignmentActivity extends BaseActivity<AssignmentPresenter> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_assignment);
ButterKnife.bind(this);
}
}
If I do this, it works
@RequiresPresenter(AssignmentPresenter.class)
public class AssignmentActivity extends NucleusAppCompatActivity<AssignmentPresenter> {
@Override
protected void onCreate(Bundle savedInstanceState) {
final PresenterFactory<AssignmentPresenter> superFactory = super.getPresenterFactory();
setPresenterFactory(new PresenterFactory<AssignmentPresenter>() {
@Override
public AssignmentPresenter createPresenter() {
AssignmentPresenter presenter = superFactory.createPresenter();
((Injector) getApplication()).inject(presenter);
return presenter;
}
});
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_assignment);
ButterKnife.bind(this);
}
}
What does mean "does not work?" How can it be? :) Can you use debugger to trace code execution and see what exactly is wrong?
@devharis you use onCreate(Bundle savedInstanceState, PersistableBundle persistentState)
in your BaseActivity which is not normally called (docs tell something about persistable mode but honestly I've never used it). You should use onCreate(Bundle savedInstanceState)
, with one parameter.
Yes, it was totally correct. I use the wrong onCreate()
👍