Android Mosby MVI绑定到演示者中的服务 [英] Android Mosby MVI bind to service in presenter

查看:139
本文介绍了Android Mosby MVI绑定到演示者中的服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Mosby创建一个小型应用程序.

I am creating a small app using Mosby.

该应用程序具有我要绑定的服务.我想正确的位置是在演示者中.但是我真的不知道该怎么做.

The app has a service which I want to bind to. I guess the correct place to do this is in the presenter. But I can't really figure out how to do it.

我要归档的是绑定服务时,我想在其上调用一个方法并将该值推送到视图,以便现在的状态是正确的.

What I want to archive is when the service is bound I want to call a method on it and push that value to the view, so that the state right now is correct.

当服务在事件总线上发送更新时,我也想将其推送到视图.

When the service sends updates on the event bus I want to push that to the view as well.

我在后面的部分中找到了一些示例,但是关于如何在演示者中绑定/解除绑定服务一无所获.

I have found some example on the later part, but nothing about how to bind/unbind the service in the presenter.

我的目的是在活动中创建类似这样的内容:

My stab on it was to create something like this in the activity:

@NonNull
@Override
public MyPresenter createPresenter() {
    return new MyPresenter(new MyService.ServiceHandler() {
            @Override
            public void start(ServiceConnection connection) {
                Intent intent = new Intent(MyActivity.this, MyService.class);
                startService(intent);
                bindService(intent, connection, Context.BIND_AUTO_CREATE);
            }

            @Override
            public void stop(ServiceConnection connection) {
                unbindService(connection);
            }
        });

然后在演示者中执行以下操作:

And then in the presenter do something like this:

private ServiceConnection connection;
private boolean bound;
private MyService service;

public MyPresenter(MyService.ServiceHandler serviceHandler) {
    super(new MyViewState.NotInitialiezedYet());

    this.serviceHandler = serviceHandler;

    connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
           MyService.LocalBinder binder = (MyService.LocalBinder) service;
            service = binder.getService();
            bool isInitialized = service.isInitialized();
            // how do i push isInitialized to view? 

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}

@Override
public void attachView(@NonNull SplashView view) {
    super.attachView(view);
    serviceHandler.start(connection);
    bound = true;
}

@Override
public void detachView(boolean retainInstance) {
    super.detachView(retainInstance);
    if(bound) {
        serviceHandler.stop(connection);
        bound = false;
    }
}

@Override
protected void bindIntents() {
    //Not sure what this would look like?
}

public void onEventInitialized(InitializedEvent event) {
    //how do I push this to the view?
 }   

我在正确的道路上吗?正确的做法是什么?我如何将服务中的值发送到onServiceConnected中的视图,以及如何在onEventInitialized事件总线上获取事件?

Am I on the correct path? What would be the correct way of doing this? How would I send the value from the service to the view in onServiceConnected and when I get events on the event bus in onEventInitialized?

推荐答案

在深入研究可能的实现之前,需要注意以下几点:

A few things to note before we dive into a possible implementation:

  1. 在Mosby中,演示者默认情况下会在屏幕方向上幸存,仅视图已附加/分离.如果在Activity中创建ServiceHandler,则会发生内存泄漏,因为ServiceHandler是在Activity中实例化的一个异常类,因此具有对外部Activity实例的引用.为避免这种情况,您可以使用Application类作为上下文来调用bindService()unbindService().
  2. 服务是业务逻辑,因此最好不要将绑定服务的逻辑放到View层(活动性)中,而应放在它自己的业务逻辑"组件中,即,将该组件称为MyServiceInteractor.
  3. 在业务逻辑中移动该部分时,您可能想知道何时取消绑定/停止该服务.在您的代码中,您已经在Presenter detachView()中完成了此操作.在这种情况下,Presenter现在对业务逻辑内部结构及其工作原理有了一些明确的了解.一个更类似于Rx的解决方案是将服务连接的生命周期与Rx Observable的生命周期"联系起来.这意味着,一旦取消订阅/处置可观察对象,则应关闭服务连接.这也与1完美匹配."Presenter可以在屏幕方向更改后幸存"(并在屏幕方向更改期间保持可观察的订阅有效).
  4. 使用Observable.create()可以轻松地将任何回调/侦听器包装"为Rx Observable.
  5. 我个人觉得使用服务(尤其是有界服务)很麻烦,并在代码中引入了更高的复杂性.没有服务,您可能(也可能无法)达到同样的效果.但这实际上取决于您的具体应用程序/用例.
  1. in Mosby, Presenters survive screen orientation per default and just the View is attached / detached. If you create a ServiceHandler in your Activity you have a memory leak because ServiceHandler is an annonaymous class instantiated in your Activity and therefore has a reference to the outer Activity instance. To avoid that you can use your Application class as context to call bindService() and unbindService().
  2. Services are business logic, so you better put that logic of binding service not into the View layer (Activity), but rather in it's own "business logic" component i.e. let's call this component MyServiceInteractor.
  3. When you move that part in a business logic, you may are wondering when to unbind / stop the service. In your code, you have done it in Presenter detachView(). While that works, Presenter now has some explicit knowledge of business logic internals and how they work. A more Rx alike solution for that would be to tie the lifecycle of service connection to the "lifecycle" of an Rx Observable. That means, service connection should be closed, once the observable is unsubscribed / disposed. This also matches perfectly with 1. "Presenter survive screen orientation changes" (and keep observable subscriptions alive during screen orientation changes).
  4. Any callback / listener can easily be "wrapped" into a Rx Observable by using Observable.create().
  5. I, personally, find Services (especially bounded services) cumbersome to work with and introduce a much higher complexity in your code. You might (or might be not) be able to achive the same without services. But it really depends on your concrete app / use case.

话虽如此,让我们看一下可能的解决方案是什么样的(类似伪代码,可能无法编译):

With that said, let's see how a possible solution could look like (pseudo alike code, may not compile):

public class MyServiceInteractor {

  private Context context;

  public MyServiceInteractor(Context context) {
    this.context = context.getApplicationContext();
  }

  public Observable<InitializedEvent> getObservable() {
    return Observable.create(emitter -> {
      if (!emitter.isDisposed()) {

        MyService.ServiceHandler handler = new MyService.ServiceHandler() {

          @Override public void start(ServiceConnection connection) {
            Intent intent = new Intent(context, MyService.class);
            context.startService(intent);
            context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
          }

          @Override public void stop(ServiceConnection connection) {
            context.unbindService(connection);
          }
        };

        emitter.onNext(handler);
        emitter.onComplete();
      }
    }).flatMap(handler ->
        Observable.create( emitter -> {
          ServiceConnection connection = new ServiceConnection() {
            @Override public void onServiceConnected(ComponentName name, IBinder service) {
              MyService.LocalBinder binder = (MyService.LocalBinder) service;
              MyService service = binder.getService();
              boolean isInitialized = service.isInitialized();
              if (!emitter.isDisposed())
                 emitter.onNext(new InitializedEvent(isInitialized));
            }

            @Override public void onServiceDisconnected(ComponentName name) {
              // you may want to emit an event too
            }
          };

        })
        .doOnDispose({handler.stop()})
    );
  }
}

因此,基本上MyServiceInteractor.getObservable()可以建立到Rx Observable世界的桥梁,并在不遵守Observable的get时停止服务连接.请注意,此代码段可能无法编译.只是为了说明可能的解决方案/工作流程的样子.

So basically MyServiceInteractor.getObservable() creates a bridge to the Rx Observable world and stops the service connection when the observable get's unsubsribed. Please note that this code snippet may not compile. It's just to illustrate how a possible solution / workflow could look like.

然后您的Presenter可能看起来像这样:

Then your Presenter could look like this:

public class MyPresenter extends MviBasePresenter<MyView, InitializedEvent> {
  private MyServiceInteractor interactor;

  public MyPresenter(MyServiceInteractor interactor){
     this.interactor = interactor;
  }

  @Override
  void bindIntents(){
    Observable<InitializedEvent> o = intent(MyView::startLoadingIntent) // i.e triggered once in Activity.onStart()
        .flatMap( ignored -> interactor.getObservable() );

    subscribeViewState(o, MyView::render);
  }
}

因此,这里的主要问题不是很特定于MVI或MVP或MVVM,主要是我们如何将android服务回调包装"到可观察到的RxJava中. 一旦有了这个,其余的就应该很容易了.

So the main question / issue here is not very MVI or MVP or MVVM specific, it's mostly how do we "wrap" the android Service callbacks into a RxJava observable. Once we have this, the rest should be easy.

与MVI唯一相关的是连接点:视图实际上必须触发意图以启动服务连接.这是通过myView.startLoadingIntent()

The only MVI related thing is to connect the dots: The view actually has to trigger an intent to start the service connection. This is done in bindIntents() via myView.startLoadingIntent()

我希望能帮上忙.

这篇关于Android Mosby MVI绑定到演示者中的服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆