Пять стадий написания LayoutManager
1. 😨 Быть такого не может, чтобы ранее такую штуку никто не делал!
2. 😡 Нет, ну почему никто не запилил?!
3. 🤬 Может, как-нибудь попроще, без менеджера?
4. 😢 *гуглинг в гитхабе*
5. 😌 Ладно-ладно, пойду писать.
Итого: Flow (раскладывает в строчку, переносит на новую при необходимости) с возможностью ограничить количество строк и показать специальную вьюшку «ещё 100500».
https://github.com/Miha-x64/FlowLayoutManager/
1. 😨 Быть такого не может, чтобы ранее такую штуку никто не делал!
2. 😡 Нет, ну почему никто не запилил?!
3. 🤬 Может, как-нибудь попроще, без менеджера?
4. 😢 *гуглинг в гитхабе*
5. 😌 Ладно-ладно, пойду писать.
Итого: Flow (раскладывает в строчку, переносит на новую при необходимости) с возможностью ограничить количество строк и показать специальную вьюшку «ещё 100500».
https://github.com/Miha-x64/FlowLayoutManager/
Gson 2.9.0
Пару месяцев назад тихой сапой вышло обновление объекта обожания всех рефлектологов и ансейфоведов.
Моё внимание привлекли эти два изменения:
• Support
• Add
Вот и отлично! Не прошло и десяти л… а, нет, прошло :)
Того гляди, скоро и
https://github.com/google/gson/blob/master/CHANGELOG.md
Пару месяцев назад тихой сапой вышло обновление объекта обожания всех рефлектологов и ансейфоведов.
Моё внимание привлекли эти два изменения:
• Support
EnumMap
deserialization (#2071)• Add
GsonBuilder.disableJdkUnsafe()
(#1904)Вот и отлично! Не прошло и десяти л… а, нет, прошло :)
Того гляди, скоро и
EnumSet
начнёт поддерживать.https://github.com/google/gson/blob/master/CHANGELOG.md
GitHub
gson/CHANGELOG.md at master · google/gson
A Java serialization/deserialization library to convert Java Objects into JSON and back - gson/CHANGELOG.md at master · google/gson
Итак, $company хочет вас схантить
Anonymous Poll
23%
Пойду собеситься, дальше посмотрю, что по условиям
42%
Скажите, чё по бабкам! После этого можно разговаривать
23%
Брысь! Люблю своё нынешнее место работы
3%
Вот кого отстажировали и вырастили, тех и нанимайте!
9%
Возьмите меня уже хоть куда-нибудь…
Adapter Delegates
Почему я не использую «классические» адаптерДелегаты?
Смотрим невооружённым глазом:
• можно забыть нужный делегат (крэш в рантайме),
• а можно забыть удалить лишний (недостижимый код),
• нельзя использовать несколько делегатов для одного типа данных.
Первые два пункта — классика: в Retrofit можно переехать с RxJava на suspend и забыть убрать
Третий пункт — ваще критикал: я хочу пару разных делегатов-заголовков, оба с типом
Короче, я создал, хоть и столько лет спустя.
Под капотом там просто сдвоенный
В комплекте валяется
Лучи добра всем трудящимся!
Почему я не использую «классические» адаптерДелегаты?
Смотрим невооружённым глазом:
val adapter = ListDelegationAdapter<List<Animal>>(А если приподнять завесу «няшного» Kotlin DSL, то вообще увидим ЭТО:
catAdapterDelegate(...),
public boolean isForViewType(List<Animal> items, int position) {Напрашивается следующий вывод:
return items.get(position) instanceof Cat;
}
• можно забыть нужный делегат (крэш в рантайме),
• а можно забыть удалить лишний (недостижимый код),
• нельзя использовать несколько делегатов для одного типа данных.
Первые два пункта — классика: в Retrofit можно переехать с RxJava на suspend и забыть убрать
CallAdapter.Factory
, а в Gson есть «изкоробочные» адаптеры для, скажем, AtomicIntegerArray
, которые психически здоровому человеку не нужны, а больному — вредны.Третий пункт — ваще критикал: я хочу пару разных делегатов-заголовков, оба с типом
CharSequence
но разными шрифтами, отступами, размерами. Или в вертикальном списке имею горизонтальные списки всякого разного, которые сплошь instanceof List
.Короче, я создал, хоть и столько лет спустя.
Под капотом там просто сдвоенный
List
из элементов и их делегатов.В комплекте валяется
ItemDecoration
, который могёт… а, не буду пересказывать скриншот, он в конце readme.Лучи добра всем трудящимся!
GitHub
GitHub - Miha-x64/Delegapter: Yet another adapter delegate library.
Yet another adapter delegate library. Contribute to Miha-x64/Delegapter development by creating an account on GitHub.
Платформа для донатов
Очередная #идея, которую я (да ладно, будем честны, подслушал и) дарю любому банку/финтеху.
Организация или отдельно взятый волонтёр открывает счёт для донатов. Для счёта генерируется специальная общедоступная страничка — там эквайринг и история операций, к которой автоматом прикрепляются электронные чеки.
Всё, любой благотворительный проект может вылезти из пещерного века, выбросить камень, плуг, соху, десять карт разных банков и систем (потому что эквайринг) и гигабайт фоток чеков, снятых в темноте на телефон.
Очередная #идея, которую я (да ладно, будем честны, подслушал и) дарю любому банку/финтеху.
Организация или отдельно взятый волонтёр открывает счёт для донатов. Для счёта генерируется специальная общедоступная страничка — там эквайринг и история операций, к которой автоматом прикрепляются электронные чеки.
Всё, любой благотворительный проект может вылезти из пещерного века, выбросить камень, плуг, соху, десять карт разных банков и систем (потому что эквайринг) и гигабайт фоток чеков, снятых в темноте на телефон.
Опять про андроид. И снова сохранение состояния
Есть паттерн такой — назовём его custom composite view: наследуем любой подходящий layout, инфлейтим внутрь него
Но, как говорится, есть один нюанс. Если на экране более одного такого компонента, айдишки внутри начинают дублироваться. В итоге при сохранении состояния побеждает тот, кто сохранялся последним, щедро раздавая всем компонентам своё состояние при восстановлении. Для вьюшек с пользовательским вводом, таких как текстовые поля, слайдеры, чузеры и вот это всё, проблема прям критичная.
Нужно переопределить
а) можно вызвать
б) а можно взять какой-нибудь
Есть паттерн такой — назовём его custom composite view: наследуем любой подходящий layout, инфлейтим внутрь него
<merge>
и переиспользуем целиком. Описан в этой статье и много где ещё.Но, как говорится, есть один нюанс. Если на экране более одного такого компонента, айдишки внутри начинают дублироваться. В итоге при сохранении состояния побеждает тот, кто сохранялся последним, щедро раздавая всем компонентам своё состояние при восстановлении. Для вьюшек с пользовательским вводом, таких как текстовые поля, слайдеры, чузеры и вот это всё, проблема прям критичная.
Нужно переопределить
dispatch(Save|Restore)InstanceState
, которые отвечают за сохранение-восстановление всего поддерева. Далее — вариации:а) можно вызвать
dispatch(Freeze|Thaw)SelfOnly(container)
, как это делает ресайклер, и в on(Save|Restore)InstanceState
сделать всю нужную работу. Придётся унаследовать SavedState
, к сожалению;б) а можно взять какой-нибудь
ParcelableSparseArray
и передать его в super.dispatchSaveInstanceState
и положить его в container
под своим id, изолировав таким образом состояние всего компонента в отдельное пространство айдишек.Lucas Rocha
Custom Layouts on Android
If you ever built an Android app, you have definitely used some of the built-in layouts available in the platform—RelativeLayout, LinearLayout, FrameLayout, etc. They are our bread and butter for building Android UIs.
Курс по ФП на Java
Знаю, что «восьмёрке» уже восемь лет, но этот курс мне скинули буквально месяц назад. Функциональная композиция, стримы-коллекторы, CompletableFuture и прочие монадки в задачах и примерах.
https://stepik.org/course/91497/syllabus
Знаю, что «восьмёрке» уже восемь лет, но этот курс мне скинули буквально месяц назад. Функциональная композиция, стримы-коллекторы, CompletableFuture и прочие монадки в задачах и примерах.
https://stepik.org/course/91497/syllabus
Stepik: online education
Java. Functional Programming
This course explains how to leverage a wide variety of functional programming concepts to solve challenges in Java. After completing it, you will have a strong understanding of lambda expressions, method references, functional interfaces, composition, stream…
Javanese Online
Немного баготворительности. В каком сценарии сеттер сработает неверно?
За три минуты и с первой попытки побеждает @italankin!
Прикрепляю фикс.
Прикрепляю фикс.
Векторные иконки в Android: анатомия, патофизиология, диагностика и хирургия
UPD: намутил промокод
UPD: намутил промокод
MikeGorunov2022JRGpc
UPD: слайдыMobius 2022 Autumn. Конференция для мобильных разработчиков
Доклад Векторные иконки в Android: анатомия, патофизиология, диагностика и хирургия — Mobius 2022 Autumn. Конференция для мобильных…
Все мы используем векторные иконки — Vector Drawable. Как они устроены, каким образом парсятся и отрисовываются на канве — всё это можно загуглить. Спикер расскажет про более сложные моменты: как их оптимизировать, найти бессмысленные группы и бесполезные…
Javanese Online
Векторные иконки в Android: анатомия, патофизиология, диагностика и хирургия UPD: намутил промокод MikeGorunov2022JRGpc UPD: слайды
Вопрос, который мне не задали на конференции: является ли SVG path-data Тьюринг-полным языком?
Anonymous Quiz
41%
да
59%
не
Javanese Online
IllegalStateException: cannot make a new request because the previous response is still open: please call response.close() Занятный крэш меня сегодня посетил. Типовое обновление токена. Нетрудно понять, где именно баг, но как именно вы бы его чинили?
А правильно ответил @dimitreuz: если ответ от неудачного запроса нам ценен, нужно его вычитать и закрыть, освободив сокет для следующего запроса.
Полраза в жизни встречал
Ожидание:
отрицание, гнев, торг, пост в канал, кастомный флоу.
ViewModel
+ MutableStateFlow
. Попросили объяснить, как использовать. Чем дальше в лес, тем меньше понимания, как и зачем этим вообще пользоваться и под какими веществами это придумали 🤦Ожидание:
val vm by viewModels(factory = { MyCoolViewModel(args) })
Реальность:val vm: MyCoolViewModel by viewModels(factoryProducer = {Ожидание:
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(...): T =
MyCoolViewModel(args) as T
}
})
val stateSmth = savedStateHandle.getMutableStateFlow("smth", defaultSmth)
Реальность:отрицание, гнев, торг, пост в канал, кастомный флоу.
Какой алгоритм решит мою задачу?
У нас есть один язык, который самостоятельно выбирает наиболее подходящие алгоритмы и структуры данных из имеющихся.Этим студентом был SQL. Если повезёт, задача выбора алгоритма сведётся к написанию SQL-запроса, а вывод
У нас есть один язык, который самостоятельно выбирает наиболее подходящие алгоритмы и структуры данных из имеющихся.
EXPLAIN
прямым текстом назовёт нужный алгоритм.Forwarded from Coffee&Code | Mobile Community
Традиционный пятничный анонс на выходные 4-5 февраля.
🌆Города, в которых на этой неделе пройдут встречи:
ANDROID:
🤖 МОСКВА
🤖 САНКТ-ПЕТЕРБУРГ
iOS:
🍏 АЛМАТЫ
🍏 АНТАЛЬЯ
🍏 АСТАНА
🍏 БАКУ
🍏 БАТУМИ
🍏 ВІЛЬНЯ
🍏 ЕКАТЕРИНБУРГ
🍏 ЕРЕВАН
🍏 ЛОНДОН
🍏 МИНСК
🍏 МОСКВА
🍎 ПОДГОРИЦА
🍏 РОСТОВ-НА-ДОНУ
🍏 САНКТ-ПЕТЕРБУРГ
🍏 СОЧИ
🍏 СТАМБУЛ
🍏 ТАШКЕНТ
🍏 ТБИЛИСИ
🍏 ЧЕРЕПОВЕЦ
👩💻 Пообщаемся на технические темы, обсудим интересные события из мобильной разработки, разберем вопросы с собеседований и поделимся опытом!
Полезности:
👏 Ozon Tech Community Mobile Meetup в Алматы
👋 Опрос - Чем живете и дышите в мире IT
🤝 Ресурсы наших коллег
🧑💻 Наш LinkedIn
Ждём абсолютно всех.
🌆Города, в которых на этой неделе пройдут встречи:
ANDROID:
🤖 МОСКВА
🤖 САНКТ-ПЕТЕРБУРГ
iOS:
🍏 АЛМАТЫ
🍏 АНТАЛЬЯ
🍏 АСТАНА
🍏 БАКУ
🍏 БАТУМИ
🍏 ВІЛЬНЯ
🍏 ЕКАТЕРИНБУРГ
🍏 ЕРЕВАН
🍏 ЛОНДОН
🍏 МИНСК
🍏 МОСКВА
🍎 ПОДГОРИЦА
🍏 РОСТОВ-НА-ДОНУ
🍏 САНКТ-ПЕТЕРБУРГ
🍏 СОЧИ
🍏 СТАМБУЛ
🍏 ТАШКЕНТ
🍏 ТБИЛИСИ
🍏 ЧЕРЕПОВЕЦ
👩💻 Пообщаемся на технические темы, обсудим интересные события из мобильной разработки, разберем вопросы с собеседований и поделимся опытом!
Полезности:
Ждём абсолютно всех.
Please open Telegram to view this post
VIEW IN TELEGRAM
OOM Resurrector
У меня на сервере постгрес раз в неделю съедает всю доступную память и своп. Настройки крутил, всё уменьшал, но нет никакого аналога нашего
В интернете есть скрипт OOM-воскресителя, прикладываю свою версию, которая ориентируется на RAM+swap.
У меня на сервере постгрес раз в неделю съедает всю доступную память и своп. Настройки крутил, всё уменьшал, но нет никакого аналога нашего
-Xmx
, поэтому и нет гарантии, что постгря не падёт жертвой ООМ-киллера.В интернете есть скрипт OOM-воскресителя, прикладываю свою версию, которая ориентируется на RAM+swap.
#!/bin/bashКоманда:
THRESHOLD=1000 # MB
INTERVAL=60 # s
while :
do
free=$(free -tm | awk '/^Total:/{print $4}')
echo "Free $free""MB"
if [ "$free" -lt $THRESHOLD ]
then
systemctl restart postgresql@14-main
echo '$ systemctl restart postgresql@14-main'
fi
sleep $INTERVAL
done
nohup ./pg-resurrect.sh > pg-resurrect.log &
О сколько нам открытий чудных готовит спека DNS!
Держу в курсе: доменное имя без точки в конце считается неполным, относительным. В эпоху умерших интранетов и локальных сетей об этом уже никто не вспомнит…
Обнаружено в процессе чтения исходников OkHttp.
https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de
Держу в курсе: доменное имя без точки в конце считается неполным, относительным. В эпоху умерших интранетов и локальных сетей об этом уже никто не вспомнит…
Обнаружено в процессе чтения исходников OkHttp.
https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de
Webmasters Stack Exchange
How can URLs have a dot . at the end, e.g. www.bla.de.?
I never believed URLs could have a period at the end, as in www.google.de. (which obvious is not working).
However, www.youtu.be. is working perfectly well. How did they manage that?
However, www.youtu.be. is working perfectly well. How did they manage that?
Сравнительная характеристика JDBC и Android SQLite
API SQLite в Android сильно отличается от привычного для джавистов JDBC. Вот краткий справочник, что как называется и каким образом себя ведёт.
Помощник для миграции
В Android мы получаем базу данных от
База данных
Отправная точка в Android называется
Соединение
JDBC:
В Android же соединения спрятаны от нас, под капотом реализован пул.
Готовимся выполнить запрос
JDBC:
Выполнить запрос
JDBC:
И нужно не забыть закрыть
Транзакции
Android
Мутации
Для них удобнее всего подходят подготовленные запросы.
JDBC:
Вре́менные таблицы и триггеры
Вот здесь спряталась хитрость. В любой СУБД временные таблицы существуют только в пределах соединения, их создавшего. Именно в SQLite есть ещё один нюанс — триггеры тоже локальны для соединения ввиду отсутствия сервера. В Android же это ограничение не действует, как если бы
API SQLite в Android сильно отличается от привычного для джавистов JDBC. Вот краткий справочник, что как называется и каким образом себя ведёт.
Помощник для миграции
В Android мы получаем базу данных от
SQLiteOpenHelper
, где есть коллбэки для миграции. В JDBC такой штуки нет, зато есть flywaydb.org и liquibase.org.База данных
Отправная точка в Android называется
SQLiteDatabase
, в JDBC — DataSource
, зачастую из HikariCP.Соединение
JDBC:
DataSource#getConnection() → Connection
.В Android же соединения спрятаны от нас, под капотом реализован пул.
Готовимся выполнить запрос
JDBC:
Connection#createStatement() → Statement
Android: неть, не готовимся.Выполнить запрос
JDBC:
Statement#execute(String) → ResultSet
Android: SQLiteDatabase#(raw)Query(…) → Cursor
Таким образом, Android избавляет нас от получения коннекшона и создания стейтмента, мы получаем курсор на руки буквально вызовом одного метода.// JDBCЧтение
hikariPool.connection.use { conn ->
conn.createStatement().use { stmt ->
stmt.execute("SELECT …").use { rs ->
TODO()
}
}
}
// Android SQLite
db.rawQuery("SELECT …").use { cur ->
TODO()
}
ResultSet
и Cursor
очень похожи, но в деталях есть разница. Например, ResultSet
не знает своего размера.И нужно не забыть закрыть
Statement
заодно с ResultSet
.Транзакции
Android
SQLiteDatabase
:beginTransaction(NonExclusive)()JDBC
setTransactionSuccessful()
endTransaction()
Connection
:setAutoCommit(false)Да, слово «транзакция» здесь вообще не фигурирует. Кстати, в одном из предыдущих постов у меня потекла транзакция именно потому что я забыл вернуть autoCommit.
commit() || rollback()
setAutoCommit(true)
Мутации
Для них удобнее всего подходят подготовленные запросы.
JDBC:
Connection#prepareStatement(SQL) → PreparedStatement
Android: SQLiteDatabase#compileStatement(SQL) → SQLiteStatement
Тут принципиальной разницы нет.Вре́менные таблицы и триггеры
Вот здесь спряталась хитрость. В любой СУБД временные таблицы существуют только в пределах соединения, их создавшего. Именно в SQLite есть ещё один нюанс — триггеры тоже локальны для соединения ввиду отсутствия сервера. В Android же это ограничение не действует, как если бы
SQLiteDatabase
оперировала лишь одним соединением. InvalidationTracker
в Room тоже написан таким образом, будто этого органичения не существует. Почему оно так работает? Для меня это открытый вопрос.Throwable — волшебный класс!
Некоторые создают эксепшон и сразу же вызывают этот метод. Это бесполезно, он ведь и так вызывается в конструкторе.
А вот в каком-нибудь другом месте можно навязать любому эксепшону текущий стек. Удачной отладки!
Метод, кстати, нефинальный. Можно переопределить его и сделать no-op, получив дешёвый эксепшон без стека.
fillInStackTrace()
: запомнить текущий стек.Некоторые создают эксепшон и сразу же вызывают этот метод. Это бесполезно, он ведь и так вызывается в конструкторе.
А вот в каком-нибудь другом месте можно навязать любому эксепшону текущий стек. Удачной отладки!
Метод, кстати, нефинальный. Можно переопределить его и сделать no-op, получив дешёвый эксепшон без стека.
getMessage()
, getCause()
, printStackTrace()
, toString()
тоже нефинальные. Если хочется пострелять с двух рук, можно переопределить их и бросить оттуда новый exception.setStackTrace()
: вообще вседозволенность. Да, можно использовать его в благих целях, дабы прятать интринсики языка, а можно приколоться над коллегами!