【问题标题】:Mosby, Conductor and Rx not playing wellMosby、Conductor 和 Rx 打得不好
【发布时间】:2016-05-17 16:26:08
【问题描述】:

我刚刚开始了一个新的示例项目,以使用 Mosby 测试 Conductor。 问题是,在我离开 FirstScreen 或旋转设备之后,在 Rx onNext 回调之后,isViewAttached() 总是返回 false,我不明白为什么。 一些需要澄清的sn-ps:

public abstract class MvpLceRxPresenter<V extends MvpLceView<M>, M> extends MvpBasePresenter<V>
        implements MvpPresenter<V> {

    protected Subscriber<M> subscriber;

    protected void unsubscribe() {
        if (subscriber != null && !subscriber.isUnsubscribed()) {
            subscriber.unsubscribe();
        }

        subscriber = null;
    }

    public void subscribe(@NonNull UseCase useCase, @Nullable Bundle bundle, final boolean pullToRefresh) {
        if (isViewAttached()) {
            getView().showLoading(pullToRefresh);
        }

        useCase.unsubscribe();

        subscriber = new Subscriber<M>() {
            private boolean ptr = pullToRefresh;

            @Override
            public void onCompleted() {
                MvpLceRxPresenter.this.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                MvpLceRxPresenter.this.onError(e, ptr);
            }

            @Override
            public void onNext(M m) {
                MvpLceRxPresenter.this.onNext(m);
            }
        };

        if (useCase instanceof DynamicUseCase) {
            DynamicUseCase dynamicUseCase = (DynamicUseCase) useCase;
            dynamicUseCase.execute(subscriber, bundle);
        } else {
            useCase.execute(subscriber);
        }
    }

    public void subscribe(Observable<M> observable, final boolean pullToRefresh) {
        if (isViewAttached()) {
            getView().showLoading(pullToRefresh);
        }

        unsubscribe();

        subscriber = new Subscriber<M>() {
            private boolean ptr = pullToRefresh;

            @Override
            public void onCompleted() {
                MvpLceRxPresenter.this.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                MvpLceRxPresenter.this.onError(e, ptr);
            }

            @Override
            public void onNext(M m) {
                MvpLceRxPresenter.this.onNext(m);
            }
        };

        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);

    }

    protected void onCompleted() {
        if (isViewAttached())
            getView().showContent();
        unsubscribe();
    }

    protected void onError(Throwable e, boolean pullToRefresh) {
        if (isViewAttached())
            getView().showError(e, pullToRefresh);
        unsubscribe();
    }

    protected void onNext(M data) {
        if (isViewAttached())
            getView().setData(data);
    }

    @Override
    public void detachView(boolean retainInstance) {
        super.detachView(retainInstance);
        if (!retainInstance)
            unsubscribe();
    }

我的主持人:

@DaggerScope(FirstScreenComponent.class)
public class FirstScreenPresenter extends MvpLceRxPresenter<FirstView, List<Contact>> {

    private final GetContacts getContacts;

    @Inject
    public FirstScreenPresenter(GetContacts getContacts) {
        this.getContacts = getContacts;
    }

    public void fetchContacts(boolean mustHaveNumber) {
        Bundle bundle = new Bundle(1);
        bundle.putBoolean("mustHaveNumber", mustHaveNumber);

        subscribe(this.getContacts, bundle, false);
    }
}

还有 FirstScreen 的某些部分:

@Override
    public void onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        swapNumberOnlyText(menu.findItem(R.id.number_only));
    }

    private void swapNumberOnlyText(MenuItem item) {
        if(mustHaveNumber)
            item.setTitle("Todos contatos");
        else
            item.setTitle("Somente com número");
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        if(item.getItemId() == R.id.number_only){
            mustHaveNumber = !mustHaveNumber;
            this.presenter.fetchContacts(mustHaveNumber);
            swapNumberOnlyText(item);
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
@Override
    public List<Contact> getData() {
        return adapter.getContacts();
    }

    @Override
    public void setData(List<Contact> data) {
        adapter.setContacts(data);
    }

    @Override
    protected String getErrorMessage(Throwable e, boolean pullToRefresh) {
        return null;
    }

    @NonNull
    @Override
    public LceViewState<List<Contact>, FirstView> createViewState() {
        return new RetainingLceViewState<>();
    }

    @NonNull
    @Override
    public FirstScreenPresenter createPresenter() {
        return presenter;
    }

    @Override
    public void loadData(boolean pullToRefresh) {
        this.presenter.fetchContacts(mustHaveNumber);
    }

让我烦恼的是,当第一次打开时,我可以点击菜单项,它就像一个魅力,但如果我离开或旋转,描述的问题就开始了。

任何帮助将不胜感激!

【问题讨论】:

  • 仅针对遇到相同问题的其他人:所描述的某些行为是期望的行为,屏幕方向问题可能是一个错误。见github.com/sockeqwe/mosby/issues/136之后我会在这里发布最终解决方案

标签: android mvp mosby


【解决方案1】:

这不是莫斯比导体问题。这个问题是由 dagger 和你注入 Presenter 的方式引起的。

您正在 onViewBound() 方法中的每个屏幕方向更改时创建并注入一个新的 Presenter 实例。这意味着每个屏幕方向都会创建一个新的演示者。

此外,由于您为演示者声明了自己的字段,因此您没有为演示者使用 MvpLceViewStateController 字段。见:https://github.com/sockeqwe/mosby-conductor/blob/master/mvp/src/main/java/com/hannesdorfmann/mosby/mvp/conductor/MvpController.java#L19

Mosby 内部工作如下:

if (getPresenter() == null) {
     setPresenter(  createPresenter() ); 
}

在 MvpController 中(您从 getPresenter() 和 setPresenter() 扩展的 MvpViewStateLceController 的基类正在使用 MvpController.presenter 字段。 https://github.com/sockeqwe/mosby-conductor/blob/master/mvp/src/main/java/com/hannesdorfmann/mosby/mvp/conductor/MvpController.java#L19

有两种解决方案:

  1. 不要声明你自己的presenter字段,而是做这样的事情(然后它将使用MvpController.presenter字段):

    @覆盖 公共 FirstScreenPresenter createPresenter() { 返回 DaggerFirstScreenComponent.builder() .activityComponent(((RootActivity) getActivity()).getActivityComponent()) .firstScreenModule(新的 FirstScreenModule()) .build().presenter(); }

这确保了一个演示者实例只会被创建一次,因为 createPresenter() 只会被调用一次。

  1. 如果您想使用自己的字段演示器,那么您还必须在从 MvpLce 扩展的控制器中覆盖 setPresenter() 和 getPresenter()

    公共类 FirstScreen 扩展 BaseController、FirstView、FirstScreenPresenter> 实现 FirstView {

    private static final String MUST_HAVE_NUMBER = "must_have_number";
    @Inject
    protected FirstScreenPresenter presenter;
    

    @覆盖 公共 FirstScreenPresenter getPresenter(){ 返回演示者; }

    @Override
    public void setPresenter(FirstScreenPresenter p){
       this.presenter = p;
    }
    

由于 Mosby 内部的工作方式,它调用 getPresenter()、createPresenter() 和 setPresenter()。但是,您仍然必须确保不要在 onViewBound() 方法中创建和注入新的 Presenter。

顺便说一句。如果您将 Activity / Fragments 与 Mosby 一起使用而不是 Conductor,您也可能会遇到此问题

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-08
    • 2011-06-15
    • 2019-06-16
    相关资源
    最近更新 更多