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: il 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.
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”
GO TO FULL VERSION