Pull to refresh
20
0
Юрий @senneco

Android team leader

Send message
Ну, можно назвать вообще как угодно =) И да, MVP можно использовать с Data Binding, при желании. Даже есть желание попробовать такой подход. В таком случае можно не использовать аннотацию @InjectViewState, а просто в момент аттача передавать биндинг-объект во вью, и всё. Для этого достаточно заоверрайдить метод attachView, и в нём передавать во вью свой объект.
Конечно можно. Просто это тогда называется MVVM. Но мне больше по душе MVP. Спорить о том, что лучше — не вижу смысла ;) Тем более про это есть целая статья, и её писал человек, который использовал и тот, и тот подход.
Я не могу понять, что ты предлагаешь, и зачем?) Для управления инжекцией презентера? Как-то очень сложно подход выглядит =)
На данный момент такой возможности нет. И пока я не представляю, как сделать такую возможность, чтобы одновременно это было и удобно, и безопасно. Потому что в таком случае вам обязательно придётся делать provide-метод, а это может быть не очевидно, или ещё чем-нибудь не красиво.

Можно подумать на досуге, как можно сделать =)
Сейчас я готовлюсь к DevFest Nsk, на котором кроме доклада будет ещё и codelab. Для этого я готовлю много простых и понятных примеров. После DevFest (а может и раньше) эти примеры появятся в wiki ;)
А, я думал, что вы знаете правильный ответ. Видимо, я вас не так понял. Потому что статья автора как раз про эту боль, и он заранее предупреждает остальных =)
Не знаком с MVVM и с Databinding Library в частности, но похоже как раз вы сможете рассказать: как нам правильно и канонично показать toast или добавить новую View на экран, не выполняя её inflate заранее? И чтобы после пересоздания Activity/Fragment эта View оставалась на экране? Или для этого придётся отказаться от Databinding Library? Конечно без кода, а общими словами =)
Когда ты приходишь в мир мобильных приложений после frontend-рзработки, в первую очередь тебя неимоверно радует отсутствие необходимости адаптировать верстку под IE, Safari и прочие непонятные браузеры :D Ещё, действительно, очень радует зоопарк девайсов. Наличие больших и маленьких устройств, производительных и не очень, дешевых и дорогих, брендовых – всё это заставляет не просто писать код, но и думать, как оно будет работать на разных устройствах. Нужно думать о производительности на слабых устройствах и о максимальной красоте на мощных. И не забывать, чтобы пользоваться приложением было удобно =)

Если же брать именно разработку под Android, то есть моменты, котоыре действительно очень радуют:

  1. Хочется выделить support library + google play services. На мой взгляд, это чуть ли не самое крутое изобретение google для android за последние ~ 3-4 года. Ведь когда для того, чтобы на старых девайсах твоё приложение работало как задумано, тебе достаточно подключить библиотеку в зависимости, и всё – это действительно круто! А иначе мы бы наверное только год-два назад начали использовать Fragment SDK(потому что раньше нужно было бы поддерживать Gingerbread, и о фрагментах не стоило бы и мечтать).
  2. Открытые исходники платформы. Это действительно круто, когда не понимая, как например работает Looper, или за счёт чего retain fragment переживают пересоздание Activity, ты идешь, читаешь исходники этого добра, и понимаешь, где магия. Или находишь где баг, и понимаешь, какой костыль нужно подставить XD
  3. Большое, активное community. Всё время появляется довольно много новых opensource-библиотек, порой вполне не дурных. Достаточное количество статей, обсуждений, конференций.


Конечно, и минусов в нашей платформе предостаточно!

  1. Lifecycle. И этим всё сказано =) Я до сих пор не могу примириться с мыслью, что при малейшем чихе, activity пересоздаётся. Мне не верится, что нельзя было сделать так, чтобы Activity была настоящим Controller/Presenter и не была привязана к тому, что видит пользователь, поворачивая устройство, или переключая Activity в состояние multi-window. Не верю, что не нашлось бы в google людей, которые бы сделали удобное SDK. Конечно, это здорово, что google не загнал нас в какой-то паттерн(вроде MVP/MVC/MVVM/e.t.c.), как вроде уже говорили в одном из подкастов. Но, блин, разве если бы был продиктован какой-то подход, который был бы удобным и беспроблемным, неужели это не помогло бы самой платформе становиться только лучше?
  2. Асинхронщина – туда же. AsyncTask конечно решает, но все знают, что работать с ним из activity – плохо. Но куда тогда деваться junior-разработчику? Идти разбираться с Loader? Наворачивать систему Service+AsyncTask и поиск AsyncTask по id? Он этого не поймёт, не почувствует, и забьёт на утечку памяти из-за использования AsyncTask прям в Activity. И может пройти не один год, пока он поймёт свою ошибку. А за это время им может быть опубликованно не одно приложение.
  3. Навигация по приложению. Мне кажется, было бы невероятно круто, если бы можно было в визуальном редакторе настроить переходы по приложению. Потом зайти в код и прописать условия перехода. В студии даже как-то раз засветился такой инструмент, но видимо что-то не срослось(потому что тогда он был слишком кривой, а сейчас я просто не смог его найти). Возможно, когда мы перестанем поддерживать sdk lvl < 16, нам поможет Context.startActivities(android.content.Intent[]). Но и это не факт. Да и с навигацией по фрагментам хотелось бы так же легко и интуитивно расквитаться.
За время использования MVP/Moxy убедился, что MVP нужно использовать тогда, когда на вашем экране есть логика. В таком случае, выделив её в Presenter и Model, View становится максимально простой. И в то же время можно легко тестировать Model и Presenter.

В то же время, MVP помогает очень сочно производить изменения дизаайна, рефакторинг кода. Например, был у вас DailogFragment, а стал BottomSheetDialog, Snackbar или Toast(в зависимости от содержимого). И если интерфейс View был сделан максимально независимо от того, как выглядит View, то вы это сделает максимально быстро и просто. Или же поменялись какие-нибудь условия бизнес-логики. У вас опять же готовый интерфейс View, который не придётся менять. И это действительно так происходит, даже на маленьких проектах(2-3 месяца).

Так же, «Андроид умеет сам восстанавливать View у которых прописан id», но вот незадача – visibility он не восстановит =( И динамически добавленная View пропадёт.

Про Loaders я молчу – мы от них как раз убегали, когда создавали Moxy :D Но опыт использования Loaders тоже очень полезен. Полезно всё – AsyncTask, Loader, Robospice, Rx, MVP, Moxy =) Главное, почувствовать когда и где что лучше использовать.

MVP показал себя с лучшей стороны – чрезвычайно дешево завести интерфейс для View и Presenter для минимальной бизнес-логики. А профит очень приятен, даже при малейших изменениях.

Статья ещё будет, и наверное не одна. Но вряд ли там найдётся место тому, «как было раньше», потому что раньше было перепробовано слишком много всего :D

PS: другие библиотеки, реализующие MVP толком не пробовал – хватало детального изучения сорцов+сэмплов, чтобы в чём-нибудь, да расстроиться. Mosby понравился больше всего =)
Я даже не знаю, есть ли ясный ответ «да» или «нет». Могу только рассказать в чём разница, а вы уже сами определитесь =)

ViewState хранится в Presenter. Presenter хранится в static-хранилище. Поэтому нет никакой обходимости передавать в команды serializable-объекты. Можно складывать хоть что. Но в случае, если процесс будет уничтожен, то и static-хранилище с Presenter будет уничтожено. А значит и все ViewState будут уничтожены.

В то же время, в Bundle saveState можно складывать только serializable-объекты(ну и примитивы со String). Выигрыш, понятно, в том, что если процесс будет уничтожен, а потом восстановлен, мы сможем запросто достать команды из savedState. Но вот какая проблема: у вас может быть команда showProgress(). И если пользователь будет видеть прогресс, то наш Presenter обязан загрузить данные. А это значит мы должны во время применения команды, ещё и начать что-то делать в Presenter. Но велика вероятность, что для этого нам придётся сохранить оочень много информации в команде. А это чревато запутанным кодом. В то же время, если мы «лениво» сохраняем команды, то сперва из savesState будет получена команда showProgress(), а сразу после этого могут идти две команды hideProgress() и showData(). И тут придётся как-то очень сильно исхитриться, чтоб Presenter перестал грузить данные, т. к. они уже есть.

И такой подход с Bundle как раз используется в Mosby(ну или почти такой). И это мне в Mosby и не понравилось – нужда каждый раз руками разруливать восстановление состояния View.

Ещё, forceLain говорит, что они используют save state для хранения ViewState. Может, у них как-то по другому. Но я именно так вижу его использование =)
Есть мысль, что стоит заводить отдельный пакет под Car, и в нём держать CarView, CarPresenter и CarViewModel. CarActivity стоит оставить в пакете activity. Этому есть несколько причин:

  • Обычно для одного Presenter есть один конкретный View. И для этого удобно передавать во View не сырой объект Model, а уже обработанный ViewModel, который содержит в себе информацию, собранную из нескольких моделей. Таким образом мы соберем в одном месте всё, что нужно для Precenetr и View. Так же сюда можно будет, например, сложить уникальные StateStrategy для связки View Presenter
  • Activity(равно как и Fragment) может содержать в себе несколько View. Поэтому чтобы не возникало путаницы, стоит всегда складывать их в пакет activity, fragment, etc

Мы (в arello :D )планируем в ближайшем времени попробовать такую структуру пакетов – может быть будет удобно. А может и нет – время покажет ;)
Судя по декомпиляции "kicker Bundesliga Fußball News", оно сделано на mosby. Так что можете спокойно посмотреть вживую на приложение с миллионной аудиторией ;)
Лично общался с людьми(но затруднюсь с именами), которые в реальных проектах используют mosby, moxy, nucleus, а так же личные поделки на тему MVP. Таких становится всё больше и больше ;)
Видимо, Android полностью останавливает приложение в таком случае. Тут вам не помогут даже глобальные presenter. Но похоже, что у activity должен был быть вызван метод onSaveInstanceState – в нём вы можете сохранить какие-нибудь флаги для presenter. А в onCreate передавать эти флаги в presenter(банально сделать метод init(Bundle args) в presenter), и уже в presenter решать, делать что-нибудь со view в onFirstViewAttach, или нет.
Эти команды применятся очень быстро, что пользователь не почувствует, что применилось несколько команд.

Но в случае, если вы хотите, вы можете написать свою стратегию, которая будет удалять команду, к которой текущая является противодействием. Или же, если новая команда приводит View в такое состояние, что все предыдущие команды становятся точно не нужными, то можно применять стратегию SingleStateStrategy. Например если есть команда showData, и нет swipe to refresh, то можно к ней применить эту стратегию, т.к. после того, как установили данные, точно не нужно ни ошибку показывать, ни прогресс.
Да, именно так. Когда вы применяете аннотацию @InjectViewState, annotation processor понимает, ViewState какой View вы хотите использовать, и генерирует его, если его ещё нет.
Не понимаю, где вы видите здесь проблемы в использовании асинк тасок? Работает она, да работает. И даже дополнительно решает проблему обновления вью с главного потока. Если не хочется тащить rx, или велосипедить, то самый подходящий инструмент.

В остальном — всё так :)
Если посмотреть в исходники, то можно увидеть, что каждый раз, когда к Presenter биндится View, вызывается абстрактный метод updateView(). Автор предлагает в нём менять состояние View. Проблема такого решения в том, что если View окажется сложной и сможет одновременно отображать 2 состояния(например, progress + empty view или данные), то этот метод будет очень раздут. К слову, хотелось бы рассказать, что мы в Moxy решили эту проблему тем, что храним очередь команд для View, и когда View аттачится к Presenter, мы накатываем на неё эту очередь команд.
Интересное замечание. Действительно, состояние не будет восстановлено, т.к. у фрагмента не будет вызван метод onCreate(). Значит, в случае с retain-фрагментом можно поступить например так: вызывать метод делегата onCreate() не в onCreate() фрагмента, а где-нибудь в другом месте. Например, в методе onCreateView(). Правда, метод onAttach() может быть более подходящим местом, но я с ходу не могу ручаться за вызовы этого метода у retain-фрагментов.

А если делать не retain-фрагмент, то утечки памяти не должно быть – при вызове метода onDestroy() у фрагмента, он будет отвязан от презентера. В то же время в презентере хранятся weak references на View, поэтому утечки не должно быть. Может быть вы как-то самостоятельно храните ссылку на фрагмент где-нибудь в презентере?
Тогда, по-моему автор должен был хотя бы разобраться, почему это происходит? Рассказать всем причину. Показать пути решения без изобретения велосипеда (например, если известно, что сравнение будет основываться на полях объекта, которые Comparable, то просто возвращать их сравнение через compare). Или собрать сборник подобных случаев-открытий автора. Тогда это будет похоже на статью. А так – чуть сократить и получится лаконичный твит =)

PS: а если все начнут постить свои небольшие открытие, то хабр превратится непонятно во что.
1
23 ...

Information

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