Судя по всем, FragmentFactory больше нужен для того, чтобы вы могли передать зависимости во фрагмент. И тогда получается, что вы можете отдать процесс создания фрагмента на откуп DI.
Но для передачи именно аргументов этот механизм уже не подходит и нужно по старинке передавать аргументы через аргументы (масло масляное получилось).
Ну, можно назвать вообще как угодно =) И да, 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, то есть моменты, котоыре действительно очень радуют:
Хочется выделить support library + google play services. На мой взгляд, это чуть ли не самое крутое изобретение google для android за последние ~ 3-4 года. Ведь когда для того, чтобы на старых девайсах твоё приложение работало как задумано, тебе достаточно подключить библиотеку в зависимости, и всё – это действительно круто! А иначе мы бы наверное только год-два назад начали использовать Fragment SDK(потому что раньше нужно было бы поддерживать Gingerbread, и о фрагментах не стоило бы и мечтать).
Открытые исходники платформы. Это действительно круто, когда не понимая, как например работает Looper, или за счёт чего retain fragment переживают пересоздание Activity, ты идешь, читаешь исходники этого добра, и понимаешь, где магия. Или находишь где баг, и понимаешь, какой костыль нужно подставить XD
Большое, активное community. Всё время появляется довольно много новых opensource-библиотек, порой вполне не дурных. Достаточное количество статей, обсуждений, конференций.
Конечно, и минусов в нашей платформе предостаточно!
Lifecycle. И этим всё сказано =) Я до сих пор не могу примириться с мыслью, что при малейшем чихе, activity пересоздаётся. Мне не верится, что нельзя было сделать так, чтобы Activity была настоящим Controller/Presenter и не была привязана к тому, что видит пользователь, поворачивая устройство, или переключая Activity в состояние multi-window. Не верю, что не нашлось бы в google людей, которые бы сделали удобное SDK. Конечно, это здорово, что google не загнал нас в какой-то паттерн(вроде MVP/MVC/MVVM/e.t.c.), как вроде уже говорили в одном из подкастов. Но, блин, разве если бы был продиктован какой-то подход, который был бы удобным и беспроблемным, неужели это не помогло бы самой платформе становиться только лучше?
Асинхронщина – туда же. AsyncTask конечно решает, но все знают, что работать с ним из activity – плохо. Но куда тогда деваться junior-разработчику? Идти разбираться с Loader? Наворачивать систему Service+AsyncTask и поиск AsyncTask по id? Он этого не поймёт, не почувствует, и забьёт на утечку памяти из-за использования AsyncTask прям в Activity. И может пройти не один год, пока он поймёт свою ошибку. А за это время им может быть опубликованно не одно приложение.
Навигация по приложению. Мне кажется, было бы невероятно круто, если бы можно было в визуальном редакторе настроить переходы по приложению. Потом зайти в код и прописать условия перехода. В студии даже как-то раз засветился такой инструмент, но видимо что-то не срослось(потому что тогда он был слишком кривой, а сейчас я просто не смог его найти). Возможно, когда мы перестанем поддерживать 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, поэтому утечки не должно быть. Может быть вы как-то самостоятельно храните ссылку на фрагмент где-нибудь в презентере?
Судя по всем, FragmentFactory больше нужен для того, чтобы вы могли передать зависимости во фрагмент. И тогда получается, что вы можете отдать процесс создания фрагмента на откуп DI.
Но для передачи именно аргументов этот механизм уже не подходит и нужно по старинке передавать аргументы через аргументы (масло масляное получилось).
@InjectViewState
, а просто в момент аттача передавать биндинг-объект во вью, и всё. Для этого достаточно заоверрайдить методattachView
, и в нём передавать во вью свой объект.Можно подумать на досуге, как можно сделать =)
Если же брать именно разработку под Android, то есть моменты, котоыре действительно очень радуют:
Конечно, и минусов в нашей платформе предостаточно!
В то же время, 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. Может, у них как-то по другому. Но я именно так вижу его использование =)
Мы (в arello :D )планируем в ближайшем времени попробовать такую структуру пакетов – может быть будет удобно. А может и нет – время покажет ;)
Но в случае, если вы хотите, вы можете написать свою стратегию, которая будет удалять команду, к которой текущая является противодействием. Или же, если новая команда приводит View в такое состояние, что все предыдущие команды становятся точно не нужными, то можно применять стратегию SingleStateStrategy. Например если есть команда showData, и нет swipe to refresh, то можно к ней применить эту стратегию, т.к. после того, как установили данные, точно не нужно ни ошибку показывать, ни прогресс.
В остальном — всё так :)
А если делать не retain-фрагмент, то утечки памяти не должно быть – при вызове метода onDestroy() у фрагмента, он будет отвязан от презентера. В то же время в презентере хранятся weak references на View, поэтому утечки не должно быть. Может быть вы как-то самостоятельно храните ссылку на фрагмент где-нибудь в презентере?