uk
Feedback

Не попадись на ботовода! Telemetrio знаходить і позначає такі канали мітками 👉 Хочеш бачити мітку, оформляй підписку

По-явански

По-явански

Відкрити в Telegram

Пишу про Java-технологии, Kotlin и Android-разработку. Редко но метко. Кодревью: http://javanese.online/%D1%80%D0%B0%D0%B7%D0%B1%D0%BE%D1%80_%D0%BA%D0%BE%D0%B4%D0%B0/ Поболтать IRL: @coffeeCodeEverywhere Автор: @Harmonizr Менеджер по рекламе: /dev/null

Показати більше
827
Підписники
Немає даних24 години
-17 днів
+530 днів
Архів дописів
Редактируемый список — EditText внутри RecyclerView Очень много лулзов на StackOverflow по этому вопросу. Написать адаптер с эдиттекстами под силу каждому, а вот получить назад отредактированные данные могут не только лишь все. Рассмотрим для начала очевидно нерабочий способ: for-each по вьюшечкам. Как ни странно, я видел такое решение в продакшене. Чтобы оно работало, ресайклер был завёрнут в скроллвью! Следующий сомнительный вариант: нацепить TextWatcher и записывать результат Editable.toString() на каждый тык пользователя по клавиатуре. Название Editable, кстати, намекает нам на мутабельность редактируемого буфера. Так возникает ещё одна стратегия — дёрнуть Editable getText(), а не слушать каждое изменение. Тогда байндинг будет выглядеть так: editText.setText(editables[position] ?: strings[position]) editables[position] = editText.text EditText создаст мутабельную копию строки. А можно сделать это самостоятельно, потому что в editables уже может лежать нужный буфер. Только нужно отвадить EditText от копирования, заменив Editable.Factory. Итоговый gist и ответ на SO.

Недавно меня посетила красивая #идея — вот бы приложения карт умели показывать слои, предоставленные другими приложениями! Открываешь Organic, 2GIS или что там сейчас есть из картографии — а туда сразу подтягиваются такси, самокаты, каршеринг, общественный транспорт, парковки — в зависимости от того, какие провайдеры установлены. Философия UNIX во всей красе. Как и большинство других идей, я не первый, к кому они приходят. Например, есть вот такая закрытая/коммерческая интеграция. А хочется — публичный интерфейс для всех.

Интерфейсы коллекций Вчера задали мне этот вопрос на собесе. Ну я и перечисляю: Iterable, Collection, List, Set, SortedSet, NavigableSet, Queue, Deque; Map, ConcurrentMap — тут остановился в ожидании реакции собеседующего. Глазами хлопаю, мол, что, надо ещё? Оказывается, это почти всё. Для пущего эффекта можно выучить: BlockingQueue, TransferQueue, BlockingDeque; SortedMap, NavigableMap, ConcurrentNavigableMap.

Опаньки, оказывается, из типового параметра можно убрать нуллабельность (подсветка синтаксиса тоже удивлена). Источник
Опаньки, оказывается, из типового параметра можно убрать нуллабельность (подсветка синтаксиса тоже удивлена). Источник

Repost from Android Broadcast
🔴 Стартует собеседование в прямом эфире Проводит Михаил Горюнов @Harmonizr, разработчик Android-приложений, библиотек и инст
🔴 Стартует собеседование в прямом эфире Проводит Михаил Горюнов @Harmonizr, разработчик Android-приложений, библиотек и инструментов. Миша будет собеседовать на позицию Android разработчика без уровня (требования здесь) P.S. Запись останется на канале #AndroidBroadcast #собеседование

Repost from Android Broadcast
🔴 Стартует собеседование в прямом эфире Проводи Михаила Горюнова @Harmonizr, разработчик Android-приложений, библиотек и инс
🔴 Стартует собеседование в прямом эфире Проводи Михаила Горюнова @Harmonizr, разработчик Android-приложений, библиотек и инструментов. Миша будет собеседовать на позицию Android разработчика без уровня (требования здесь) P.S. Запись останется на канале #AndroidBroadcast #собеседование

Парсинг ботокоманд /add_thing Name Description /add_thing Name 'Long description' Как распарсить эти команды и не сойти с ума
+1
Парсинг ботокоманд
/add_thing Name Description
/add_thing Name 'Long description'

Как распарсить эти команды и не сойти с ума? Внутри JDK периодически используется StringTokenizer, но это просто String.split() образца 1995 года. А нужный класс, который понимает кавычки, называется StreamTokenizer, и, как следует из названия, он ещё и стрим разобрать может.

Repost from Android Broadcast
Сбор заявок на участие в собеседовании Мы ищем кандидата, кто пройдет в прямом эфире собеседование на вакансию, идентичную ре
Сбор заявок на участие в собеседовании Мы ищем кандидата, кто пройдет в прямом эфире собеседование на вакансию, идентичную реальной, в прямом эфире у Михаила Горюнова @Harmonizr , разработчика Android-приложений, библиотек и инструментов. Миша будет собеседовать на позицию Android разработчика (речь не идет про уровень или года опыт), а с требованиями можно ознакомиться здесь   @android_broadcast #собеседование

Transient Iterator Что может выстрелить в этом кусочке кода: map.entries.toSet()? А вот что: entrySet().iterator().next() возвращает себя — этот же итератор, который впридачу реализовывает Map.Entry. В итоге из энтрей непустой Map всегда получается Set из единственного Entry. С одной стороны, это жёсткая подстава. С другой — экономный способ просмотреть любое количество элементов за константную память. Поэтому в тех местах, где это может быть полезно, я предоставляю оба варианта — «нормальный» итератор и «мимолётный». Так же можно поступить с итератором по строкам файла: не возбраняется фильтровать, искать префиксы или какие-то символы, а потом уже выгружать в память необходимое подмножество элементов.

Эффективное ООП Files.readAllLines(path).size Страшно? Жутко? Жизнь — боль? Вывод: это ваше ООП неэффективно, объекты нинужны, они только память тратят. Нужна функция file_count_lines(path)! абстракция либо неверно выбрана, либо коряво реализована. Рассмотрим оба варианта. Другая абстракция: Iterator<CharSequence> & Closeable. Файл проматывается по мере доступа к элементам. CharSequence можно выгрузить в память методом toString(), до этого он просто помнит отступ и длину в файле, а charAt() заглядывает в mmap. Некорявая реализация: List<CharSequence> & Closeable. Написать такой лист сильно сложнее, чем итератор, но нет ничего невозможного: • size при первом вызове проматывает файл и строит индекс — складывает позиции всех \n в массив интов — и возвращает его размер, • get(at) проматывает файл до нужного места, достраивая по пути индекс, если он неполный. Естественно, оба метода используют один и тот же индекс и умеют достраивать его по мере необходимости. Гипотетический Files.listOfLines(path).forEach() пробегает итератором по файлу ровно один раз. Самый тяжёлый сценарий, Files.listOfLines(path).map(CharSequence::toString), приведёт к накладным расходам в виде промежуточных CharSequence и индекса, но это копейки.

Приняв во внимание обратную связь сообщества, редакция приняля решение прекратить ведение канала на неопределённый срок.

Throwable — волшебный класс! fillInStackTrace(): запомнить текущий стек. Некоторые создают эксепшон и сразу же вызывают этот метод. Это бесполезно, он ведь и так вызывается в конструкторе. А вот в каком-нибудь другом месте можно навязать любому эксепшону текущий стек. Удачной отладки! Метод, кстати, нефинальный. Можно переопределить его и сделать no-op, получив дешёвый эксепшон без стека. getMessage(), getCause(), printStackTrace(), toString() тоже нефинальные. Если хочется пострелять с двух рук, можно переопределить их и бросить оттуда новый exception. setStackTrace(): вообще вседозволенность. Да, можно использовать его в благих целях, дабы прятать интринсики языка, а можно приколоться над коллегами!

Сравнительная характеристика JDBC и Android SQLite 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)()
setTransactionSuccessful()
endTransaction()
JDBC Connection:
setAutoCommit(false)
commit() || rollback()
setAutoCommit(true)
Да, слово «транзакция» здесь вообще не фигурирует. Кстати, в одном из предыдущих постов у меня потекла транзакция именно потому что я забыл вернуть autoCommit. Мутации Для них удобнее всего подходят подготовленные запросы. JDBC: Connection#prepareStatement(SQL) → PreparedStatement Android: SQLiteDatabase#compileStatement(SQL) → SQLiteStatement Тут принципиальной разницы нет. Вре́менные таблицы и триггеры Вот здесь спряталась хитрость. В любой СУБД временные таблицы существуют только в пределах соединения, их создавшего. Именно в SQLite есть ещё один нюанс — триггеры тоже локальны для соединения ввиду отсутствия сервера. В Android же это ограничение не действует, как если бы SQLiteDatabase оперировала лишь одним соединением. InvalidationTracker в Room тоже написан таким образом, будто этого органичения не существует. Почему оно так работает? Для меня это открытый вопрос.

О сколько нам открытий чудных готовит спека DNS! Держу в курсе: доменное имя без точки в конце считается неполным, относительным. В эпоху умерших интранетов и локальных сетей об этом уже никто не вспомнит… Обнаружено в процессе чтения исходников OkHttp. https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de

OOM Resurrector У меня на сервере постгрес раз в неделю съедает всю доступную память и своп. Настройки крутил, всё уменьшал,
OOM Resurrector У меня на сервере постгрес раз в неделю съедает всю доступную память и своп. Настройки крутил, всё уменьшал, но нет никакого аналога нашего -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 &

Какой алгоритм решит мою задачу? У нас есть один язык, который самостоятельно выбирает наиболее подходящие алгоритмы и структуры данных из имеющихся. Этим студентом был SQL. Если повезёт, задача выбора алгоритма сведётся к написанию SQL-запроса, а вывод EXPLAIN прямым текстом назовёт нужный алгоритм.

Пол раза в жизни встречал 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) Реальность: отрицание, гнев, торг, пост в канал, кастомный флоу.

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