- PVSM.RU - https://www.pvsm.ru -

Работая с Android часто можно видеть, как весь функциональный код помещается в методы жизненного цикла activity/fragment. В общем-то такой подход имеет некоторое обоснование — «методы жизненного цикла» всего лишь хэндлеры, обрабатывающие этапы создания компонента системой и специально предназначенные для наполнения их кодом. Добавив сюда то, что каркас UI описывается через xml файлы, мы уже получаем базовое разделение логики и интерфейса. Однако из-за не совсем «изящной» структуры жизненного цикла, его зависимости от множества флагов запуска, и различной (хоть и похожей) структуры для разных компонентов, эффективно воспользоваться подобным разделением не всегда бывает возможно, что в итоге выливается в написании всего кода в onCreate().
MVP паттерн разработки для android, предлагающий разбивать приложение на следующие части:
Model/View/Presenter должны представлять из себя интерфейсы для большей гибкости модификации кода.
Рассмотрим пример приложения, состоящего из одного экрана, на котором находится EditText и TextView. При этом по мере редактирования текста в EditText отправляются сетевые запросы, результат которых должен отображаться в TextView (конкретика запроса не должна нас волновать, это может быть перевод, краткая справка по термину или что то подобное).
ExampleModel.java:
public interface ExampleModel {
Observable<String> changeText();
Observable<String> request(String query);
}
ExampleView.java:
public interface ExampleView {
void showResponse(String result);
}
ExamplePresenter.java:
public interface ExamplePresenter {
void onCreate(Activity activity, Bundle savedInstanceState);
}
Так как Model и View используют одни и тебе виджеты (в нашем случае EditText и TextView) для своей работы, разумно будет реализовать содержащий их класс.
ExampleViewHolder.java:
public class ExampleViewHolder {
public final EditText editText;
public final TextView textView;
public ExampleViewHolder(EditText editText, TextView textView) {
this.editText = editText;
this.textView = textView;
}
}
При реализации Model мы предполагаем использование rxAndroid, для «оборачивания» EditTetx, и retrofit для реализации сетевых запросов.
ExampleModelImpl.java:
public class ExampleModelImpl implements ExampleModel {
private final ExampleViewHolder viewHolder;
public ExampleModelImpl(final ExampleViewHolder viewHolder) {
this.viewHolder = viewHolder;
}
@Override
public Observable<String> changeText() {
return WidgetObservable
.text(viewHolder.editText)
.map(new Func1<OnTextChangeEvent, String>() {
@Override
public String call(OnTextChangeEvent event) {
return event.toString().trim();
}
});
}
@Override
public Observable<String> request(String query) {
//всю работу берет на себя retrofit
return RestManager.newInstance().request(query);
}
}
ExampleViewImpl.java:
public class ExampleViewImpl implements ExampleView {
private final ExampleViewHolder viewHolder;
public ExampleViewImpl(final ExampleViewHolder viewHolder) {
this.viewHolder = viewHolder;
}
@Override
public void showResponse(final String result) {
viewHolder.textView.setText(result);
}
}
Так как количество сетевых запросов зависит от скорости набора текста (а она может быть достаточно высока), существует естественное желание ограничить частоту событий редактирование текста в EditText. В данном случае это реализуется директивой debounce [3] (при этом, естественно, ввод текста не блокируется, а лишь пропускается часть событий редактирования, произошедших в временной промежуток в 150 миллисекунд).
ExamplePresenterImpl.java:
public class ExamplePresenterImpl implements ExamplePresenter {
private final ExampleModel model;
private final ExampleView view;
private Subscription subscription;
public ExamplePresenterImpl(ExampleModel model, ExampleView view) {
this.model = model;
this.view = view;
}
@Override
public void onCreate(Activity activity, Bundle savedInstanceState) {
subscription = model
.changeText()
//ограничивает частоту событий
.debounce(150, TimeUnit.MILLISECONDS)
.switchMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String query) {
return model.request(query);
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String result) {
view.showResponse(result);
}
});
}
@Override
public void onDestroy() {
if (subscription != null) {
subscription.unsubscribe();
}
}
}
Реализация activity, передающая всю сущностную часть работы Presenter:
ExampleActivity.java
public class ExampleActivity extends Activity {
private ExamplePresenter examplePresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.example_activity);
final ExampleViewHolder exampleViewHolder = new ExampleViewHolder(
(TextView) findViewById(R.id.text_view),
(EditText) findViewById(R.id.edit_text)
);
final ExampleModel exampleModel
= new ExampleModelImpl(exampleViewHolder);
final ExampleView exampleView
= new ExampleViewImpl(exampleViewHolder);
examplePresenter
= new ExamplePresenterImpl(exampleModel, exampleView);
examplePresenter.onCreate(this, savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
examplePresenter.onDestroy();
}
}
Хотя наш пример невероятно упрощен, в нем уже есть нетривиальные моменты связанные с контролем частоты событий. Представить же эволюцию нашего приложения в разрезе mvp довольно легко:
MVP — не единственный способ разбиения Android-приложения на компоненты, и уж тем более он не предполагает обязательного использования rxJava вместе с ним. Однако одновременное их использование дает приемлемые результаты в упрощении структуры поддерживаемого приложения.
Автор: 7voprosov
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/85578
Ссылки в тексте:
[1] rxJava: https://github.com/ReactiveX/RxJava
[2] android.view.View: http://developer.android.com/reference/android/view/View.html
[3] debounce: http://reactivex.io/documentation/operators/debounce.html
[4] Источник: http://habrahabr.ru/post/252903/
Нажмите здесь для печати.