JavaRush /Java Blog /Random-IT /MVP in Android per i più piccoli

MVP in Android per i più piccoli

Pubblicato nel gruppo Random-IT
Quando ho iniziato il mio viaggio come sviluppatore Android, le parole "Architettura di applicazioni mobili" mi hanno causato un profondo sconcerto, Google e gli articoli su Habré mi hanno portato a una depressione ancora maggiore: guardo il libro e non vedo nulla. Penso che se stai leggendo questo articolo, hai già studiato questa immagine più di una volta e hai cercato di capire cosa sta succedendo: MVP su Android per i più piccoli - 1il problema di comprendere l'approccio architettonico nello sviluppo mobile, secondo me, risiede nell'astrattezza dell'architettura stessa. Ogni sviluppatore ha la propria visione su come implementare correttamente questo o quel modello. Esempi più o meno decenti di implementazione dell'MVP sono stati trovati nel settore anglofono di Internet, il che non sorprende. Diamo un'occhiata brevemente a cosa è cosa e passiamo a un esempio. Modello : livello dati. Non mi piace usare il termine “logica di business”, quindi nelle mie applicazioni lo chiamo Repository e comunica con il database e la rete. Visualizza : livello di visualizzazione. Sarà Attività , Frammento o Visualizzazione personalizzata se non ti piace ballare con un tamburello e interagire con il ciclo di vita. Permettetemi di ricordarvi che inizialmente tutte le applicazioni Android sono subordinate alla struttura MVC , dove il Controller è un'Attività o un Frammento . Presenter è un livello tra Vista e Modello. La Vista trasmette gli eventi che le accadono, il presentatore li elabora, se necessario, accede al Modello e restituisce i dati alla Vista per il rendering. In relazione ad Android e ad un esempio specifico, metterò in evidenza la parte importante: il contratto. Questa è l'interfaccia che descrive tutte le interazioni tra i componenti di cui sopra. Riassumendo la parte teorica:
  • View conosce Presenter;
  • Il relatore conosce View e Model (Repository);
  • Modello da solo;
  • Il contratto regola le interazioni tra loro.
In realtà, nell'esempio stesso, per semplicità dell'esperimento, facendo clic su un pulsante, caricheremo una riga dal database e la visualizzeremo in un TextView . Ad esempio, il database contiene un elenco dei migliori ristoranti della città. Cominciamo con il contratto: creiamo un'interfaccia MainContract:
public interface MainContract {
    interface View {
        void showText();
    }

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

    interface Repository {
        String loadMessage();
    }
}
Per ora, stiamo semplicemente evidenziando i 3 componenti della nostra futura applicazione e cosa faranno. Successivamente descriveremo il Repository:
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 "Сосисочная у Лёхи»;
    }
}
Tutto è chiaro, basta caricare e scaricare i dati. Il prossimo è il Presentatore:
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()");
    }
}
Ricordi che ho scritto di ballare con un tamburello e del ciclo della vita? Il Presenter vive finché vive la sua View, quando si sviluppano scenari utente complessi, consiglio di duplicare tutte le callback delle View nel Presenter e richiamarle nei momenti opportuni, duplicando il ciclo di vita Activity/Fragment, in modo da capire per tempo di cosa ha bisogno da fare con i dati attualmente sospesi nell’“interlayer”. E infine, Visualizza:
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()");
    }
}
Cosa sta succedendo?
  • L'attività, nota anche come View, onCreate()crea un'istanza Presenter in un metodo e si passa al relativo costruttore.
  • Quando viene creato un Presenter, riceve esplicitamente una View e crea un'istanza di Repository (a proposito, può essere reso Singleton)
  • Quando si preme un pulsante, la Vista bussa al presentatore e dice: "Il pulsante è stato premuto".
  • Il presentatore si rivolge al Repository: "Scaricami questa schifezza".
  • Il repository carica e consegna il "materiale" al presentatore.
  • Il relatore si rivolge a View: “Ecco i dati per te, disegnali”
Questo è tutto, ragazzi. PS È importante delineare chiaramente le responsabilità tra i componenti. Ad esempio, in uno dei miei progetti di formazione, quando si cliccava su un pulsante era necessario modificare i dati nel database. Il modello è stato descritto da una classe POJO, ho passato le informazioni sulla posizione della vista, che è responsabile delle informazioni sull'oggetto sullo schermo, Presenter ha cercato questo oggetto nell'elenco e lo ha inviato per essere scritto nel repository. Sembra tutto logico? Ma il mio mentore ha sottolineato quanto segue: il Repository dovrebbe SOLO scrivere e leggere, non dovrebbe estrarre le informazioni necessarie dal POJO e decidere cosa è necessario scrivere. Il Presentatore dovrà fornirgli solo le informazioni da registrare e nulla più. Non esiste un quadro rigido per l'implementazione dell'architettura: sperimenta, prova, cerca ciò che è conveniente per te personalmente. E non dimenticare di mostrare ai tuoi compagni senior la revisione del codice =) L'esempio è disponibile su GitHub: https://github.com/admitrevskiy/MVP_Example
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION