水の記事で、私はすでにAndroidクライアントをプロジェクトの計画クライアントにすべきだと書いています。大勢の人がアクセスでき、軽量、機能的、美しく、高速です(アプリケーションではなく、夢です!)。 プラットフォームを選択する理由ですべてが明確な場合、それを基に上記のすべての要件を実装する方法がすべてとは程遠いものでした。
以前は、Androidの開発は行っていませんでした。そのため、私にとってかなり貴重な情報源は次のとおりでした。
- 本「Android Programming:The Big Nerd Ranch Guide」 ( 出版社「Peter」からの翻訳に精通していることで、以前から存在する原則が確認されました。「オリジナルを読むことができます-オリジナルを読んでください」)。
- Android用のGoogle開発サイト。
- 出版社O'Reillyの本「Efficient Android Threading」。
- Yandexプロジェクト「Mobilization」のビデオ。
Androidのアーキテクチャとそれらのコンポーネントの相互作用を使用して、示された質問のソースを調べた後、それ以上はありませんでした。 ただし、重要な疑問が1つ残っていました。アプリケーション自体の構造はどうなるのでしょうか。 いくつかの例とプロトタイプは、機能の成長に伴い、すべてがすぐに「ヌードル」に変わり始めたことを示しました。
- Androidオブジェクト(Activity、Preferences、TextViewなど)を操作するロジックは、ビジネスロジックと混在していました。
- ストレージオブジェクトは、インターフェイス構築コードで取り上げられました。
- ネイティブAndroidオブジェクトを操作し、 Robolectricインスタンスに置き換える必要があるため、ユニットテストは地獄になりました。
- 非同期コードの検証は、デバイスまたはエミュレーターでのみ可能でした(原則: "launched-checked-repeated"に従って)。
一歩後退して見直す必要があることが明らかになりました:Androidは数年前から存在し、このプラットフォーム用のコードを長い間開発している人々がいます。大規模な開発プロジェクトがあります。したがって、確立されたプラクティスに関する情報を入手できる場所があります。
Androidアプリケーションに適したアーキテクチャを見つけるための主な基準は次のとおりです。
- 開発したコードとそのコンポーネントの簡単なテスト容易性-テストしやすいコードは、バグを作成したりアプリケーションを「ダンプ」したりすることなく、開発と変更が簡単です。
- 弱いコンポーネントの凝集。アプリケーション/コンポーネントの部分は、(少なくともしばらくの間)超強力な相互作用を必要とせずに、異なる開発者が開発できます。
この検索により、YouTubeで興味深いビデオが見つかりました。 「テストコードを書いています」 (モバイル開発に関するメビウス会議でのEvgeny Matsyuk(a)によるスピーチの録音)(非常に多くがありました!) 実装のために、追加のリソースとツールを検討する必要がありました。
- オリジナルの記事「The Clean Architecture」では、Habr「Misconceptions of Clean Architecture」に関する優れた説明記事の存在に注意する必要があります。
- 「Architecting Android ... The clean way?」および「Architecting Android ... The evolution」の記事を掲載したFernando Cejasブログ。
- ダガー2 ;
- RxJava
RxJavaの研究と併せてこれらのプラクティスを使用したプロトタイプの開発には多くの時間がかかりましたが、しばらくして最初のプロトタイプの準備が整いました。 それの特徴は、新しい画面を追加するときに作成されるインターフェースとクラスの数が非常に多いことでした:3つのインターフェースと3つのクラス(アクティビティ/フラグメントとそのインターフェース、プレゼンターとそのインターフェース、インターアクターとそのインターフェース)-オーバーエンジニアリングの典型的な例です。 正式には、現時点では何も変わっていませんが、これはメリットの裏返しだと思います。 しかし、出力では、疎結合構造を持つ簡単にテストされたアプリケーションを取得します。
実装
Habrの記事「クリーンアーキテクチャのエラー」から、クリーンアーキテクチャのメモリコンポーネントの更新について説明します。
選択したアーキテクチャの各Androidコンポーネントと要素を次の表に示します。
クラス | レベル | 実装可能なインターフェース | 予定 |
実装アクティビティ/フラグメント(XXXX_Activity / XXXX_Fragment) | UI | I_XXXX_View | Android要素を使用したアクションの実際の実装:プロパティの変更、コールバックの受信、サービスの開始、Android APIの操作 |
XXXX_PresenterImpl | UI | I_XXXX_Presenter | プレゼンテーションレベルのアクション、プレゼンテーションロジックの調整-インターフェイスメソッドI_XXXX_View、I_XXXX_Interactorの呼び出し |
XXXX_InteractorImpl | ビジネス/ユースケース | I_XXXX_Interactor | メインアプリケーションロジックの実装、インターフェイスメソッドI_XXXX_Repositoryの呼び出し |
XXXX_RepositoryImpl | データ/リポジトリ | I_XXXX_Repository | データソース、外部API、Androidネットワークとデータベース、ContentProvidersなどとの直接対話を実装する |
相互作用の組織
コンポーネントとデータ転送の相互作用は、Androidアプリケーションのユーザーが入力するよりも多くのデータを受信するという事実を考慮して編成されます。 したがって:
- 信号は、通常の同期呼び出しを介してより深い層に送信されます(ボタンを押した/スクロール/入力したデータ->メソッドと呼ばれます)。
- 下位層からのデータの受信は、非同期Rxストリームによって編成されます(呼び出しを受信->結果と共にデータを送信)。
- 同期化されたデータの取得は最小限に抑えられます(ほとんどが初期化コードおよびその他の補助画面とまれな画面です)。
パッケージ構成
Fernando Cejasによる元の記事では、「レベル別」と「機能別」を編成するための2つのオプションがありました。私は自分で組み合わせたアプローチを開発しました。
- 最初はレベル(UI、データ、ビジネス)ごとに
- メイン画面の「ui」で「news_watcher」、「news_tape」など。
- 「データ」および「ビジネス」-主要エンティティ「news_header」、「news_article」などによる
興味深い機能は、インタラクターの数が「メイン画面の数」+「エンティティの数」に等しくなったという事実でした。トリッキーなデータ収集を編成する必要がある場合があります(たとえば、異なるソースからの組み合わせ)。必要とされるすべてのインターアクターは完全に望まれていませんでした。 さらに、インターアクターは単一のコピーで使用されるという事実を考慮して、メソッドの実行に重要な特定の状態を保存できるため、次のように実装しました:画面のインターアクターは適切なメソッドのエンティティのインターアクターになります(委任の外観につながります)インタラクター画面のメソッド)。
初期化
- アクティビティ/フラグメント:
- Androidによって作成されます(非シングルトン、スコープ付き)
- ビュー#onCreate()メソッドで初期化されます(フラグメントのフラグメント#onViewCreated()で完了)
- D / Dagger2に内部的に割り当てられたプレゼンター
- プレゼンター内部の初期化は、指定されたメソッドで実行されます(View#onCreate()、Fragment#onViewCreated()for Fragment)
- 発表者:
- Dagger2を介して作成(非シングルトン、スコープ付き)
- ビューは、プレゼンターを介してビュー自体によって割り当てられます#bindView()
- メソッドPresenterで初期化されます#initializePresenter()Viewによって呼び出されます(Viewの初期化後、適切なタイミングで初期化を行う必要があるため)
- Dagger2を介して内部的に割り当て/初期化されたインタラクター
- Interactor-> Presenter関係の作成は、Presenter#initializePresenter()メソッドで実行されます(Rx初期化のための他のInteractorメソッドを使用)
- インタラクター:
- Dagger2を介して作成(シングルレット、スコープなし)
- Dagger2を介して初期化されます(インタラクター#initializeInteractor)
- Dagger2を介して割り当て/初期化されたリポジトリ
- リポジトリ:
- Dagger2を介して作成(シングルレット、スコープなし)
- Dagger2を介して初期化(リポジトリ#initializeRepository)
テストアプローチ
テストに関して-革新的なものはありません:
- UIレベル-JUnit + Mockito + Robolectric
- ビジネスレベル-JUnit + Mockito
- データレベル-JUnit + Mockito
ご清聴ありがとうございました!