Комментарии 34
Я бы сделал toast (и большинство ваших функций) как фунцию-расширение для Context, на мой взгляд решение с синглтоном App спорное.
TextWatcherObject не обязательно должен быть классом, в Kotlin доступны интерфейсы с реализацией методов по умолчанию.
SharedPreferences я бы сделал в виде следующего делегата (для всех типов, а не только для строки)
TextWatcherObject не обязательно должен быть классом, в Kotlin доступны интерфейсы с реализацией методов по умолчанию.
SharedPreferences я бы сделал в виде следующего делегата (для всех типов, а не только для строки)
1) Toast. К сожалению context есть не везде. Поэтому я и перешел к варианту доступному везде и всегда.
2) TextWatcherObject. Согласен, можно сделать и интерфейсом.
3) SharedPreferences. Интересное решение. Спасибо, испробую на досуге.
2) TextWatcherObject. Согласен, можно сделать и интерфейсом.
3) SharedPreferences. Интересное решение. Спасибо, испробую на досуге.
По поводу вашего делегата для SharedPreferences. Как я понял, у него нельзя задать default по умолчанию. И как можно в процессе работы поменять значение preferences внутри? Пока приходит на ум только оборачивание в класс, в который при инициализации передавать SharedPreferences. И тогда пересоздавать этот контейнер.
Сильно упрощенный пример использования:
// "за кадром" получаем ссылку на SharedPreference ( скорее всего инжектим)
var myValue: String by Preference(
preference,
"KEY", // понятное дело, в реальном коде здесь будет ссылка на константу
"DEFAULT_VALUE"
)
// кладем новое значение в shared pref
myValue = "NEW_VALUE"
Пояснение по вопросу. Как поменять хранилище значений? Поменять значение с типом SharedPreferences. И я про default, который можно определить в самом конструкторе, чтобы каждый раз не передавать «константу»
Ну про default лучший вариант сделать:
а вот «поменять хранилище значений» я честно не знаю (да и не могу придумать зачем)
//предварительно сделав Preference open
class IntPreference(
private val preferences: SharedPreferences,
private val name: String
) : Preference<Int>(preferences, name, 0)
а вот «поменять хранилище значений» я честно не знаю (да и не могу придумать зачем)
Что за жесть, 2020 год был не за горами, на хабре постили, как из аппликейшна сделать синглтон…
Не статья, а набор вредных советов. Особенно порадовало «Но имеем что имеем». Действительно, и так уже утомились, зачем ещё в чём-то разбираться.
Не статья, а набор вредных советов. Особенно порадовало «Но имеем что имеем». Действительно, и так уже утомились, зачем ещё в чём-то разбираться.
Правильнее было бы назвать эту статью — «Как делать нельзя»
Сборник костылей и вредных советов. Где-то в углу заплакали паттерны и чистая архитектура
Зачем хранить ссылку на activity в app, если можно там просто держать имя класса?
Для возможности получить доступ из любой точки по необходимости. А так же для методов получения переведенной строки в любой точке без контекста
Пересмотрите подход. В двух словах — вам не должны быть нужны строки локализации вне активити/фрагмента. Текст отображается на UI, а не там, где нет контекста. Тот код, где вы решаете какой текст вывести (бизнес логика), не должен зависеть от каких-то там ресурсов
ВСЕ ссылки на активити нужны при случаях создания ненужных копий активити(клики на уведомлениях, обработка Deep Link). И не надо говорить про singleInstance и прочие неработающие флаги(прибамбасы). Со всеми вытекающими последствиями c(над) LiveData. В правильной архитектуре LiveData — абсолютное зло.
В случае создания ненужных копий активити выглядит смешно архитектура с одной активити, когда сама система не может ее поддерживать
При создании копии активити остаются в памяти(в стеке) она сама, а также НЕНУЖНЫЕ ВСЕ объекты, которые связаны с ней. Вы должны каким-то образом удалять ВЕСЬ этот ненужный хлам. Если вы используете потоковую(реактивную) архитектуру это просто ее разрушает. Решение этого — Moxy или пилите свою службу доставки данных(actions) до конкретного получателя(View, Presenter). Кому как нравиться. В меня наверно 20 лет вдалбливали, что данные ВСЕГДА ПАССИВНЫ. UI активен. В потоковых(реактивных) структурах выборка данных всегда полностью отвязана от UI. В UI(Presenter) передаются либо оповещения о прошедших событиях либо неизменяемые данные — по сути архитектура Flutter. Т.е. архитектура выглядит как View->Presenter->Запрос->Выборка данных(бизнес логика)->Результат->Служба доставки->Presenter->View. Служба доставки это Moxy или самописка. Данная архитектура с легкостью льется на Flutter — да на что угодно.
Уточнение — в потоковых системах (не реактивных и не во Flutter — это частные случаи реализаций) понятия состояние вообще нет. Это динамические системы. Всё течёт, всё меняется(Гераклит).
По поводу RecyclerView, не пробовали ли вы использовать ListAdapter, который использует DiffUtils под капотом? Было бы более интересно если бы вы описали старинный кейс, когда при notify, пересоздается view. А если в общем, всё что вы здесь представили напрочь нарушает разделение по слоям приложение. Если у вас безнес логика напрямую вызывает View, у меня для вас плохие новости
Не пробовали. Сразу использовался RecyclerView.Adapter без DiffUtils. Что касается пересоздания — как я понял из того, что нашел в интернете, RecyclerView сам решает, переиспользовать View или создать новую, а почему и как он решает — надо копаться под капотом. Единственно как удалось заметно повлиять на пересоздание — через обращение напрямую к View в ViewHolder, а не через notify. Но даже так, при самописном свайпе и смещении позиции View — периодически View создавалось, а не переиспользовалось. К слову, странность с пересозданием наблюдалась еще и у знакомых на другом проекте с другой архитектурой и стилем написания. Но все равно не исключаю возможности, что просто что-то сделано в адаптере не так.
Что касается «бизнес-логики» и View. Различные слои у нас точно есть, может не везде идеально и где-то глаз успел замылиться, но буду работать над этим. Учиться, учиться и еще раз учиться :)
Что касается «бизнес-логики» и View. Различные слои у нас точно есть, может не везде идеально и где-то глаз успел замылиться, но буду работать над этим. Учиться, учиться и еще раз учиться :)
изучите что такое di и clean architecture — поймете как можно удобно все решить. А еще включите leak canary и попробуйте написать java юнит тесты и поймете почему все эти предложения — тихий ужас.
Правильно ли я понял, что это «пятничный» саркастический пост а-ля вредные советы? :)
Нет, это серьезный пост в четверг, от которого у многих пригорело
Я думаю у многих пригорело потому что некоторые практики, которые вы описали, идут в разрез с представлениями о правильной архитектуре.
Дам вам лишь конструктивный фидбэк, без негатива)
Самое важное что нужно понять: делать что-то статическим полем для того, чтобы «можно было использовать откуда угодно» — плохая идея и code smell. Усиливается связь между вашими компонентами, что ухудшает тестирование и рефакторинг. Или захотите вы вашу утилиту переиспользовать в другом приложение, а там неявная завязка на ваш другой App-класс, которого нет в новом приложении.
Любой класс/функция должны получать свои зависимости из вне, как аргумент функции или конструктора.
Также, показ тоста из утилит — идея не очень. Всё, что касается отображения, должно происходить в активити/фрагмент. Смешение ответственностей очень плохо влияет на код, порождает много ошибок и нарушает SOLID.
Дам вам лишь конструктивный фидбэк, без негатива)
Самое важное что нужно понять: делать что-то статическим полем для того, чтобы «можно было использовать откуда угодно» — плохая идея и code smell. Усиливается связь между вашими компонентами, что ухудшает тестирование и рефакторинг. Или захотите вы вашу утилиту переиспользовать в другом приложение, а там неявная завязка на ваш другой App-класс, которого нет в новом приложении.
Любой класс/функция должны получать свои зависимости из вне, как аргумент функции или конструктора.
Также, показ тоста из утилит — идея не очень. Всё, что касается отображения, должно происходить в активити/фрагмент. Смешение ответственностей очень плохо влияет на код, порождает много ошибок и нарушает SOLID.
Спасибо за конструктивный фидбэк, без негатива.
Тестирование — тут мне нечего сказать, на проекте у нас его нет и я не приверженец этого подхода.
Рефакторинг — сколько его ни было, никаких особых проблем не испытывал из-за своего подхода. Скорее проблемы случаются, когда ленишься прописывать сеттеры и обращаешься к переменным напрямую. Благо с kotlin их теперь можно переопределить в любой момент без труда
Если передавать аргументы чуть сложнее базовых классов — их все равно придется переносить в новый проект. Если модуль удобен и универсален, пусть я может и потрачу чуть больше времени, но перенесу его полностью со всеми связями. Какие то базовые вещи, которые оправдали свою пользу — в любом случае окажутся и в новом проекте. Если модуль все же специфичен — и так и так придется его менять и адаптировать.
Если поле существует и должно существовать в единственном экземпляре и оно может понадобиться по всему проекту, то почему сразу надо писать кучу кода, чтобы все о ней узнали? При сферическом коне в вакууме — это будет лучше статики. Ну а в реальности куча кода — либо вручную, либо через какие-то библиотеки.
В идеальной работе приложения — toast и не выскочит. Но ради тех редких случаев, когда он понадобится, прокидывать во все методы контекст или какие либо колбеки, создавая связи, которые в теории могут обернуться утечкой памяти? А так — и утечек нет и сообщение отобразится не зависимо от того, что случилось с приложением.
В любом случае это то, к чему я пришел исходя из рабочих проектов. То, что работает и что я с большой вероятностью возьму и в следующий раз. То, что получено путем проб и ошибок. Да, где-то это больше о работе и удобстве здесь и сейчас, чем о красоте и потенциальном удобстве когда-то в будущем.
Я постараюсь учесть все, на что мне указали и сделать свой код еще лучше. И я рад, что не все закатывают глаза и сразу лепят минус. Еще раз спасибо за конструктив :)
Тестирование — тут мне нечего сказать, на проекте у нас его нет и я не приверженец этого подхода.
Рефакторинг — сколько его ни было, никаких особых проблем не испытывал из-за своего подхода. Скорее проблемы случаются, когда ленишься прописывать сеттеры и обращаешься к переменным напрямую. Благо с kotlin их теперь можно переопределить в любой момент без труда
Если передавать аргументы чуть сложнее базовых классов — их все равно придется переносить в новый проект. Если модуль удобен и универсален, пусть я может и потрачу чуть больше времени, но перенесу его полностью со всеми связями. Какие то базовые вещи, которые оправдали свою пользу — в любом случае окажутся и в новом проекте. Если модуль все же специфичен — и так и так придется его менять и адаптировать.
Если поле существует и должно существовать в единственном экземпляре и оно может понадобиться по всему проекту, то почему сразу надо писать кучу кода, чтобы все о ней узнали? При сферическом коне в вакууме — это будет лучше статики. Ну а в реальности куча кода — либо вручную, либо через какие-то библиотеки.
В идеальной работе приложения — toast и не выскочит. Но ради тех редких случаев, когда он понадобится, прокидывать во все методы контекст или какие либо колбеки, создавая связи, которые в теории могут обернуться утечкой памяти? А так — и утечек нет и сообщение отобразится не зависимо от того, что случилось с приложением.
В любом случае это то, к чему я пришел исходя из рабочих проектов. То, что работает и что я с большой вероятностью возьму и в следующий раз. То, что получено путем проб и ошибок. Да, где-то это больше о работе и удобстве здесь и сейчас, чем о красоте и потенциальном удобстве когда-то в будущем.
Я постараюсь учесть все, на что мне указали и сделать свой код еще лучше. И я рад, что не все закатывают глаза и сразу лепят минус. Еще раз спасибо за конструктив :)
А вот кстати, как по новым гайдам, используя Lifecycle, узнать, какая активити запущена, когда находишься в функции onMessageReceived (в сервисе FirebaseMessagingService)? У меня не получилось разобраться.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Самодельный «сахар» для Android проекта или «Как делать нельзя»