Przez kilka tygodni bawiłem się wzorcem MVP i doszedłem do punktu, w którym potrzebuję kontekstu, aby rozpocząć service
i uzyskać dostęp do Shared Preferences
.Czy prezenter znający działanie/kontekst jest złym pomysłem we wzorcu MVP?
Przeczytałem, że celem MVP jest oderwanie widoku od logiki i posiadanie context
wewnątrz Presenter
może pokonać ten cel (popraw mnie jeśli się mylę w tym).
Obecnie mam LoginActivity, który wygląda mniej więcej tak:
LoginActivity.java
public class LoginActivity extends Activity implements ILoginView {
private final String LOG_TAG = "LOGIN_ACTIVITY";
@Inject
ILoginPresenter mPresenter;
@Bind(R.id.edit_login_password)
EditText editLoginPassword;
@Bind(R.id.edit_login_username)
EditText editLoginUsername;
@Bind(R.id.progress)
ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
MyApplication.getObjectGraphPresenters().inject(this);
mPresenter.setLoginView(this, getApplicationContext());
}
@Override
public void onStart() {
mPresenter.onStart();
ButterKnife.bind(this);
super.onStart();
}
@Override
public void onResume() {
mPresenter.onResume();
super.onResume();
}
@Override
public void onPause() {
mPresenter.onPause();
super.onPause();
}
@Override
public void onStop() {
mPresenter.onStop();
super.onStop();
}
@Override
public void onDestroy() {
ButterKnife.unbind(this);
super.onDestroy();
}
@OnClick(R.id.button_login)
public void onClickLogin(View view) {
mPresenter.validateCredentials(editLoginUsername.getText().toString(),
editLoginPassword.getText().toString());
}
@Override public void showProgress() { mProgressBar.setVisibility(View.VISIBLE); }
@Override public void hideProgress() {
mProgressBar.setVisibility(View.GONE);
}
@Override public void setUsernameError() { editLoginUsername.setError("Username Error"); }
@Override public void setPasswordError() { editLoginPassword.setError("Password Error"); }
@Override public void navigateToHome() {
startActivity(new Intent(this, HomeActivity.class));
finish();
}
}
Presenter Interfejs ILoginPresenter.java
public interface ILoginPresenter {
public void validateCredentials(String username, String password);
public void onUsernameError();
public void onPasswordError();
public void onSuccess(LoginEvent event);
public void setLoginView(ILoginView loginView, Context context);
public void onResume();
public void onPause();
public void onStart();
public void onStop();
}
Wreszcie moja Presenter :
LoginPresenterImpl.java
public class LoginPresenterImpl implements ILoginPresenter {
@Inject
Bus bus;
private final String LOG_TAG = "LOGIN_PRESENTER";
private ILoginView loginView;
private Context context;
private LoginInteractorImpl loginInteractor;
public LoginPresenterImpl() {
MyApplication.getObjectGraph().inject(this);
this.loginInteractor = new LoginInteractorImpl();
}
/**
* This method is set by the activity so that way we have context of the interface
* for the activity while being able to inject this presenter into the activity.
*
* @param loginView
*/
@Override
public void setLoginView(ILoginView loginView, Context context) {
this.loginView = loginView;
this.context = context;
if(SessionUtil.isLoggedIn(this.context)) {
Log.i(LOG_TAG, "User logged in already");
this.loginView.navigateToHome();
}
}
@Override
public void validateCredentials(String username, String password) {
loginView.showProgress();
loginInteractor.login(username, password, this);
}
@Override
public void onUsernameError() {
loginView.setUsernameError();
loginView.hideProgress();
}
@Override
public void onPasswordError() {
loginView.setPasswordError();
loginView.hideProgress();
}
@Subscribe
@Override
public void onSuccess(LoginEvent event) {
if (event.getIsSuccess()) {
SharedPreferences.Editor editor =
context.getSharedPreferences(SharedPrefs.LOGIN_PREFERENCES
.isLoggedIn, 0).edit();
editor.putString("logged_in", "true");
editor.commit();
loginView.navigateToHome();
loginView.hideProgress();
}
}
@Override
public void onStart() {
bus.register(this);
}
@Override
public void onStop() {
bus.unregister(this);
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
}
Jak widać, zdałem kontekst z Activity
do mojego Presenter
tylko tak mogę uzyskać dostęp do Shared Preferences
. Martwię się, że przekazuję kontekst mojemu prezenterowi. Czy to jest w porządku? Czy powinienem to robić w inny sposób?
EDIT Zaimplementowane Jahnold za 3-te preferencji
Więc ignorować interfejsu i implementacji, ponieważ jest to dość dużo cała sprawa. Teraz jestem injecting
interfejsem dla Sharedpreference w moim prezenterem. Oto mój kod na AppModule
AppModule.java
@Module(library = true,
injects = {
LoginInteractorImpl.class,
LoginPresenterImpl.class,
HomeInteractorImpl.class,
HomePresenterImpl.class,
}
)
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
@Provides
@Singleton
public RestClient getRestClient() {
return new RestClient();
}
@Provides
@Singleton
public Bus getBus() {
return new Bus(ThreadEnforcer.ANY);
}
@Provides
@Singleton
public ISharedPreferencesRepository getSharedPreferenceRepository() { return new SharedPreferencesRepositoryImpl(application.getBaseContext()); }
}
}
Sposób uzyskać kontekst jest z MyApplication.java
Kiedy zaczyna aplikacja, I upewnij się, aby stworzyć ten wykres obiektów z tej linii kodu:
objectGraph = ObjectGraph.create(new AppModule(this));
Czy to w porządku? Chodzi mi o to, że teraz nie muszę przekazywać kontekstu z działania do mojego prezentera, ale wciąż mam kontekst aplikacji.
Podoba mi się również ten ostatni, ale patrząc na implementację, nadal potrzebowałbym odpowiedniego kontekstu? Więc Wewnątrz mojego modułu DI, muszę określić gdzieś właściwy kontekst? Pytam o to, ponieważ nie mam bladego pojęcia, jak ustawić ten zastrzyk. Czy to samo można zrobić z usługami? –
Nieważne, rozgryzłem to. Działa dobrze, ale nie jestem pewien, jak to działa za kulisami. Zaktualizuję moje pytanie, aby pokazać, co zrobiłem, aby wykonać zastrzyk, i daj mi znać, jeśli to nie jest optymalne. –
To, co zrobiłeś, wygląda dobrze. Kontekst aplikacji jest teraz ukryty przed prezenterem, ponieważ jest zamknięty wewnątrz SharedPreferencesRepository. Wszystkie prezenter wie o tym, że jest repozytorium. – Jahnold