Mosby、Conductor 和 Rx 表现不佳
Mosby, Conductor and Rx not playing well
我刚刚开始了一个新的示例项目来测试 Conductor with Mosby。
问题是,在我离开 FirstScreen 或旋转设备后,在 Rx onNext 回调之后,isViewAttached()
总是返回 false,我不明白为什么。
澄清一些片段:
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);
}
困扰我的是,当我第一次打开时,我可以点击菜单项,它就像一个超级按钮,但如果我离开或旋转,描述的问题就会出现。
如有任何帮助,我们将不胜感激!
这不是 Mosby-Conductor 的问题。这个问题是由 dagger 和你注入 presenter 的方式引起的。
您在 onViewBound() 方法中的每个屏幕方向更改时创建并注入一个新的 Presenter 实例。这意味着每个屏幕方向都会创建一个新的演示者。
此外,由于您要为 Presenter 声明自己的字段,因此您没有为 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 的基础 class 正在使用 MvpController.presenter 字段。
https://github.com/sockeqwe/mosby-conductor/blob/master/mvp/src/main/java/com/hannesdorfmann/mosby/mvp/conductor/MvpController.java#L19
有两种解决方案:
不要声明你自己的 presenter 字段,而是做这样的事情(然后它将使用 MvpController.presenter 字段):
@Override
public FirstScreenPresenter createPresenter() {
return DaggerFirstScreenComponent.builder()
.activityComponent(((RootActivity) getActivity()).getActivityComponent())
.firstScreenModule(新的 FirstScreenModule())
.build().presenter();
}
因为 createPresenter() 只会被调用一次,所以这确保了 Presenter 实例只会被创建一次。
如果您想使用自己的现场演示者,那么您还必须在从 MvpLce
扩展的控制器中覆盖 setPresenter() 和 getPresenter()
public class FirstScreen extends BaseController, FirstView, FirstScreenPresenter> implements FirstView {
private static final String MUST_HAVE_NUMBER = "must_have_number";
@Inject
protected FirstScreenPresenter presenter;
@Override
public FirstScreenPresenter getPresenter(){
return 主持人;
}
@Override
public void setPresenter(FirstScreenPresenter p){
this.presenter = p;
}
由于 Mosby 的内部工作方式,它会调用 getPresenter()、createPresenter() 和 setPresenter()。但是,您仍然必须确保不要在 onViewBound() 方法中创建和注入新的 Presenter。
顺便说一句。如果您使用 Activity / Fragments with Mosby 而不是 Conductor
,您也可以 运行 解决这个问题
我刚刚开始了一个新的示例项目来测试 Conductor with Mosby。
问题是,在我离开 FirstScreen 或旋转设备后,在 Rx onNext 回调之后,isViewAttached()
总是返回 false,我不明白为什么。
澄清一些片段:
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);
}
困扰我的是,当我第一次打开时,我可以点击菜单项,它就像一个超级按钮,但如果我离开或旋转,描述的问题就会出现。
如有任何帮助,我们将不胜感激!
这不是 Mosby-Conductor 的问题。这个问题是由 dagger 和你注入 presenter 的方式引起的。
您在 onViewBound() 方法中的每个屏幕方向更改时创建并注入一个新的 Presenter 实例。这意味着每个屏幕方向都会创建一个新的演示者。
此外,由于您要为 Presenter 声明自己的字段,因此您没有为 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 的基础 class 正在使用 MvpController.presenter 字段。 https://github.com/sockeqwe/mosby-conductor/blob/master/mvp/src/main/java/com/hannesdorfmann/mosby/mvp/conductor/MvpController.java#L19
有两种解决方案:
不要声明你自己的 presenter 字段,而是做这样的事情(然后它将使用 MvpController.presenter 字段):
@Override public FirstScreenPresenter createPresenter() { return DaggerFirstScreenComponent.builder() .activityComponent(((RootActivity) getActivity()).getActivityComponent()) .firstScreenModule(新的 FirstScreenModule()) .build().presenter(); }
因为 createPresenter() 只会被调用一次,所以这确保了 Presenter 实例只会被创建一次。
如果您想使用自己的现场演示者,那么您还必须在从 MvpLce
扩展的控制器中覆盖 setPresenter() 和 getPresenter()public class FirstScreen extends BaseController, FirstView, FirstScreenPresenter> implements FirstView {
private static final String MUST_HAVE_NUMBER = "must_have_number"; @Inject protected FirstScreenPresenter presenter;
@Override public FirstScreenPresenter getPresenter(){ return 主持人; }
@Override public void setPresenter(FirstScreenPresenter p){ this.presenter = p; }
由于 Mosby 的内部工作方式,它会调用 getPresenter()、createPresenter() 和 setPresenter()。但是,您仍然必须确保不要在 onViewBound() 方法中创建和注入新的 Presenter。
顺便说一句。如果您使用 Activity / Fragments with Mosby 而不是 Conductor
,您也可以 运行 解决这个问题