Как стать автором
Обновить

Комментарии 26

Вся статья о том, как в onAttach сохранить ссылку на контекст.
Причем даже тут неверно. Надо не ссылку на контекст, а ссылку на интерфейс, чтобы каждом вызове не приводить к типу.
В 2018, когда я поддерживал приложение, написанное с использованием EventBus от greenrobot, я очень сильно матерился на того, кто в 2016 это делал.
1. по какой именно причине? В своей практике я сталкивался с message race — когда два объекта взаимно уведомляли друг друга и порой вступали в цикл. Кроме того, если источников сообщений очень много — логику с ними становится тяжело разбирать, какое сообщение за кем вызывается и к чему это приводит.

2. альтернативное решение из коробки для передачи сообщения от одной части к другой?

я не адвокат greenrobot, но они в индустрии уже давно, уже в 2016 их многие библиотеки считались золотым стандартом.

После того, как прочухал и отстрада п.1 выше, в своих проектах на любых платформах стараюсь воздерживаться от дополнительных источников и приемников event, предпочитаю выстраивать архитектуру с более ясной инфраструктурой данных
1. Ужаснейший антипатерн потому что. Невозможно чисто по коду и за адекватное время отследить ни отправителей событий для конкретного получателя, ни получателей по конкретному отправителю. Мне в наследство прилетало два проекта с eventbus. Первый — целиком построен на ней и это адовый треш, понять, что откуда летит и кем принимается — не реально. Второй — eventbus используется всего в паре мест, но чтобы найти источник события нужно пользоваться поиском по всему проекту.
2. Rx или LiveData.
Если продолжить развивать эту тему, то следующей стадией этого Hello World будет одна ViewModel на активити и фрагмент с общей LiveData-переменной, значение которой в третьей версии приложения будет автоматом сохраняться в репозитории через SharedPreferences, которые в четвёртой версии могут трансформироваться до Room.
Почему бы просто не сделать так
if (getActivity() != null && !getActivity().isFinishing()) {
    ((MainActivity) getActivity()).fragmentMail(counter);
} 

В данном случае, контекст всегда будет у одной и той же активити, да и интерфейс никакой не нужен
При необходимости можно еще добавить проверку на класс
Все дела
В таком случае фрагмент будет жестко привязан к MainActivity, т.е. если использовать этот фрагмент в другой активити, то придется во фрагменте еще проверку и кастинг для другой активити добавлять. Не тестируемо, не расширяемо.

Интерфейс – это, пожалуй, единственная здравая мысль в статье. Так что, если хочется простоты, то на котлине можно сделать так:


(activity as? Postman)?.fragmentMail(counter)

Или же сделать реализацию интерфейса обязательной, в зависимости от требований:


(activity as Postman).fragmentMail(counter)

Конечно, в этом случае, если активити-хозяин не реализует необходимый интерфейс, то приложение упадет.


А вот проверку на isFinishing я бы во фрагменте не делал – это совсем не забота фрагмента следить за жизненным циклом активити. Если эта проверка нужна, то ее сама активити и должна делать.

А зачем фрагмент расширен интерфейсом постман? зачем он нужен?
а как же clean architecture и тп? пример из серии «как не нужно программировать».
А потом все дружно удивляются от куда у нас столько говнокодеров
Спасибо за подробную статью, как раз изучаю разработку на Андроиде на kotlin, и в сети явно не хватает таких вот подробных примеров.
1) Разве аргументы и бандлы отменили? Intent?
2) Presenter? ViewModel?
3) можно и так, но врядли твои коллеги одобрят+при перевороте значение потеряется
Джунам после такого надо больно по пальцам бить, чтобы больше так не делали.
Зачем фрагменту имплементировать Почтальона, если он так и так стучит к активности, которая имплементирует почтальона?
mikaakim, Вы правы, спасибо!
В котлин коде (да и в джава тоже) — зачем мы проверяем и берём активность, если нам нужен почтальон?
Т.е. сразу так и написать if ( context is Postman) this.postman = context
Всегда делал так. Это даст больше универсальности, т.к. postman может приходить, например, не только из контекста, но и передаваться сообщением или через save/restore или ещё десятком способов. Фильтры там нагородить можно будет.
Внезапно недавно обнаружил что можно сделать
SomeShit.kt:
object SomeShit
{
var SomeCounter: Int = 0
}


после чего в фрагменте можно писать в SomeShit.SomeCounter, а в активности читать оттуда и наоборот. Чувствую что есть какой то подвох, потому что слишком просто получается но не могу придумать какой именно.

На самом деле примерно так и надо (ну не именно так, а чуть хитрее). Я вообще не понимаю, почему большинство примеров начинаются с хранения состояния в активити и попыток его сохранить/передать при повороте экрана, сворачивании приложения и переходе на другую активити.
Всей этой боли можно избежать, если сделать иначе: хранить состояние отдельно от активити в более стабильном месте: например, в статическом поле, в поле application, а что-то долговременное вообще сохранять в preferences.


Есть некоторые моменты, связанные с многопоточностью и activity lifecycle (оно может создаваться заново, поэтому ссылку на него хранить нельзя, но при этом его надо как-то уведомлять об изменения состояния). В androidx появились стандартные классы, которые делают это адекватным образом:


  1. Класс для хранения состояния, который может уведомлять подписавшихся, причем состояние можно изменять и из UI потока, и из другого.
  2. У activity появился lifecycleOwner, благодаря чему активити при завершении работы будет отписано от уведомлений.
    Я не использовал этот подход для чего-то сложного, но на простых примерах очень понравилось — код намного проще и короче.

Подвох в том, что это, по сути, глобальная переменная, со всеми вытекающими: отсутствие инкапсуляции, нерасширяемость в результате невозможности иметь более одного независимого экземпляра класса и т.д.

Плюс еще надо будет нагородить костылей, чтобы сохранять состояние такого класса, иначе нет никаких гарантий, что значения останутся после того, как приложение ушло в бэкграунд.
По-хорошему, надо во фрагменте создать не поле activity, а поле postman. И создать метод
fun setPostman(postman: Postman) {
    this.postman = postman
}

и из активности вызвать fragment.setPostman(this)

Не говорю, что это единственное и лучшее решение, но если речь о передаче данных через интерфейс, то стоит делать так, иначе джуны рискуют нарваться как минимум на критику ментора

Нет, как раз так делать не стоит. Жизненные циклы у фрагмента и у активити разные, ОС может пересоздать фрагмент, и ваш setPostman метод не будет вызван.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории