演示者了解活动/上下文对MVP模式是一个坏主意吗?

补救。

我已经用MVP模式玩了几周了,到了我需要上下文来开始service和访问的地步Shared Preferences

我读过,MVP的目的是将视图与逻辑分离,并contextPresenter可能的范围内破坏该目的(如果我错了,请纠正我)。

当前,我有一个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到了Presenterjust中,以便可以访问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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Java-Vertx-发布-订阅模式:在其自己的使用者中发布消息。这是一个坏主意吗?

匿名类实例----这是一个坏主意吗?

捕获OutOfMemoryError是一个坏主意吗?

演示者指示当前活动充实下一个活动;是否违反MVP规则

何时调用活动上下文或应用程序上下文?

活动上下文和应用程序上下文之间的区别

在Android MVP中,演示者应该返回一个值吗?

在C中创建一个通用的“函数指针”联合是一个坏主意吗?

复制一个迭代器是一个坏主意吗?

CloudWatch Agent:批处理大小等于“ 1”-这是一个坏主意吗?

Orion CB可以作为上下文使用者订阅另一个Orion CB吗?

在Node / Express服务器中使用async / await是一个坏主意吗?

getContext()方法在片段中返回null。将上下文保持在变量中是个好主意吗?我想了解利弊

使用Django模板标签向模板添加上下文会是个坏主意吗?(尝试模仿Vue组件的易用性)

获取imageView的主要活动上下文

在MVP设计模式中,一个视图可以有多个演示者吗?

使/ etc目录只读只是一个坏主意吗?

给用户sudo访问perl是一个坏主意吗?

具有活动上下文的Android问题

上下文在另一个活动的片段中不可用

Android活动上下文为空

包装System.Math是一个坏主意吗?

一个活动中基于所选列表视图项的多个上下文菜单

有一个包含外键的复合主键是一个坏主意吗?

活动上下文的特定投射无效异常

活动上下文与应用程序上下文

Kodein 和活动上下文讨论

使 ef core DbContext 瞬态是一个坏主意吗?

具有活动上下文的处理程序类会导致内存泄漏吗?