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

    Можно подумать на досуге, как можно сделать =)
  • Различия между MVVM и остальными MV*-паттернами
    0
    Сейчас я готовлюсь к DevFest Nsk, на котором кроме доклада будет ещё и codelab. Для этого я готовлю много простых и понятных примеров. После DevFest (а может и раньше) эти примеры появятся в wiki ;)
  • Как перестать использовать MVVM
    0
    А, я думал, что вы знаете правильный ответ. Видимо, я вас не так понял. Потому что статья автора как раз про эту боль, и он заранее предупреждает остальных =)
  • Как перестать использовать MVVM
    0
    Не знаком с MVVM и с Databinding Library в частности, но похоже как раз вы сможете рассказать: как нам правильно и канонично показать toast или добавить новую View на экран, не выполняя её inflate заранее? И чтобы после пересоздания Activity/Fragment эта View оставалась на экране? Или для этого придётся отказаться от Databinding Library? Конечно без кода, а общими словами =)
  • Android Dev: продолжение подкастов о профессиональной разработке под Android
    +1
    Когда ты приходишь в мир мобильных приложений после 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[]). Но и это не факт. Да и с навигацией по фрагментам хотелось бы так же легко и интуитивно расквитаться.
  • Moxy — реализация MVP под Android с щепоткой магии
    0
    За время использования 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 понравился больше всего =)
  • Moxy — реализация MVP под Android с щепоткой магии
    0
    Я даже не знаю, есть ли ясный ответ «да» или «нет». Могу только рассказать в чём разница, а вы уже сами определитесь =)

    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. Может, у них как-то по другому. Но я именно так вижу его использование =)
  • MVP на стероидах: заставляем робота писать код за вас
    +1
    Есть мысль, что стоит заводить отдельный пакет под Car, и в нём держать CarView, CarPresenter и CarViewModel. CarActivity стоит оставить в пакете activity. Этому есть несколько причин:

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

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

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

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

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

    PS: а если все начнут постить свои небольшие открытие, то хабр превратится непонятно во что.
  • Никогда не делайте компараторов на базе вычитания
    +6
    Целая статья на хабре на такую тему. Хабр умирает? Похоже, что да.
  • Moxy — реализация MVP под Android с щепоткой магии
    0
    Главный вопрос, который нам здесь нужно решить – а где будет жить экземпляр 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. Тогда вы не потеряете ваши изменения после изменения конфигурации.
  • Moxy — реализация MVP под Android с щепоткой магии
    0
    В большинстве случаев, здесь будет достаточно сделать так, чтоб на каждую страницу ViewPager был свой Presenter. Так вам будет проще всего – не нужно будет ничего разруливать.

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

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

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

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

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


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

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

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

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

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

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

    И да, у нас идёт тэгирование не View, а Presenter ;)
  • Android VIPER на реактивной тяге
    0
    Moxy довольно легко расширяется до VIPE®. Правда, Router там особо не нужен, но может и он войдёт.
    Разница между статьями в том, что эта статья – про архитектуру. А статья про Moxy – больше про то, как используя Moxy построить приложение, подходящее под паттерны MVP & Co. Так что эта статья вам очень пригодится ;)
  • Построение Android приложений шаг за шагом, часть первая
    0
    babylon похоже вы комментарии не в той статье пишите. Здесь разговор про Android+MVP, а вы говорите о JavaScript Object Notation, JSONNET и т. д.
  • Android архитектура клиент-серверного приложения
    +2
    Мало того, решение можно сделать ещё более «простым», подпилив 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();
    
  • В поиске бесплатных билетов, исследование игры Аэрофлота
    +5
    А я ничего не констатировал — что они хранят в открытом виде. Но сам факт, что высылают на почту мне мой пароль в открытом виде, не нравится. Никогда не любил, когда так делают. Кроме случаев, когда пароль генерируется при регистрации.

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