2017-03-27 6 views
1

私はMosbyを使用して小さなアプリを作成しています。Android Mosby MVIはプレゼンターのサービスにバインドします

私はバインドしたいサービスがあります。私はこれを行う正しい場所が発表者にあると思います。しかし、私は本当にそれを行う方法を把握することはできません。

私がアーカイブしたいのは、サービスがバインドされているときです。メソッドを呼び出してその値をビューにプッシュして、現在の状態が正しいようにしたいとします。

サービスがイベントバス上でアップデートを送信するとき、私はそれをビューにもプッシュしたいと思います。

私は、後の部分でいくつかの例を見つけましたが、プレゼンターでサービスをバインド/アンバインドする方法については何も見つかりませんでした。

その上に私の刺しが活動中でこのようなものを作成することでした。

@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); 
      } 
     }); 

をそしてプレゼンターにこのような何かを:

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のイベントバスでイベントを取得したらどうなりますか?

答えて

4

我々は可能な実装に飛び込む前に、注意すべきいくつかのこと:モスビーで

  1. 、プレゼンターはデフォルトごとに画面の向きを生き残るためには、単にビューが外さ/装着されています。 ServiceHandlerActivityに作成した場合、ServiceHandlerはアクティビティでインスタンス化されるアノニマスクラスであるため、外部アクティビティインスタンスへの参照があるため、メモリリークが発生します。これを回避するには、Applicationクラスをコンテキストとして使用してbindService()unbindService()を呼び出すことができます。
  2. サービスはビジネスロジックなので、バインディングサービスのロジックをViewレイヤー(アクティビティ)に入れるのではなく、独自の "ビジネスロジック"コンポーネント、つまりこのコンポーネントをMyServiceInteractorと呼ぶことにします。
  3. ビジネスロジックでその部分を移動すると、いつサービスをバインド/停止するのか不思議に思うかもしれません。あなたのコードでは、Presenter detachView()でそれを行いました。それが機能している間、Presenterはビジネスロジックの内部構造とその働きを明示的に理解しています。より多くのRxのソリューションは、サービス接続のライフサイクルをRx Observableの「ライフサイクル」に結びつけることです。つまり、オブザーバブルが登録解除/廃棄されると、サービス接続を閉じる必要があります。これは完全に1にもマッチします。「プレゼンターは画面の向きの変化に耐えます」(画面の向きの変更中に観察可能なサブスクリプションが生き続ける)
  4. すべてのコールバック/リスナーは、Observable.create()を使用してRx Observableに簡単に「ラップする」ことができます。
  5. 私は、個人的には、サービス(特に有界サービス)が扱いにくく、コードの複雑性が非常に高くなることがわかりました。あなたはサービスなしで同じことをすることができるかもしれない(そうでないかもしれない)かもしれない。しかし、それは実際にあなたの具体的なアプリケーション/ユースケースに依存します。

ということで、のは、可能な解決策は次のようになります方法を見てみましょう(擬似似コード、コンパイルされない場合があります):

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の観察可能な世界へのブリッジを作成し、停止します観測可能なgetのunsubsribedときにサービス接続。このコードスニペットはコンパイルできないことに注意してください。可能なソリューション/ワークフローがどのように見えるかを説明するだけです。

次に、あなたのPresenterは次のようになります。

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ではありませんRxJavaは観測可能です。 これを取得したら、残りの部分は簡単にする必要があります。

唯一のMVI関連の問題は、ドットを接続することです。実際には、ビューはサービス接続を開始する意図を引き起こす必要があります。これはbindIntents()で行われます。myView.startLoadingIntent()

私は助けてくれることを望みます。

+0

迅速な回答をいただきありがとうございます。また、すべてのブログ記事に感謝します! 私はこれを試してみます。 –

+0

オクラ、私はそれの周りに私の頭をラップしようとしたが、私は理解していない。 私はストリーミングオーディオであるバックグラウンドサービスを持っています。私はバインドを介してサービスを一時停止し、次に呼び出すことができる必要があります。サービスからは、次のトラック、進捗状況などのイベントが発生します。 現在、アクティビティとサービスの間にはイベントバスがあります。 サービスから観測可能な方が良いですか? インタラクタは、UIから次のトラックイベントを送信してサーバーに送信し、新しいトラックモデルを返すことができるように見えますか? 私はRxを初めて使っているので、新しいことがたくさんあります。 –

+0

次に、オーディオプレイヤーをバックグラウンドで実行し続けるサービスが唯一必要です。 'Observable getStateObservable()'メソッドを使って 'AudioPlayer'クラスを作成します(PLAYING song XYまたはPAUSEDのような現在のプレーヤー状態を返します)。 'Presenter'によって使用される' AudioPlayerInteractor'は、その状態をサブスクライブしてビューに表示します。あなたの 'Service'は' AudioPlayerInteractor'と同じ 'AudioPlayer'インスタンスを共有しますが、' audioPlayer.pause() 'のようにコントロールします。サービスをバインドする必要はありません。Android Intentsを使用して 'Service'と通信するだけです。 – sockeqwe

関連する問題