我已经用 MVP 模式玩了几个星期了,现在我已经到了需要上下文来启动 service
和访问 Shared Preferences的地步
.
我读过 MVP 的目的是将 View 与逻辑分离,并且在 Presenter
中包含 context
可能会破坏该目的(如果我'我错了)。
目前,我有一个如下所示的 LoginActivity:
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();
}
}
演示者界面 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();
}
最后,我的演示者:
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() {
}
}
如您所见,我将 Activity
的上下文传递到我的 Presenter
中,这样我就可以访问 Shared Preferences
。我很担心将上下文传递给我的演示者。这是一件好事吗?还是我应该以其他方式来做?
EDIT 实现了 Jahnold 的第三个偏好
所以让我们忽略接口(interface)和实现,因为它几乎就是全部。所以现在我将 Sharedpreference 的接口(interface)注入(inject)
到我的演示者中。这是我的 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()); }
}
}
我获取上下文的方式是从 MyApplication.java
当应用程序开始时,我确保使用这行代码创建这个对象图:
objectGraph = ObjectGraph.create(new AppModule(this));
这样好吗?我的意思是我现在不必将 Activity 的上下文传递给我的演示者,但我仍然拥有应用程序的上下文。
最佳答案
您问这个问题已经有一段时间了,但我认为无论如何提供答案都会很有用。我强烈建议演示者不应该有 Android 上下文(或任何其他 Android 类)的概念。通过将 Presenter 代码与 Android 系统代码完全分离,您可以在 JVM 上对其进行测试,而无需复杂的模拟系统组件。
要实现这一点,我认为您有三个选择。
从 View 访问 SharedPreferences
这是三者中我最不喜欢的,因为访问 SharedPreferences 不是 View 操作。但是,它确实使 Activity 中的 Android 系统代码远离 Presenter。在你的 View 界面中有一个方法:
boolean isLoggedIn();
可以从演示者那里调用。
使用 Dagger 注入(inject) SharedPreferences
由于您已经在使用 Dagger 注入(inject)事件总线,您可以将 SharedPreferences 添加到您的 ObjectGraph 中,因此将获得一个使用 ApplicationContext 构造的 SharedPreferences 实例。这是您无需将 Context 传递给您的演示者即可获得它们。
这种方法的缺点是您仍在传递一个 Android 系统类 (SharedPreferences),并且在您想要测试 Presenter 时必须模拟它。
创建 SharePreferencesRepository 接口(interface)
这是我从 Presenter 中访问 SharedPreferences 数据的首选方法。基本上,您将 SharedPreferences 视为模型并为其提供存储库接口(interface)。
您的界面类似于:
public interface SharedPreferencesRepository {
boolean isLoggedIn();
}
然后你可以有一个具体的实现:
public class SharedPreferencesRepositoryImpl implements SharedPreferencesRepository {
private SharedPreferences prefs;
public SharedPreferencesRepositoryImpl(Context context) {
prefs = PreferenceManager.getDefaultSharedPreferences(context);
}
@Override
public boolean isLoggedIn() {
return prefs.getBoolean(Constants.IS_LOGGED_IN, false);
}
}
它是 SharedPreferencesRepository 接口(interface),然后你用 Dagger 将它注入(inject)到你的 Presenter 中。这样,可以在测试期间在运行时提供一个非常简单的模拟。正常运行时提供具体实现。
关于android - 在 MVP 模式中,了解 Activity/上下文的演示者是不是一个坏主意?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34303510/