Pull to refresh
20
0
Юрий @senneco

Android team leader

Send message
Целая статья на хабре на такую тему. Хабр умирает? Похоже, что да.
Главный вопрос, который нам здесь нужно решить – а где будет жить экземпляр Router?

Если он будет жить и использоваться только во View, то никаких проблем нет – у нас есть напрямую доступ к Activity.

Сложней, если ссылка на Router нужна внутри Presenter. В таком случае вам не обойтись без явной передачи "чего-то" из View в Presenter. Здесь вы встаёте перед другим выбором: что передать из View? Context? А если Router будет не стартовать Activity, а менять фрагменты? Тогда придётся передавать что-то другое. Таким образом само собой напрашивается решение из соседней статьи: из View вы устанавливаете в Presenter непосредственно экземпляр Router, с которым в будущем будете работать из Presenter.

Мне кажется, если реализовывать VIPER, то нужно идти по второму пути и просто в onCreate передавать в Presenter экземпляр Router. В таком случае хотелось бы обратить внимание на две вещи:

  1. Не забудьте убирать Router из Presenter, когда View уничтожается(иначе будет утечка памяти)
  2. Вы можете расширить функционал MvpDelegate, добавив в метод `onCreate` указывание Router для Presenter, и очищая ссылку на Router в Presenter внутри метода MvpDelegate `onDestroy`

PS: Router ломается, если вы начинаете строить приложение не на фрагментах, а на custom view, т.к. при смене конфигурации вы потеряете все изменения лэйаута. В таком случае не используйте Router, а работайте прямыми командами во View из Presenter через ViewState. Тогда вы не потеряете ваши изменения после изменения конфигурации.
В большинстве случаев, здесь будет достаточно сделать так, чтоб на каждую страницу ViewPager был свой Presenter. Так вам будет проще всего – не нужно будет ничего разруливать.

В таком случае, каждый Fragment будет по-своему инициализировать свой Presenter, а Presenter будет уже доставать нужные данные. И вам будет очень просто обработать команды из Presenter, и оба фрагмента будут независимы друг от друга.
Если вам нужно, чтоб команда отрабатывала исключительно один раз, значит она не должна быть сохранена во ViewState. Для этого у неё должна быть стратегия SkipStrategy. Её можно указать, применив к методу start в интерфейсе View аннотацию: @StateStrategyType(SkipStrategy.class)

Ещё на заметку, ваш код можно изменить:

  • в activity, в методе onCreate выполните ваш код presenter.setStartValue(getIntent().getExtras().getInt(Constants.VALUE));
  • в presenter, в методе setStartValue сохарните пришедшее значение где-нибудь в presenter
  • в методе onFirstViewAttach берёте это значение и работаете с ним

Но это не обязательно – ваш подход абсолютно так же будет работать. Просто имейте ввиду возможность такого способа =)

Учтите, что метод onFirstViewAttach будет вызван только при первом привязывании view. А после поворота девайса, он уже не будет вызван. Но похоже вы это и так поняли =)
Если брать именно Moxy, то вам ничего не придётся переписывать при выпиливании либы. Придётся только дописывать =) Я вас не уговариваю, а просто информирую ;)
Вот как это можно сделать в moxy:
  • в каждом методе View сохранять в Bundle какое-то описание состояния
  • складывать этот Bundle в outState
  • в onCreate передавать этот Bundle в Presenter
  • в Presenter смотреть в метод onFistViewAttached, есть ли Bundle
  • если есть Bundle, «парсить» его и давать команды во ViewState


У этого способа есть минус – он не автоматизирован. Но есть и плюс – лишний раз Bundle парситься не будет. А вы как-нибудь автоматизировали создание сериализуемого ViewState?
Библиотека за вас архитектуру не построит — только поможет автоматизировать рутинную работу(по типу сохранения стейте). Но, конечно – не хотите — не используйте
Хотелось бы рассказать как мы решили этот вопрос в Moxy (т.к. мы решили что это главная проблема, которая стоит перед нами) — View аттачится в onStart(только если до этого был onCreate), а детачится в onDestroy. Так и утечек памяти нет, и лишний раз не применяются команды ко View из ViewState. В то же время, т.к. ViewState храниться в Presenter, а не во View, он не должен быть сериализуемым :)
Да, мы очень хотели, чтоб пришлось писать минимум кода. И в то же время хотелось попробовать annotation processor =) Результат крайне порадовал – для полноценного сохранения состояния достаточно применить аннотацию @GenerateViewState к MvpView и @InjectViewState к MvpPresenter. Когда видишь этот код и результат его работы, кажется что там есть магия =)

Правда, если можно обойтись без кодогенерации/рефлексии, используя только наследование/композицию, это наверное даже круче.
Понятно, а мы решили, что раз процесс убился, и всё-равно потерялись все Presenter, то просто пусть заново будет создан Presenter и всё начнётся сначала. Я замечал, что у стоковых Android-приложений именно такое поведение =)

Да, у нас тоже легко сделать кейс что на другой активити такого же типа будет использоваться другой Presenter =) Вообще, изначально все Presenter – локальные. И, соответственно, на каждый экран свои Presenter. А вот если указать глобальный тэг, то будет использоваться везде один Presenter. Ну и спец. фишка – динамический тэг для глобального презентера. Например, открыли список своих контакто☘ → создался Presenter для нашего списка контактов. Затем открыли список контактов друга → создался Presenter для списка его контактов. Затем вернулись к своему списку контактов, и тут уже не создаётся новый Presenter, а берётся старый. Актуально может быть, например, если эти Presenter очень долго отрабатывают и будет обидно потерять их.

А ваше решение где-нибудь опубликовано? Было бы интересно посмотреть =)
У вас видимо ViewState хранится во View, поэтому вы вынуждены сериализовывать его и складывать в Bundle?

Мы решили развязать пользователю руки, и поэтому ссылка на ViewState хранится в Presenter. Presenter в свою очередь хранится не в Activity, а в статичном хранилище. Это позволяет не зависеть Presenter(а значит и ViewState) от жизненного цикла View. И поэтому даже если команда во ViewState прилетела в то время, когда View не приаттачена к Presenter/к ViewState, как только View будет приаттачена, ViewState сообщит ей весь набор команд, которые она должна выполнить. За счёт этого можно из Presenter передавать в командах даже несериализуемые данные.

А если вы это и говорили, то круто, что мы не одни так подумали =)

И да, у нас идёт тэгирование не View, а Presenter ;)
Moxy довольно легко расширяется до VIPE®. Правда, Router там особо не нужен, но может и он войдёт.
Разница между статьями в том, что эта статья – про архитектуру. А статья про Moxy – больше про то, как используя Moxy построить приложение, подходящее под паттерны MVP & Co. Так что эта статья вам очень пригодится ;)
babylon похоже вы комментарии не в той статье пишите. Здесь разговор про Android+MVP, а вы говорите о JavaScript Object Notation, JSONNET и т. д.
Мало того, решение можно сделать ещё более «простым», подпилив gson, чтоб не нужно было писать аннотации в модели =) Gson умеет «сам» убирать префикс m и переводить CamelCase к lower_case_with_underscores и обратно(когда переводим объект в json):

new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                .setFieldNamingStrategy(new FieldNamingStrategy {
                    public String translateName(Field field) {
                        String name = FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES.translateName(field);
                        name = name.substring(2, name.length()).toLowerCase();
                        return name;
                    }
                })
                .create();
А я ничего не констатировал — что они хранят в открытом виде. Но сам факт, что высылают на почту мне мой пароль в открытом виде, не нравится. Никогда не любил, когда так делают. Кроме случаев, когда пароль генерируется при регистрации.

В моей памяти не помню, чтоб 100500 сервисов так делали. Помню 1 точно, и ещё один, не точно. Итого, теперь их в списке 3. А вы, видимо, считаете это хорошей практикой?
Они ещё и пароль в открытом виде на почту пересылают после регистрации =\ Может и хранятся в открытом виде? Жесть.
Не понимаю, о чём статья-то? И для чего она здесь? В ней совершен какой-то front-end прорыв? Или как-то особенно используются какие-то новые интересные инструменты? Сарказма нет. Может я действительно что-то не понимаю?
Использую на винде opera mail — местами очень не нравится, но в целом симпатично и компактно =)
Имею среднеспециальное образование. Мечтаю/пытаюсь стать лютым разработчиком =)
Недавно видел лекцию на подобную тему. Может кому пригодится: Рекурсивное расширение типа

Information

Rating
Does not participate
Location
Новосибирск, Новосибирская обл., Россия
Date of birth
Registered
Activity