我已经玩了几个星期的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
,因此我可以访问Shared Preferences
.我非常担心将上下文传递给我的演示者.这是一件好事吗?或者我应该采取其他方式吗?
EDIT实施了Jahnold的第三选择权
所以让我们忽略界面和实现,因为它几乎就是整个事情.所以现在我是injecting
共享偏好的接口进入我的演示者.这是我的代码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
当应用程序开始时,我确保用这行代码创建这个Object图:
objectGraph = ObjectGraph.create(new AppModule(this));
这个可以吗?我的意思是我现在不必将活动的上下文传递给我的演示者,但我仍然有应用程序的上下文.
你问这个问题已经有一段时间了,但我认为无论如何都要提供一个答案是有用的.我强烈建议演示者不应该有Android Context(或任何其他Android类)的概念.通过将Presenter代码与Android系统代码完全分离,您可以在JVM上对其进行测试,而无需模拟系统组件的复杂性.
为实现这一点,我认为你有三个选择.
从View中访问SharedPreferences
这是我最不喜欢的三个,因为访问SharedPreferences 不是一个视图操作.但是,它确实使Activity中的Android系统代码远离Presenter.在您的视图界面中有一个方法:
boolean isLoggedIn();
可以从演示者调用.
使用Dagger注入SharedPreferences
由于您已经使用Dagger注入事件总线,因此可以将SharedPreferences添加到ObjectGraph中,这样就可以获得使用ApplicationContext构造的SharedPreferences实例.这是你得到它们而不必将Context传递给你的演示者.
这种方法的缺点是你仍然传入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.这样,在测试期间可以在运行时提供非常简单的模拟.在正常操作期间,提供具体实现.
这个问题是在不久前回答的,并且假设MVP的定义就是他的代码中使用的OP,@ Jahnold的回答确实很好。
但是,应该指出的是,MVP是一个高级概念,并且可以遵循MVP原理进行许多实现-有多种方法可以使猫变皮。
MVP的另一种实现是基于Android中的Activity不是UI Elements的想法,它指定Activity
并Fragment
作为MVP演示者。在此配置中,MVP演示者可以直接访问Context
。
顺便说一句,即使在前面提到的MVP实现中,我也不会使用Context
它来访问SharedPreferences
presenter-我仍然会为SharedPreferences
它定义包装器类并将其注入presenter。