JavaRush /Blog Java /Random-PL /MVP na Androida dla najmłodszych

MVP na Androida dla najmłodszych

Opublikowano w grupie Random-PL
Kiedy zaczynałem swoją przygodę jako programista Androida, słowa „Architektura aplikacji mobilnych” wywołały u mnie głębokie zdziwienie, Google i artykuły na temat Habré wpędziły mnie w jeszcze większą depresję – patrzę na książkę i nic nie widzę. Myślę, że jeśli czytasz ten artykuł, to już nie raz przestudiowałeś ten obraz i próbowałeś zrozumieć, co się dzieje: MVP w Androidzie dla najmłodszych - 1moim zdaniem problem zrozumienia podejścia architektonicznego w rozwoju urządzeń mobilnych leży w abstrakcyjności samej architektury. Każdy programista ma własną wizję prawidłowego wdrożenia tego lub innego wzorca. Mniej lub bardziej przyzwoite przykłady wdrożenia MVP znaleziono w anglojęzycznym sektorze Internetu, co nie jest zaskakujące. Przyjrzyjmy się krótko, co jest czym i przejdźmy do przykładu. Model - poziom danych. Nie lubię używać określenia „logika biznesowa”, dlatego w moich aplikacjach nazywam to Repozytorium i komunikuje się z bazą danych i siecią. Widok — poziom wyświetlania. Będzie to widok aktywności , fragmentu lub widoku niestandardowego, jeśli nie lubisz tańczyć z tamburynem i wchodzić w interakcję z cyklem życia. Przypomnę, że początkowo wszystkie aplikacje na Androida podporządkowane są strukturze MVC , gdzie Kontrolerem jest Aktywność lub Fragment . Prezenter jest warstwą pomiędzy Widokiem i Modelem. Widok przesyła zdarzenia, które mu się zdarzają, prezenter przetwarza je, jeśli to konieczne, uzyskuje dostęp do Modelu i zwraca dane do Widoku w celu renderowania. W nawiązaniu do Androida i konkretnego przykładu podkreślę istotną część – Kontrakt. Jest to interfejs opisujący wszystkie interakcje pomiędzy powyższymi komponentami. Podsumowując część teoretyczną:
  • Widok wie o Prezenterze;
  • Prezenter wie o widoku i modelu (repozytorium);
  • Modeluj samodzielnie;
  • Kontrakt reguluje interakcje między nimi.
Właściwie sam przykład, dla uproszczenia eksperymentu, klikając na przycisk, załadujemy wiersz z bazy danych i wyświetlimy go w TextView . W bazie znajduje się np. lista najlepszych restauracji w mieście. Zacznijmy od umowy: Stwórzmy interfejs MainContract:
public interface MainContract {
    interface View {
        void showText();
    }

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

    interface Repository {
        String loadMessage();
    }
}
Na razie po prostu podkreślamy 3 elementy naszej przyszłej aplikacji i ich działanie. Następnie opiszemy Repozytorium:
public class MainRepository implements MainContract.Repository {

    private static final String TAG = "MainRepository";
    @Override
    public String loadMessage() {
        Log.d(TAG, "loadMessage()");
        /** Здесь обращаемся к БД Lub сети.
         * Я специально ничего не пишу, чтобы не загромождать пример
         * DBHelper'ами и прочими не относяшимеся к теме obiektами.
         * Поэтому я буду возвращать строку Сосисочная =)
         */
        return "Сосисочная у Лёхи»;
    }
}
Wszystko jest z nim jasne, wystarczy załadować i rozładować dane. Następny w kolejności jest prezenter:
public class MainPresenter implements MainContract.Presenter {
    private static final String TAG = "MainPresenter";

    //Компоненты MVP Aplikacje
    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()");
    }
}
Pamiętacie, jak pisałam o tańcu z tamburynem i cyklu życia? Prezenter żyje tak długo, jak żyje jego Widok. Podczas opracowywania złożonych scenariuszy użytkownika radzę zduplikować wszystkie wywołania zwrotne Widoku w Prezenterze i wywołać je w odpowiednich momentach, powielając cykl życia Aktywności/Fragmentu, aby z czasem zrozumieć, co należy zrobić z danymi, które aktualnie wiszą w „międzywarstwie”. I na koniec widok:
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()");
    }
}
Co się dzieje?
  • Działanie, znane również jako Widok, onCreate()tworzy instancję Presenter w metodzie i przekazuje się do jej konstruktora.
  • Kiedy tworzony jest prezenter, jawnie otrzymuje widok i tworzy instancję repozytorium (nawiasem mówiąc, można go przekształcić w Singleton)
  • Po naciśnięciu przycisku View puka do prezentera i mówi: „Przycisk został naciśnięty”.
  • Prezenter zwraca się do Repozytorium: „Pobierz dla mnie te bzdury”.
  • Repozytorium ładuje i dostarcza „materiały” Prezenterowi.
  • Prezenter odwraca się do Widoku: „Oto dane dla Ciebie, narysuj je”
To wszystko, chłopaki. PS Ważne jest jasne określenie odpowiedzialności pomiędzy komponentami. Przykładowo w jednym z moich projektów szkoleniowych po kliknięciu przycisku konieczna była zmiana danych w bazie danych. Model został opisany klasą POJO, przekazałem informację o położeniu widoku, który odpowiada za informację o obiekcie na ekranie, Prezenter odszukał ten obiekt na liście i wysłał go do zapisania do Repozytorium. Czy wszystko wydaje się logiczne? Ale mój mentor zauważył, co następuje: Repozytorium powinno TYLKO zajmować się pisaniem i czytaniem, nie powinno wyciągać niezbędnych informacji z POJO i decydować, co musi napisać. Prezenter musi przekazać mu jedynie informacje do nagrania i nic więcej. Nie ma ścisłych ram wdrażania architektury: eksperymentuj, próbuj, szukaj tego, co jest dla Ciebie wygodne. I nie zapomnij pokazać swoim starszym towarzyszom przeglądu kodu =) Przykład jest dostępny na GitHub: https://github.com/admitrevskiy/MVP_Example
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION