
Yuraと共同開発したMoxyフレームワークは、Android開発で広く使用されていました。 Activity、Fragment、Viewを操作するときにMVPパターンの実装を提供し、ライフサイクルの「コールバック地獄」をプレゼンターから完全に分離します。
この動作は、ViewStateエンティティにより実装されます。ViewStateエンティティは、PresenterとViewの間でメソッド呼び出しをプロキシし、特別な戦略に基づいてそれらの一部をキューに格納します。 ビューを再作成すると、すべてのメソッドが呼び出されるわけではなく、現在キューにあるメソッドのみが呼び出されます。
この記事では、「箱から出してすぐに」提供される戦略がどのように機能するか、どの場合にそれぞれを適用すべきかについて説明します。 パート2で 、内部からの作業戦略のメカニズムについて説明し、カスタム戦略を記述します。
モクシーの原理
プレゼンターのコマンドキュー(ビューで呼び出されるメソッド)は、再作成後のビューの状態を決定します。
キューへのコマンドの保存は、Viewインターフェイスメソッドで指定された戦略に従って行われます。 ViewState自体は、Viewインターフェイスとそれに使用される注釈に基づいて、コンパイル時に生成されます。
戦略を不正確に使用すると、アプリケーションの動作が不正確になり、メモリがオーバーフローする可能性があります。
ビューにアクティブ状態の場合にのみコマンドを適用できます-getMvpDelegate()。OnAttach()およびgetMvpDelegate()。 Activity 、 Fragment、またはカスタムandroid.view.Viewの OnDetach()の呼び出しの間隔内。

回路デバイス
戦略の仕事を説明するために、次のスキームを使用します。

X軸はタイムライン(左から右への時間方向)です。
円はチームを示し、同じチーム(それぞれ、同じ戦略を持っている)は同じ色と番号の円で示されます。
プレゼンターは、 getViewState()。DoSomething(args)メソッドを使用してプレゼンターが開始するコマンドを開始します。 発表者が実行したチームには、特に断りのない限り、このスキームで考慮されている戦略があります。
ViewState軸は、後で再作成されるときにビューに適用されるコマンドキューの状態を表します。 コマンドは下から上に順番に適用されます。 ViewStateの初期状態は、軸の左端の位置に表示されます。
ビュー軸は、ビューに適用されるコマンドのセットです。 方向変更アイコンは、ビューを再作成することを意味します。 ビュー軸は、ダイアグラムの中央から開始できます-これは、この時点からビューがアクティブ状態になったことを意味します。
すぐに使える戦略
Moxyはデフォルトで以下の戦略を提供します:
AddToEndStrategy-コマンドを実行し、コマンドをキューの最後に追加します
AddToEndSingleStrategy-コマンドを実行し、それをキューの最後に追加し、以前のインスタンスをすべて削除します
SingleStateStrategy-コマンドを実行し、キューをクリアしてコマンドを追加します
SkipStrategy-コマンドを実行する
OneExecuteStrategy-できるだけ早くコマンドを実行する
それらがどのように機能し、いつ使用するかを詳しく見てみましょう。
AddToEndStrategy
AddToEndStrategyは、キューの最後にコマンドを追加する最も単純な戦略です。

プレゼンターがAddToEndStrategyストラテジーでコマンド(2)を呼び出す場合:
- コマンド(2)は、ViewStateキューの最後に追加されます。
- コマンド(2)がアクティブ状態の場合、ビューに適用されます
ビューを再作成するとき:
- ViewStateコマンドは順次Viewに適用されます
AddToEndSingleStrategy
この戦略は、ViewStateキューにこの戦略でマークされた各コマンドのインスタンスが1つしか存在できないという点で、AddToEndStrategyと異なります。
長期使用
カスタムコマンドを使用する場合、キューの状態を変更できます(たとえば、使用可能なコマンドのいずれかを複製します)が、これは細心の注意を払って行う必要があります)

プレゼンターがAddToEndSingleStrategy戦略でコマンド(2)を呼び出す場合:
- コマンド(2)は、ViewStateキューの最後に追加されます。
- コマンド(2)がすでにキューにあった場合、古いコマンドはキューから削除されます
- コマンド(2)がアクティブ状態の場合、ビューに適用されます
ビューを再作成するとき:
- ViewStateコマンドは順次Viewに適用されます
SingleStateStrategy
この戦略は、原則としてAddToEndSingleStrategyに似ています。 違いは、この戦略を持つコマンドがViewStateに入ると、コマンドキューが完全にクリアされることです。

プレゼンターがSingleStateStrategy戦略でコマンド(1)を呼び出す場合:
- コマンドキューは完全にクリアされます
- コマンド(1)がViewStateキューの最後に追加されます。
- コマンド(1)がビューに適用されます(アクティブ状態の場合)
ビューを再作成するとき:
- ViewStateコマンドは順次Viewに適用されます
スキップ戦略
スキップ戦略はViewStateスタックを変更しません。 このコマンドは、アクティブ状態の場合にのみビューに適用されます。

アクティブ状態のビューの存在に応じて、チームの動作は異なります。
a)ビューがアクティブ状態の場合:
プレゼンターがSkipStrategy戦略を使用してコマンド(4)を呼び出したときに、ビューがアクティブ状態の場合:
- コマンドはビューに適用されます
- チームのラインナップは変更されていません。
ビューを再作成するとき:
- ViewStateコマンドは順次Viewに適用されます
b)アクティブ状態でビューがない場合 :
プレゼンターがSkipStrategyストラテジーでコマンド(4)を呼び出すと、アクティブ状態のViewがない場合 :
- コマンドはビューには適用されません
- チームのラインナップは変更されていません。
ビューを再作成するとき:
- ViewStateコマンドは順次Viewに適用されます
OneExecuteStrategy
この戦略はSkipStrategyに非常に似ています。 違いは、アクティブ状態でビューが存在しない場合、コマンドが表示されるのを待ってから、一度だけ適用されることです。

アクティブ状態のビューの存在に応じて、チームの動作は異なります。
a)ビューがアクティブ状態の場合:
プレゼンターがOneExecuteStrategyストラテジーでコマンド(4)を呼び出したときに、ビューがアクティブ状態の場合(動作はSkipStretegyに完全に似ています):
- コマンドはビューに適用されます
- チームのラインナップは変更されていません。
ビューを再作成するとき:
- ViewStateコマンドは順次Viewに適用されます
b)アクティブ状態でビューがない場合 :
プレゼンターがOneExecuteStrategyストラテジーでコマンド(4)を呼び出すと、アクティブ状態のViewがない場合 :
- コマンド(4)は、ViewStateキューの最後に追加されます。
ビューがアクティブな状態で表示される場合:
- ViewStateコマンドは順次Viewに適用されます
- コマンド(4)が実行されると、コマンドキューから削除されます。 Viewコマンド(4)のその後の再作成は呼び出されません。
メソッドの戦略の欠如
メソッドの戦略が明示的に示されていないため、インターフェイス全体の戦略とデフォルトの戦略家に基づく自動出力が行われます。 現在、デフォルトの戦略はAddToEndStrategyです。
コマンドスコープ
このセクションでは、コマンドの典型的なユースケースを収集しました。
AddToEndStrategy-複数のコマンドを順番に適用する必要がある場合に使用します。これらのコマンドは、ビューを再作成するときに再適用する必要があります。
例:組織画面は、一般情報、プロモーション、購入履歴の3つのブロックで構成されています。 これらのパーツはダウンロードして、画面に非同期で表示できます。 再構成後、すべてのブロックに情報を表示することが重要です。 すべてのメソッドは、AddToEndStrategy戦略でマークされます。
AddToEndSingleStrategy-ビューを複数回再作成するときにコマンドを使用する必要がある場合に使用します。
例:画面にロードインジケーターがあり、View:toggleLoading(visible:Boolean)メソッドを呼び出すことでその可視性が変更されます。
ネタバレ:以下のコード例はKotlinで提供されています。
このメソッドには、AddToEndSingleStrategy戦略があります。
SingleStrategy-以前に働いていたチームの結果が私たちにとって重要でない場合に使用されます。
例:プロファイル画面のデータはネットワークからダウンロードされます。 画面には、ロード、データ表示、ダミー画面の3つの相互排他的な状態があります。 したがって、ViewにはshowLoading()、showData()、およびshowStub()メソッドがあります。 3つのチームすべてに、SingleStateStrategy戦略があります。
SkipStrategy-アクティブな状態のビューがある場合にのみ、今すぐアクションを実行する必要がある場合に使用します。
例1:ユーザーデータを編集する画面があります。 [保存]をクリックすると、toggleLoading(show = true)コマンドを呼び出してダウンロードインジケーターが表示されます。 ダウンロードが失敗した場合、画面はtoggleLoadingコマンドで初期状態に戻り(show = false)、showLoadingError()コマンドでエラー情報とともにSnackBarが表示されます。 最後のチームには、SkipStrategy戦略があります。 実行結果はアクティブな画面でのみ必要であり、構成を変更するときに保存しないでください。
例2:何らかのユーザーアクションの後のアニメーションの開始。 コマンドを1回だけ使用することは理にかなっており、アクティブビューがない場合は、コマンドをキューに保存する必要はありません。
OneExecuteStrategy-ビューが最初にアクティブ状態になったときに何らかのアクションを実行する必要がある場合に使用されます。
例1:次の画面を開きます。 新しいActivity、Fragment、またはFragmentDialogの起動は、この戦略を使用して便利に実行されます。 この場合、1回の起動が保証されます。
明確化
より正確には、最初の添付ビューでの起動を保証します。 何らかの理由でビューがアクティブ状態ではなく、アクティブ状態にならない場合、コマンドは呼び出されません。 この状況は理論的には可能ですが、これを覚えておく必要があります。
OneExecuteStrategyとSkipStrategyは非常によく似ています。それらを使用するときは注意してください。
戦略による問題を回避する方法は?
戦略を使用すると、次の2種類の問題が発生する場合があります。
問題1.キュー内の余分なチーム
デフォルト戦略であるAddToEndStrategy のみを使用する場合、キューは徐々に増加します。 これにより、
- ビューをプレゼンターにアタッチするときにUIをフリーズする
- メモリオーバーフロー
解決策:
戦略を設定して、チームのキューのサイズを最小限に抑えるようにしてください。
問題2.ビューを再作成するときの不適切な状態
多くの場合、アプリケーションは縦向きのみを必要とし、その結果、開発中に修正されますが、ビューは向きの変更だけでなく、他の構成変更でも再作成されます。
解決策:
開発時には、アプリケーションの向きを変更する機能を残します。 これにより、戦略の正しい適用だけでなく、デバイスの構成を変更することによるその他の副作用も制御できます。
おわりに
Moxyの戦略はかなり柔軟なツールであり、不正確な使用は論理エラーにつながる可能性があります。 この記事がMoxyの使用をより透明で明白にすることを願っています。
パート2では、戦略が徹底的に機能するメカニズムと、カスタム戦略を作成する方法を調べます。
コードを常に設計どおりに実行してください!
ライブラリMoxy Xanderblinovおよびsennecoの著者から