JavaRush /Java Blog /Random-JA /小さなお子様向けの Android の MVP
Алексей Дмитревский
レベル 31
Москва

小さなお子様向けの Android の MVP

Random-JA グループに公開済み
私が Android 開発者としての道を歩み始めたとき、「モバイル アプリケーション アーキテクチャ」という言葉を聞いて深い当惑を感じました。Google とハブレに関する記事のせいで私はさらに憂鬱になりました。本を見ても何も見えませんでした。この記事を読んでいるあなたは、すでにこの図を何度か研究し、何が起こっているのかを理解しようとしていると思います。 小さなお子様向けの Android の MVP - 1私の意見では、モバイル開発におけるアーキテクチャ アプローチを理解する上での問題は、アーキテクチャ自体の抽象性にあります。各開発者は、特定のパターンを正しく実装する方法について独自のビジョンを持っています。MVP 実装の多かれ少なかれまともな例がインターネットの英語圏の分野で見つかりましたが、これは驚くべきことではありません。何が何であるかを簡単に見て、例に移りましょう。 モデル- データ レベル。私は「ビジネス ロジック」という用語を使いたくないので、アプリケーションではそれをリポジトリと呼び、データベースやネットワークと通信します。 ビュー— 表示レベル。タンバリンを使って踊ったり、ライフサイクルを操作したりしたくない場合は、アクティビティフラグメント、またはカスタム ビューになります。最初はすべての Android アプリケーションがMVC構造に従属しており、コントローラーがアクティビティまたはフラグメントであることを思い出してください。 プレゼンターはビューとモデルの間のレイヤーです。View は発生したイベントを送信し、プレゼンターは必要に応じてイベントを処理し、Model にアクセスして、レンダリングのためにデータを View に返します。Android と具体的な例に関連して、重要な部分である契約に焦点を当てます。これは、上記のコンポーネント間のすべての対話を記述するインターフェイスです。 理論的な部分を要約すると、次のようになります。
  • ビューはプレゼンターについて知っています。
  • 発表者はビューとモデル (リポジトリ) について知っています。
  • 単独でモデル化します。
  • 契約はそれらの間の相互作用を管理します。
実際、この例自体は、実験を簡単にするために、ボタンをクリックしてデータベースから行をロードし、 TextView に表示します。たとえば、データベースには市内の最高のレストランのリストが含まれています。 コントラクトから始めましょう: インターフェイスを作成しましょうMainContract:
public interface MainContract {
    interface View {
        void showText();
    }

    interface Presenter {
        void onButtonWasClicked();
        void onDestroy();
    }

    interface Repository {
        String loadMessage();
    }
}
現時点では、将来のアプリケーションの 3 つのコンポーネントとそれらが何を行うのかを強調するだけです。 次にリポジトリについて説明します。
public class MainRepository implements MainContract.Repository {

    private static final String TAG = "MainRepository";
    @Override
    public String loadMessage() {
        Log.d(TAG, "loadMessage()");
        /** Здесь обращаемся к БД or сети.
         * Я специально ничего не пишу, чтобы не загромождать пример
         * DBHelper'ами и прочими не относяшимеся к теме an objectми.
         * Поэтому я буду возвращать строку Сосисочная =)
         */
        return "Сосисочная у Лёхи»;
    }
}
データのロードとアンロードを行うだけで、すべてが明確になります。 次はプレゼンターです。
public class MainPresenter implements MainContract.Presenter {
    private static final String TAG = "MainPresenter";

    //Компоненты MVP applications
    private MainContract.View mView;
    private MainContract.Repository mRepository;

    //Сообщение
    private String message;


    //Обрати внимание на аргументы конструктора - мы передаем экземпляр View, а Repository просто создаём конструктором.
    public MainPresenter(MainContract.View mView) {
        this.mView = mView;
        this.mRepository = new MainRepository();
        Log.d(TAG, "Constructor");
    }

    //View сообщает, что кнопка была нажата
    @Override
    public void onButtonWasClicked() {
        message = mRepository.loadMessage();
        mView.showText(message);
        Log.d(TAG, "onButtonWasClicked()");
    }

    @Override
    public void onDestroy() {
        /**
         * Если бы мы работали например с RxJava, в этом классе стоило бы отписываться от подписок
         * Кроме того, при работе с другими методами асинхронного андроида,здесь мы боремся с утечкой контекста
         */

        Log.d(TAG, "onDestroy()");
    }
}
タンバリンを使ったダンスとライフサイクルについて書いたのを覚えていますか? プレゼンターはビューが存在する限り存続します。複雑なユーザー シナリオを開発する場合は、必要なものをすぐに理解できるように、プレゼンター内のすべてのビュー コールバックを複製し、適切な時点で呼び出して、アクティビティ/フラグメントのライフサイクルを複製することをお勧めします。現在「インターレイヤー」に滞留しているデータを処理する必要があります。 そして最後に、以下を表示します。
public class MainActivity extends AppCompatActivity implements MainContract.View {

    private static final String TAG = "MainActivity";

    private MainContract.Presenter mPresenter;

    private Button mButton;

    private TextView myTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Создаём Presenter и в аргументе передаём ему this - эта Activity расширяет интерфейс MainContract.View
        mPresenter = new MainPresenter(this);

        myTv = (TextView) findViewById(R.id.text_view);
        mButton = (Button) findViewById(R.id.button);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.onButtonWasClicked();
            }
        });
        Log.d(TAG, "onCreate()");
    }

    @Override
    public void showText(String message) {
        myTv.setText(message);
        Log.d(TAG, "showMessage()");
    }

    //Вызываем у Presenter метод onDestroy, чтобы избежать утечек контекста и прочих неприятностей.
    @Override
    public void onDestroy() {
        super.onDestroy();
        mPresenter.onDestroy();
        Log.d(TAG, "onDestroy()");
    }
}
どうしたの?
  • View とも呼ばれるアクティビティは、onCreate()メソッド内で Presenter インスタンスを作成し、それ自体をそのコンストラクターに渡します。
  • Presenterを作成すると明示的にViewを受け取りRepositoryインスタンスを作成します(ちなみにSingletonにすることも可能)
  • ボタンが押されると、ビューはプレゼンターをノックして「ボタンが押されました」と伝えます。
  • プレゼンターはリポジトリに向かって「このくだらないものをダウンロードしてください。」
  • リポジトリは「内容」をロードしてプレゼンターに配信します。
  • プレゼンターは View に向き直ります。「これがあなたのデータです。描いてください。」
以上です、皆さん。PS コンポーネント間の責任を明確に線引きすることが重要です。たとえば、私のトレーニング プロジェクトの 1 つでは、ボタンをクリックするときにデータベース内のデータを変更する必要がありました。モデルは POJO クラスによって記述され、画面上のオブジェクトに関する情報を担当するビューの場所に関する情報を渡しました。Presenter はリスト内でこのオブジェクトを探し、リポジトリに書き込まれるように送信しました。すべてが論理的だと思われますか?しかし、私のメンターは次のことを指摘しました。リポジトリは書き込みと読み取りのみを行うべきであり、POJO から必要な情報を引き出したり、何を書き込む必要があるかを決定したりすべきではありません。プレゼンターは、記録する情報のみを提供し、それ以上は何も与えてはなりません。アーキテクチャを実装するための厳密なフレームワークはありません。実験して、試して、自分にとって使いやすいものを探してください。そして、先輩の同志にコードレビューを見せることを忘れないでください =) 例は GitHub で入手できます: https://github.com/admitrevskiy/MVP_Example
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION