Где хранится Bundle во время уничтожения / Когда работает onSaveInstanceState()
onSaveInstanceState() — вызывается, только когда Activity уничтожается по инициативе системы, а не юзера
вызывается, когда активити уничтожена из-за:
• поворота экрана
• нехватки памяти
• смены языка
• других системных событий, требующих пересоздания\временного уничтожения активити
не вызывается:
• если пользоваться ушел с экрана кнопкой "назад"
• пользователь закрыл приложение
Bundle — объект-мапа, хранящий информацию в виде "ключ-значение"
Bundle на время пересоздания активити записывается в объект ActivityRecord, отвечающий за хранение информации о нашей активити
ActivityRecord в свою очередь связан через Task c ActivityManagerService.... нормальному человеку эти тонкости знать уже не надо)
В конечном итоге Bundle сериализуется (превращается в строку) и записывается на диск (а не остается в оперативке)
...,
в Bundle нельзя сохранять тяжелые данные. но почему?
чем отличается onCreate(Bundle state) от onRestoreInstanceState(Bundle state)?
onSaveInstanceState() — вызывается, только когда Activity уничтожается по инициативе системы, а не юзера
вызывается, когда активити уничтожена из-за:
• поворота экрана
• нехватки памяти
• смены языка
• других системных событий, требующих пересоздания\временного уничтожения активити
не вызывается:
• если пользоваться ушел с экрана кнопкой "назад"
• пользователь закрыл приложение
Bundle — объект-мапа, хранящий информацию в виде "ключ-значение"
Bundle на время пересоздания активити записывается в объект ActivityRecord, отвечающий за хранение информации о нашей активити
ActivityRecord в свою очередь связан через Task c ActivityManagerService.... нормальному человеку эти тонкости знать уже не надо)
В конечном итоге Bundle сериализуется (превращается в строку) и записывается на диск (а не остается в оперативке)
...,
в Bundle нельзя сохранять тяжелые данные. но почему?
чем отличается onCreate(Bundle state) от onRestoreInstanceState(Bundle state)?
🔥1
как узнали о канале?
Anonymous Poll
18%
hubr
42%
чаты в tg
19%
посты в группах tg
2%
vk
1%
tiktok
4%
скинули ссылку друзья-знакомые
14%
другие ресурсы
Package Name != Application ID
Я был уверен, что Play Market использует имя пакета для аутентификации приложения ⇒ изменять пакет после публикации нельзя. Это не так
• package name — имя папок, в которых лежит проект. используется для
• сборки проекта
• расположения генерируемых файлов (например, com.dick.test.R)
• доступа к классам через точку (например, .MainActivity = com.dick.test.MainActivity)
следовательно, package name, прописанный в манифесте, должен обязательно совпадать с реальным названием папок
• applicationId — уникальный айдишник приложения
прописывается в buiild.gradle → android → defaultConfig
используется во всех местах, где просят прописать package name приложения — например, в Play Market и Firebase Console
даже Context.getPackageName() возвращает application id, а не package name... не спрашивайте почему
....
но есть еще интересный факт: после сборки apk в манифест приложения в поле package зашивается applicationId
Я был уверен, что Play Market использует имя пакета для аутентификации приложения ⇒ изменять пакет после публикации нельзя. Это не так
• package name — имя папок, в которых лежит проект. используется для
• сборки проекта
• расположения генерируемых файлов (например, com.dick.test.R)
• доступа к классам через точку (например, .MainActivity = com.dick.test.MainActivity)
следовательно, package name, прописанный в манифесте, должен обязательно совпадать с реальным названием папок
• applicationId — уникальный айдишник приложения
прописывается в buiild.gradle → android → defaultConfig
используется во всех местах, где просят прописать package name приложения — например, в Play Market и Firebase Console
даже Context.getPackageName() возвращает application id, а не package name... не спрашивайте почему
....
но есть еще интересный факт: после сборки apk в манифест приложения в поле package зашивается applicationId
Ставим несколько клик-листенеров на разные буквы TextView
Иногда возникает потребность отобразить сплошной текст, в котором будет несколько ссылок
Можно:
• повесить OnTouchListener и вычислять координату каждой буквы по тапу — но это больше похоже на лайфхаки из тик-тока а-ля "что делать, если сверху кружка запаяна, а снизу в ней дырка"
• засунуть в проект библиотеку с FlowLayout (а-ля LinearLayout, но с автоматическим переносом вьюх на следующую строчку) и запихать в него отдельные TextView для каждого слова — но не надо
А можно сделать быстро и встроенными средствами:
1. создаем для каждой ссылки отдельный SpannableString
2. на каждый SpannableString вешаем свой ClickableSpan(), содержащий колбэк onClick()
3. собираем все SpannableString через SpannableStringBuilder и передаем его через textView.setText(builder)
4. меняем textView.movementMethod = LinkMovementMethod, чтобы TextView научилась обрабатывать касания на отдельные участки
5. ставим цвет ссылок с помощью textView.linkTextColor = color
готово, нажатия на определенные символы будет выполнять разные действия
....
по совету читателей решил приводить небольшие куски кода - ссылки буду оставлять в комментариях
Иногда возникает потребность отобразить сплошной текст, в котором будет несколько ссылок
Можно:
• повесить OnTouchListener и вычислять координату каждой буквы по тапу — но это больше похоже на лайфхаки из тик-тока а-ля "что делать, если сверху кружка запаяна, а снизу в ней дырка"
• засунуть в проект библиотеку с FlowLayout (а-ля LinearLayout, но с автоматическим переносом вьюх на следующую строчку) и запихать в него отдельные TextView для каждого слова — но не надо
А можно сделать быстро и встроенными средствами:
1. создаем для каждой ссылки отдельный SpannableString
2. на каждый SpannableString вешаем свой ClickableSpan(), содержащий колбэк onClick()
3. собираем все SpannableString через SpannableStringBuilder и передаем его через textView.setText(builder)
4. меняем textView.movementMethod = LinkMovementMethod, чтобы TextView научилась обрабатывать касания на отдельные участки
5. ставим цвет ссылок с помощью textView.linkTextColor = color
готово, нажатия на определенные символы будет выполнять разные действия
....
по совету читателей решил приводить небольшие куски кода - ссылки буду оставлять в комментариях
👍5
Как по hashCode определяется позиция
Вероятно, все слышали, что есть структуры данных, в которых позиция, на которой будет храниться элемент, определяется его hashCode
Но hashCode — это Integer, значит, в таких структурах как HashMap, должен лежать массив длиной 2^32 ?
Нет, делается немного хитрее (на примере HashMap):
1. создается массив определенного размера например, arr.length = 16
2. когда наступает момент добавить новый элемент, вычисляется его хэш (хэш ключа). например, hashCode = 200
3. вычисляется позиция, на которую нужно положить элемент = hashCode % arr.length = 200 % 16 = 8
4. на позиции 8 создается массив. в него добавляется наш элемент
5. когда следующий элемент попадает на позицию 8, наш первый элемент сохраняет на него ссылку
да, мы получили массив связанных списков
да, нам теперь плевать на коллизии
дальше можно подумать, как эту систему балансировать, изменяя размер arr.length
....
есть ли другие подходы к сохранению данных, с вычислением позиции через hashCode элемента?
Вероятно, все слышали, что есть структуры данных, в которых позиция, на которой будет храниться элемент, определяется его hashCode
Но hashCode — это Integer, значит, в таких структурах как HashMap, должен лежать массив длиной 2^32 ?
Нет, делается немного хитрее (на примере HashMap):
1. создается массив определенного размера например, arr.length = 16
2. когда наступает момент добавить новый элемент, вычисляется его хэш (хэш ключа). например, hashCode = 200
3. вычисляется позиция, на которую нужно положить элемент = hashCode % arr.length = 200 % 16 = 8
4. на позиции 8 создается массив. в него добавляется наш элемент
5. когда следующий элемент попадает на позицию 8, наш первый элемент сохраняет на него ссылку
да, мы получили массив связанных списков
да, нам теперь плевать на коллизии
дальше можно подумать, как эту систему балансировать, изменяя размер arr.length
....
есть ли другие подходы к сохранению данных, с вычислением позиции через hashCode элемента?
👍1
Когда не нужно заменять HashMap на ArrayMap
Думаю, все в курсе — если попробовать использовать HashMap, студия предложит заменить тип на ArrayMap
Почему (короткий ответ) — ArrayMap оптимизирован под Android
Почему (правильный ответ) —
HashMap для каждой пары ключ-значение создаёт объект Map.Entry, который хранит ключ, значение, хэш ключа, ссылку на следующий объект
Такая сложность нужна, так как HashMap хранит данные в массиве массивов (см. "связанные посты" в комментариях)
Но даже пустой объект весит n-байт, а тут столько побочной информации
Поэтому ArrayMap реализует другой подход: он содержит два массива - один mArray для keys и objects, второй mHashes для хэша ключей
Процесс добавления новой пары:
1. в mArray кладется ключ
2. на следующую позицию в mArray кладется объект
3. вычисляется hash от ключа и кладется в mHashes
Вывод: в HashMap поиск по ключу быстрее, но ArrayMap съедает в разы меньше памяти
....
А зачем тогда SparseArray?
Бывали ли у вас ситуации, когда правильный выбор структуры давал заметный прирост к производительности или андроид-разработчику в реальности такие знания не пригождаются?
Думаю, все в курсе — если попробовать использовать HashMap, студия предложит заменить тип на ArrayMap
Почему (короткий ответ) — ArrayMap оптимизирован под Android
Почему (правильный ответ) —
HashMap для каждой пары ключ-значение создаёт объект Map.Entry, который хранит ключ, значение, хэш ключа, ссылку на следующий объект
Такая сложность нужна, так как HashMap хранит данные в массиве массивов (см. "связанные посты" в комментариях)
Но даже пустой объект весит n-байт, а тут столько побочной информации
Поэтому ArrayMap реализует другой подход: он содержит два массива - один mArray для keys и objects, второй mHashes для хэша ключей
Процесс добавления новой пары:
1. в mArray кладется ключ
2. на следующую позицию в mArray кладется объект
3. вычисляется hash от ключа и кладется в mHashes
Вывод: в HashMap поиск по ключу быстрее, но ArrayMap съедает в разы меньше памяти
....
А зачем тогда SparseArray?
Бывали ли у вас ситуации, когда правильный выбор структуры давал заметный прирост к производительности или андроид-разработчику в реальности такие знания не пригождаются?
👍2👎1
с наступившим, работяги!
желаю отдохнуть от работы, учебы, самообразования и всего, от чего хочется отдохнуть, чтобы взяться за все это с новыми силами и получить новые успехи
желаю отдохнуть от работы, учебы, самообразования и всего, от чего хочется отдохнуть, чтобы взяться за все это с новыми силами и получить новые успехи
❤1
Концепция InputStream - Reader - Buffered
С понятием InputStream мобильный разработчик сталкивается, когда нужно прочитать локальный файл или загрузить его по ссылке
Общая логика считывания больших объемов данных (или данных неизвестного размера) следующая:
• InputStream (все его реализации) — класс, позволяющий обозначить источник данных (файл, ссылка, массив байтов...) и начать считывать его байт за байтом
• Reader (все его реализации) — класс, преобразующий байты, считываемые при помощи InputStream, в символы по заданной таблице кодирования (например, Unicode)
• Buffered (все его реализации) — класс, позволяющий ускорить процесс чтения за счет буферизации источника данных
Зачем буфер: если мы читаем файл с жесткого диска, то каждая операция запроса и переноса байтов с самого диска в оперативку занимает много времени. Потому нашей программе лучше за одно обращение считать n-байтов, а не один. Это справедливо для сетевых запросов
Комбинация реализаций позволяет считать данные в нужном формате и с оптимальными настройками буферизации
Та же самая история справедлива и для отдачи байтов: OutputStream - Writer - Buffered
....
загадка: по какой таблице кодирования Reader/Writer сопоставит байты и символы? откуда он ее возьмет - зашита в Java, определена в Android, задается JVM...?
С понятием InputStream мобильный разработчик сталкивается, когда нужно прочитать локальный файл или загрузить его по ссылке
Общая логика считывания больших объемов данных (или данных неизвестного размера) следующая:
• InputStream (все его реализации) — класс, позволяющий обозначить источник данных (файл, ссылка, массив байтов...) и начать считывать его байт за байтом
• Reader (все его реализации) — класс, преобразующий байты, считываемые при помощи InputStream, в символы по заданной таблице кодирования (например, Unicode)
• Buffered (все его реализации) — класс, позволяющий ускорить процесс чтения за счет буферизации источника данных
Зачем буфер: если мы читаем файл с жесткого диска, то каждая операция запроса и переноса байтов с самого диска в оперативку занимает много времени. Потому нашей программе лучше за одно обращение считать n-байтов, а не один. Это справедливо для сетевых запросов
Комбинация реализаций позволяет считать данные в нужном формате и с оптимальными настройками буферизации
Та же самая история справедлива и для отдачи байтов: OutputStream - Writer - Buffered
....
загадка: по какой таблице кодирования Reader/Writer сопоставит байты и символы? откуда он ее возьмет - зашита в Java, определена в Android, задается JVM...?
🔥5👍3❤1
Kotlin Coroutine. Suspend — зачем и как?
suspend — ключевое слово, которое говорит компилятору о том, что в функцию нужно добавить (и передать при вызове) аргумент типа Continuation
Continuation — интерфейс, который содержит одну функцию resumeWith(result: Result<T>). то есть обычный колбэк
когда suspend-функция выполнила работу, она должна вызвать continuation.resumeWith(result)
если она этого не сделает, то корутина, вызвавшая эту suspend-функцию, просто зависнет
после вызова колбэка, корутина, запустившая suspend-функцию, получит результат (если функция обещала что-то вернуть) и продолжит работу
примечание: разработчику самому нужно вызывать continuation.resumeWith(result) только когда он использует suspendCoroutine { ... }. в остальных корутин-билдерах оно вызывается самостоятельно под капотом
Выводы:
• suspend само по себе не делает функцию асинхронной, только добавляет параметр при компиляции
если вы на функцию поставите клеймо suspend, но долгие операции, выполняемые в ней, не вынесите в отдельный поток, то асинхронность не случится
• suspend-функцию можно вызвать только из корутины (или другой suspend-функции)
потому что Continuation — это объект, который создается в корутине и передается suspend-функциям внутри нее
suspend — ключевое слово, которое говорит компилятору о том, что в функцию нужно добавить (и передать при вызове) аргумент типа Continuation
Continuation — интерфейс, который содержит одну функцию resumeWith(result: Result<T>). то есть обычный колбэк
когда suspend-функция выполнила работу, она должна вызвать continuation.resumeWith(result)
если она этого не сделает, то корутина, вызвавшая эту suspend-функцию, просто зависнет
после вызова колбэка, корутина, запустившая suspend-функцию, получит результат (если функция обещала что-то вернуть) и продолжит работу
примечание: разработчику самому нужно вызывать continuation.resumeWith(result) только когда он использует suspendCoroutine { ... }. в остальных корутин-билдерах оно вызывается самостоятельно под капотом
Выводы:
• suspend само по себе не делает функцию асинхронной, только добавляет параметр при компиляции
если вы на функцию поставите клеймо suspend, но долгие операции, выполняемые в ней, не вынесите в отдельный поток, то асинхронность не случится
• suspend-функцию можно вызвать только из корутины (или другой suspend-функции)
потому что Continuation — это объект, который создается в корутине и передается suspend-функциям внутри нее
🔥16👍7
sealed interface vs sealed class vs enum
enum:
• в первую очередь - это перечисление. поэтому у него есть методы для перебора значений
• у каждой константы перечисления есть только параметры, описанные в конструкторе родительского enum
• у каждого константы перечисления есть только функции, объявленные как abstract или определенные в родительском enum
• каждое константы перечисления - object, то есть имеет один экземпляр
• реализует serializable (удобно сохранять\отправлять как строку) и comporable (непонятно зачем)
sealed:
• в первую очередь - обычный класс
• каждый из наследников может иметь свой конструктор, свои функции
• наследниками могут быть как object, так и class
следовательно, наследники-классы могут существовать в нескольких экземплярах
• в отличие от обычных open-классов, все наследники sealed класса должны быть определены до компиляции
то есть sealed что-то среднее между open и final-классами
sealed class или sealed interface:
• class - если хочешь создать переменную в конструкторе\внутри класса
• interface - если переменные не нужны\достаточно констант в companion object
....
в каких конкретно кейсах вы используете sealed, а в каких enum?
enum:
• в первую очередь - это перечисление. поэтому у него есть методы для перебора значений
• у каждой константы перечисления есть только параметры, описанные в конструкторе родительского enum
• у каждого константы перечисления есть только функции, объявленные как abstract или определенные в родительском enum
• каждое константы перечисления - object, то есть имеет один экземпляр
• реализует serializable (удобно сохранять\отправлять как строку) и comporable (непонятно зачем)
sealed:
• в первую очередь - обычный класс
• каждый из наследников может иметь свой конструктор, свои функции
• наследниками могут быть как object, так и class
следовательно, наследники-классы могут существовать в нескольких экземплярах
• в отличие от обычных open-классов, все наследники sealed класса должны быть определены до компиляции
то есть sealed что-то среднее между open и final-классами
sealed class или sealed interface:
• class - если хочешь создать переменную в конструкторе\внутри класса
• interface - если переменные не нужны\достаточно констант в companion object
....
в каких конкретно кейсах вы используете sealed, а в каких enum?
👍8😁4🔥3🤩1
новая карта канала
в давности обещал усовершенствовать карту канала. момент настал:
• можно увидеть список всех вышедших постов на одном экране
• с помощью поиска по заголовку и телу отыщатся посты на интересующие вас темы
• киллерфича — кнопка "открыть рандомный пост". использовать, когда хочется получить новые знания, но не знаешь, с чего начать
android-dpd.ru
....
в комментариях можно предлагать недостающие фичи, высмеивать баги и голосовать, нужен ли полноценный многостраничник
в давности обещал усовершенствовать карту канала. момент настал:
• можно увидеть список всех вышедших постов на одном экране
• с помощью поиска по заголовку и телу отыщатся посты на интересующие вас темы
• киллерфича — кнопка "открыть рандомный пост". использовать, когда хочется получить новые знания, но не знаешь, с чего начать
android-dpd.ru
....
в комментариях можно предлагать недостающие фичи, высмеивать баги и голосовать, нужен ли полноценный многостраничник
🔥11🎉5👍4
Dolgo.polo Dev | Денис Долгополов pinned «новая карта канала в давности обещал усовершенствовать карту канала. момент настал: • можно увидеть список всех вышедших постов на одном экране • с помощью поиска по заголовку и телу отыщатся посты на интересующие вас темы • киллерфича — кнопка "открыть…»
Kotlin Coroutine — корутины за 100 слов
Основные принципы:
• Приостанавливаемые — могут в определенных точках запомнить свое состояние, замереть, дать потоку выполнить часть другой функции, а после продолжить выполнение с той же точки
• Асинхронные — за счет приостанавливаемости функции разбиваются на блоки. Поток может выполнять блоки от разных функций вперемешку
• Прекращаемые — можем прекратить выполнение функции с помощью job.cancel()
Функция замечает, что ее отменили, когда проверяет флаг Job.isActive
Основные понятия:
• CoroutineScope — контейнер для корутин
дает возможность сгруппировать корутины, задать параметры CoroutineContext и отменить сразу все дочерние корутины
корутины могут быть запущены только внутри CoroutineScope
• CoroutineContext — характеристики для CoroutineScope
Например, Dispatcher.Main - это наследник CoroutineContext - задает пул потоков, в котором будут выполняться функции
• suspend — указывает, что функция может содержать асинхронный код
• Job — объект, описывающий состояние корутины
Напримери, содержит флаг isActive
• Deffered<T> — расширяет Job. Позволяет вернуть значение из корутины
Основные билдеры — функции, создающие и сразу запускающие корутину:
• launch — возвращает Job
• async — возвращает Deffered<T>
• suspendCoroutine(cont: Continuation<T>) — возвращает T, который нужно вернуть через колбек cont.resume(obj: T)
Основные принципы:
• Приостанавливаемые — могут в определенных точках запомнить свое состояние, замереть, дать потоку выполнить часть другой функции, а после продолжить выполнение с той же точки
• Асинхронные — за счет приостанавливаемости функции разбиваются на блоки. Поток может выполнять блоки от разных функций вперемешку
• Прекращаемые — можем прекратить выполнение функции с помощью job.cancel()
Функция замечает, что ее отменили, когда проверяет флаг Job.isActive
Основные понятия:
• CoroutineScope — контейнер для корутин
дает возможность сгруппировать корутины, задать параметры CoroutineContext и отменить сразу все дочерние корутины
корутины могут быть запущены только внутри CoroutineScope
• CoroutineContext — характеристики для CoroutineScope
Например, Dispatcher.Main - это наследник CoroutineContext - задает пул потоков, в котором будут выполняться функции
• suspend — указывает, что функция может содержать асинхронный код
• Job — объект, описывающий состояние корутины
Напримери, содержит флаг isActive
• Deffered<T> — расширяет Job. Позволяет вернуть значение из корутины
Основные билдеры — функции, создающие и сразу запускающие корутину:
• launch — возвращает Job
• async — возвращает Deffered<T>
• suspendCoroutine(cont: Continuation<T>) — возвращает T, который нужно вернуть через колбек cont.resume(obj: T)
👍25❤3
ладно, возвращаемся
для начала не будет забавной пикчи, будет предположения о последствиях нового закона об IT
плюсы:
• освобождение от налогов — у компаний останется значительно больше дохода на развитие
• отсрочка от армии — приличной части айтишников действительно будет полезнее провести год, занимаясь профильным делом
• льготная ипотека сотрудникам — кредиты под хорошую ставку всегда хорошо
минусы:
• освобождение от налогов
тут нужно учитывать, что у государства не так много видов дохода. один из них — налоги
если их начинают собирать меньше, то в бюджете становится меньше денег на добрые дела (развитие инфраструктуры, безопасности, поддержки нуждающихся)
значит, нужно либо больше печатать, либо больше собирать с других, либо урезать расходы
• отсрочка от армии
вероятнее всего, в законе будет прописано, что отсрочку получат только те, кто отработал энное количество времени на организацию
значит, у компаний будет козырь на снижение зарплат молодым специалистам: "не хочешь работать за полцены? мы тебя списываем с должности жизненно важного айтишника и твоя отсрочка сгорает"
не то чтобы это совсем нечестно, просто к этому нужно быть готовым
• льготная ипотека сотрудникам
риски аналогичны предыдущему пункту
перебежать из компании в компанию (или шантажировать начальство офером в другую компанию) станет куда сложнее, если твоя ипотека превращается в анти-льготную при переходе в другую компанию
опять же, это абсолютно честно, но бесконечный рост айтишных зарплат замедлит
для начала не будет забавной пикчи, будет предположения о последствиях нового закона об IT
плюсы:
• освобождение от налогов — у компаний останется значительно больше дохода на развитие
• отсрочка от армии — приличной части айтишников действительно будет полезнее провести год, занимаясь профильным делом
• льготная ипотека сотрудникам — кредиты под хорошую ставку всегда хорошо
минусы:
• освобождение от налогов
тут нужно учитывать, что у государства не так много видов дохода. один из них — налоги
если их начинают собирать меньше, то в бюджете становится меньше денег на добрые дела (развитие инфраструктуры, безопасности, поддержки нуждающихся)
значит, нужно либо больше печатать, либо больше собирать с других, либо урезать расходы
• отсрочка от армии
вероятнее всего, в законе будет прописано, что отсрочку получат только те, кто отработал энное количество времени на организацию
значит, у компаний будет козырь на снижение зарплат молодым специалистам: "не хочешь работать за полцены? мы тебя списываем с должности жизненно важного айтишника и твоя отсрочка сгорает"
не то чтобы это совсем нечестно, просто к этому нужно быть готовым
• льготная ипотека сотрудникам
риски аналогичны предыдущему пункту
перебежать из компании в компанию (или шантажировать начальство офером в другую компанию) станет куда сложнее, если твоя ипотека превращается в анти-льготную при переходе в другую компанию
опять же, это абсолютно честно, но бесконечный рост айтишных зарплат замедлит
👍2
Типы permissions для получения локации
Для работы с местоположением, нужно явно запросить одно из разрешений:
• ACCESS COARSE LOCATION
когда приложение находится в статусе foreground (открыта активити или запущен сервис с уведмолением) и нужно получить примерную локацию (точность порядка +-3 км)
• ACCESS FINE LOCATION
статус foreground и нужно получить точную локацию (порядка +-10-50м)
• ACCESS BACKGROUND LOCATION
можно получать локацию, когда приложение не имеет статуса foreground
такое нужно приложениям для семейного контроля, отслеживанию друзей или IoT-пультам
особенности:
• можно запросить только COARSE, но нельзя запросить только FINE
если вам нужно разрешение ACCESS FINE LOCATION, запрашивайте сразу оба, а пользователь выберет - дать точную локацию, приближенную или отказать
• если пользователь уже выдал COARSE, можно запросить апгрейд до FINE
для этого снова выполните запрос на оба разрешения
• запрашивать ACCESS BACKGROUND LOCATION можно после получения одного из foreground-разрешений
при этом, если получено только COARSE, то только эта точность и будет доступна в background
....
описанные механики разнятся от версии к версии андроида
приведенная логика справедлива для Android 12, в более старых некоторые требования мягче
Для работы с местоположением, нужно явно запросить одно из разрешений:
• ACCESS COARSE LOCATION
когда приложение находится в статусе foreground (открыта активити или запущен сервис с уведмолением) и нужно получить примерную локацию (точность порядка +-3 км)
• ACCESS FINE LOCATION
статус foreground и нужно получить точную локацию (порядка +-10-50м)
• ACCESS BACKGROUND LOCATION
можно получать локацию, когда приложение не имеет статуса foreground
такое нужно приложениям для семейного контроля, отслеживанию друзей или IoT-пультам
особенности:
• можно запросить только COARSE, но нельзя запросить только FINE
если вам нужно разрешение ACCESS FINE LOCATION, запрашивайте сразу оба, а пользователь выберет - дать точную локацию, приближенную или отказать
• если пользователь уже выдал COARSE, можно запросить апгрейд до FINE
для этого снова выполните запрос на оба разрешения
• запрашивать ACCESS BACKGROUND LOCATION можно после получения одного из foreground-разрешений
при этом, если получено только COARSE, то только эта точность и будет доступна в background
....
описанные механики разнятся от версии к версии андроида
приведенная логика справедлива для Android 12, в более старых некоторые требования мягче
👍11🔥2
Философия LayoutParams
xml-вьюшки стремительно отмирают в пользу Compose. уважим их несколькими постами и заодно разберемся, почему у людей возникло желание от них избавиться
свойства LayoutParams:
• базовый класс — ViewGroup.LayoutParams
он содержит только два свойства - height, weight
• View получают LayoutParams от ближайшего родительского ViewGroup
если родитель вьюшки - RelativeLayout, то View.mLayoutParams = RelativeLayout.LayoutParams
если у вьюшки нет родителя (она еще не attached), то View.mLayoutParams = null
• ViewGroup.LayoutParams расширяется наследниками
например, есть такая иерархия: RelativeLayout.LayoutParams extends ViewGroup.MarginLayoutParams extends ViewGroup.LayoutParams
• если вызвать View.setLayoutParams(...), то View передаст новые параметры родителю, тот перерисует себя, а после перерисуется сама View
....
какие отрицательные побочки мы получаем от наследовательной архитектуры LayoutParams? в чем ее минусы (из-за которых набирает популярность compose)?
xml-вьюшки стремительно отмирают в пользу Compose. уважим их несколькими постами и заодно разберемся, почему у людей возникло желание от них избавиться
свойства LayoutParams:
• базовый класс — ViewGroup.LayoutParams
он содержит только два свойства - height, weight
• View получают LayoutParams от ближайшего родительского ViewGroup
если родитель вьюшки - RelativeLayout, то View.mLayoutParams = RelativeLayout.LayoutParams
если у вьюшки нет родителя (она еще не attached), то View.mLayoutParams = null
• ViewGroup.LayoutParams расширяется наследниками
например, есть такая иерархия: RelativeLayout.LayoutParams extends ViewGroup.MarginLayoutParams extends ViewGroup.LayoutParams
• если вызвать View.setLayoutParams(...), то View передаст новые параметры родителю, тот перерисует себя, а после перерисуется сама View
....
какие отрицательные побочки мы получаем от наследовательной архитектуры LayoutParams? в чем ее минусы (из-за которых набирает популярность compose)?
👍6
Xml — зачем префиксы android, tools, app
Описывая вьюшки в xml-файлах, мы чаще всего пишем android:paramName="value"
В этом случае android — это префикс
Префикс принято объявлять в самом старшем элементе xml-файла — используется следующая конструкция:
xmlns:prefixName="URI"
Зачем?
чтобы можно было легко подменять способ применения параметра, не изменяя его имени
например, параметр text для TextView
• если написать, android:text="value", то после запуска приложения мы увидим в TextView текст "value"
• если написать tools:text="value", то в редакторе студии мы увидим в TextView текст "value", а при запуске приложения - нет
один и тот же параметр обработался по-разному
Существуют следующие префиксы:
• android — параметры, которые повлияют на работу приложения
• tools — параметры, которые учитываются Lint и визуальным редактором студии, но вырезаются при компиляции
• app — создание собственных параметров (или использования кастомных параметров из библиотек)
....
почему ссылка из объявления префикса не открывается?
например, xmlns:app="http://schemas.android.com/apk/res-auto"
Описывая вьюшки в xml-файлах, мы чаще всего пишем android:paramName="value"
В этом случае android — это префикс
Префикс принято объявлять в самом старшем элементе xml-файла — используется следующая конструкция:
xmlns:prefixName="URI"
Зачем?
чтобы можно было легко подменять способ применения параметра, не изменяя его имени
например, параметр text для TextView
• если написать, android:text="value", то после запуска приложения мы увидим в TextView текст "value"
• если написать tools:text="value", то в редакторе студии мы увидим в TextView текст "value", а при запуске приложения - нет
один и тот же параметр обработался по-разному
Существуют следующие префиксы:
• android — параметры, которые повлияют на работу приложения
• tools — параметры, которые учитываются Lint и визуальным редактором студии, но вырезаются при компиляции
• app — создание собственных параметров (или использования кастомных параметров из библиотек)
....
почему ссылка из объявления префикса не открывается?
например, xmlns:app="http://schemas.android.com/apk/res-auto"
👍17🤩1
Философия LayoutInflator
LayoutInflator — класс, умеющий создавать из xml-разметки объект View
По умолчанию он использует стандартный парсер XmlPullParser, но можно передать свой (зачем - придумать сложно)
Способности:
• inflate(...) — парсит xml-файл в View-объект
если передать ссылку на родителя-ViewGroup, то LayoutInflator автоматически прикрепит View к родителю и вернет ссылку не на View, а на этого родителя
при передачи родителя View получает родительский LayoutParams. если родителя нет — View.mLayoutParams = null
• createView(...)
может создать нужную View без xml-файла
замечание:
findViewById(...) — не парсит xml, а осуществляет поиск по уже созданным View
LayoutInflator — класс, умеющий создавать из xml-разметки объект View
По умолчанию он использует стандартный парсер XmlPullParser, но можно передать свой (зачем - придумать сложно)
Способности:
• inflate(...) — парсит xml-файл в View-объект
если передать ссылку на родителя-ViewGroup, то LayoutInflator автоматически прикрепит View к родителю и вернет ссылку не на View, а на этого родителя
при передачи родителя View получает родительский LayoutParams. если родителя нет — View.mLayoutParams = null
• createView(...)
может создать нужную View без xml-файла
замечание:
findViewById(...) — не парсит xml, а осуществляет поиск по уже созданным View
👍27🔥1🎉1
Kotlin Operator
Еще одна возможность котлина, которой можно придумать применение в своем проекте
чтобы коллеги потом ходили и спрашивали, чего ты там такой умный понапридумывал
Как работает:
1. помечаем функцию словом operator
2. даем ей одно из заложенных в языке названий
например, unaryPlus() связана с оператором "+"
3. пишем реализацию этой функции — все, что душе угодно
Например (напишем через extensions-функцию — гулять так гулять):
operator fun User.unaryPlus() : User {
this. name = this. name + " vot eto ya molodec"
return this
}
используем:
user. name = "Ivan"
print( (+user).name) )
получаем:
Ivan vot eto ya molodec
также доступны функции:
• unaryMinus() == "-"
• not() = "!"
• plus() == a "+" b
• rangeTo() == a".."b
и другие
....
где в реальном проекте этому можно найти хорошее применение?
Еще одна возможность котлина, которой можно придумать применение в своем проекте
чтобы коллеги потом ходили и спрашивали, чего ты там такой умный понапридумывал
Как работает:
1. помечаем функцию словом operator
2. даем ей одно из заложенных в языке названий
например, unaryPlus() связана с оператором "+"
3. пишем реализацию этой функции — все, что душе угодно
Например (напишем через extensions-функцию — гулять так гулять):
operator fun User.unaryPlus() : User {
this. name = this. name + " vot eto ya molodec"
return this
}
используем:
user. name = "Ivan"
print( (+user).name) )
получаем:
Ivan vot eto ya molodec
также доступны функции:
• unaryMinus() == "-"
• not() = "!"
• plus() == a "+" b
• rangeTo() == a".."b
и другие
....
где в реальном проекте этому можно найти хорошее применение?
👍9🔥7😁3🎉1
URL vs URI
При работе с файлами или интернетом можно заметить, что иногда нужно указывать URL, а иногда — URI. а почему?
URI — это более общее понятие, включающее в себя URL и URN
UR* - Uniform Resource
• I - Identifier
• L - Location
• N - Name
значит:
• URI может являться и путем до файла, и именем файла
• URL — указывает расположение файла (причем обычно в сети Интернет, кхе)
• URN — имя файла (без пути до него)
URL/URI имеют схожую структуру:
scheme://authoritypath?query# fragment
• scheme — протокол (например, http: или mailto: или tel:)
• authority — логин или хост
• path — путь до файла, разделенный косыми чертами
• query — слова-фильтры
• fragment — произвольный параметр
URI могут быть абсолютные или относительные (вести куда-то, относительно папки, в которой будут открыты)
....
чем отличаются две scheme, встречающиеся при работе с файлами в андроиде — content и file?
При работе с файлами или интернетом можно заметить, что иногда нужно указывать URL, а иногда — URI. а почему?
URI — это более общее понятие, включающее в себя URL и URN
UR* - Uniform Resource
• I - Identifier
• L - Location
• N - Name
значит:
• URI может являться и путем до файла, и именем файла
• URL — указывает расположение файла (причем обычно в сети Интернет, кхе)
• URN — имя файла (без пути до него)
URL/URI имеют схожую структуру:
scheme://authoritypath?query# fragment
• scheme — протокол (например, http: или mailto: или tel:)
• authority — логин или хост
• path — путь до файла, разделенный косыми чертами
• query — слова-фильтры
• fragment — произвольный параметр
URI могут быть абсолютные или относительные (вести куда-то, относительно папки, в которой будут открыты)
....
чем отличаются две scheme, встречающиеся при работе с файлами в андроиде — content и file?
❤10🔥10👍9
MVI — архитектура за 100 слов
Этот подход набирает все большую популярность, вытесняя MVVM
Яркий признак MVI — наличие State
State — неизменяемое состояние (объект, который называет состояние и передает все связанные с ним данные)
Например, ErrorState(val errorMessage: String)
Слои:
• Model — источник State для View
• View — принимает State и отображает
• Intent — события
Не буду вдаваться в философию, приведу пример:
когда пользователь открывает экран, Model создает State.Defult
загружает в этот State всю информацию, которую нужно отобразить, и отдает View
через некоторые время пользователь нажимает кнопку
View сообщает об новом Intent в Model
Model формирует новый State и отдает View
В целом — это все. Но мир не идеален, поэтому:
Intent можно разделить на 2 группы:
• Действия юзера
• События от бекенда (например, пришло новое сообщение)
State можно поделить на 2 группы:
• Стабильные — должны отображаться даже после переворота экрана
• Эффекты — должны отобразиться один раз (например, Toast\Snackbar)
Плюсы MVI:
• наглядность — прямой "поток" данных и событий, без макарон
• модульность — тестируемость
• заранее прописываешь стейты, так что не забываешь учесть разные состояния (обычно State.Default, State.Failed, State.Loading, State.Success)
Минусы MVI:
• часто приходится делать тяжелый моральный выбор - если на экране появляются новые вьюшка, то нужно создавать новый State или модифицировать старый?
Этот подход набирает все большую популярность, вытесняя MVVM
Яркий признак MVI — наличие State
State — неизменяемое состояние (объект, который называет состояние и передает все связанные с ним данные)
Например, ErrorState(val errorMessage: String)
Слои:
• Model — источник State для View
• View — принимает State и отображает
• Intent — события
Не буду вдаваться в философию, приведу пример:
когда пользователь открывает экран, Model создает State.Defult
загружает в этот State всю информацию, которую нужно отобразить, и отдает View
через некоторые время пользователь нажимает кнопку
View сообщает об новом Intent в Model
Model формирует новый State и отдает View
В целом — это все. Но мир не идеален, поэтому:
Intent можно разделить на 2 группы:
• Действия юзера
• События от бекенда (например, пришло новое сообщение)
State можно поделить на 2 группы:
• Стабильные — должны отображаться даже после переворота экрана
• Эффекты — должны отобразиться один раз (например, Toast\Snackbar)
Плюсы MVI:
• наглядность — прямой "поток" данных и событий, без макарон
• модульность — тестируемость
• заранее прописываешь стейты, так что не забываешь учесть разные состояния (обычно State.Default, State.Failed, State.Loading, State.Success)
Минусы MVI:
• часто приходится делать тяжелый моральный выбор - если на экране появляются новые вьюшка, то нужно создавать новый State или модифицировать старый?
👍28🔥8❤2