我已经用MVP模式玩了几周了,到了我需要上下文来开始service
和访问的地步Shared Preferences
。
我读过,MVP的目的是将视图与逻辑分离,并context
在Presenter
可能的范围内破坏该目的(如果我错了,请纠正我)。
当前,我有一个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
just中,以便可以访问Shared Preferences
。我很担心将上下文传递给我的演示者。这可以做吗?还是我应该以其他方式来做?
编辑实施了Jahnold的第三个偏好
因此,让我们忽略接口和实现,因为它几乎是整个事情。现在,我是injecting
演示者中Sharedpreference的界面。这是我的代码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));
这个可以吗?我的意思是我现在不必将活动的上下文传递给我的演示者,但是我仍然拥有该应用程序的上下文。
自从您问这个问题以来已经有一段时间了,但是我认为无论如何提供答案都是有用的。我强烈建议演示者不应该具有Android上下文(或任何其他Android类)的概念。通过将Presenter代码与Android系统代码完全分开,您可以在JVM上对其进行测试,而不会导致模拟系统组件复杂化。
为此,我认为您有三种选择。
从视图访问SharedPreferences
这是我这三个中最不喜欢的,因为访问SharedPreferences 不是视图操作。但是,它的确会使Activity中的Android系统代码远离Presenter。在您的视图界面中有一个方法:
boolean isLoggedIn();
可以从演示者那里调用。
使用Dagger注入SharedPreferences
由于您已经在使用Dagger注入事件总线,因此可以将SharedPreferences添加到ObjectGraph中,这样将获得已使用ApplicationContext构造的SharedPreferences实例。这是您获得它们而不必将上下文传递给演示者的情况。
这种方法的缺点是您仍在传递Android系统类(SharedPreferences),并且在要测试Presenter时必须对其进行模拟。
创建一个SharePreferencesRepository接口
这是我从Presenter中访问SharedPreferences数据的首选方法。基本上,您将SharedPreferences视为模型,并为其提供了一个存储库接口。
您的界面类似于:
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接口,然后将Dagger插入到Presenter中。这样,可以在测试期间的运行时提供非常简单的模拟。在正常操作期间,提供了具体的实现。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句