Dolgo.polo Dev | Денис Долгополов – Telegram
Dolgo.polo Dev | Денис Долгополов
2.28K subscribers
87 photos
2 videos
120 links
Разбираемся в мобильной разработке (Android/iOS) и пытаемся выяснить, зачем оно так устроено

Статистика/цены: @dolgo_polo_dev_stats

По вопросам/рекламе: @dolgopolovdenis
Download Telegram
​​Почему нельзя писать liveData.observe(this...) во Fragment?


Lifecycle — объект, который отслеживает жизненный цикл (Fragment, Activity....) и передает их в качестве состояния тем, кто на этот жизненный цикл подписался (ViewModel, LiveData...)

LifecycleOwner — интерфейс, который содержит единственный метод — getLifecycle() : Lifecycle

LiveData.observe(...) — может принимать ссылку и на Lifecycle, и на LifecycleOwner


С помощью объекта Lifecycle мы сообщаем LiveData, когда данные должны приходить подписчику, а когда — уже нет


Activity и Fragment реализует интерфейс LifecycleOwner

Получается, что в Activity и Fragment мы спокойно можем просто передавать this в LiveData.observe()?


В Activity можем, а во Fragment — можем, но так делать не стоит


Дело в том, что у Fragment жизненный цикл View и самого фрагмента не жестко связаны:

если фрагмент был убран с экрана (onDetach), но добавлен в backstack, то View может быть уничтожена (onDestroyView), а сам фрагмент — нет

получается, если при подписки на LiveData мы передадим ссылку на фрагмент, а не на viewLifecycleOwner, то возможна ситуация, когда данные придут, а View уже уничтожено — приложение упадет
👍333
​​Многопоточность за 100 слов

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

дальше по шагам:
1) первый поток скопировал объект к себе в кэш
2) первый поток изменил поле объекта
3) второй поток скопировал объект к себе в кэш
4) второй поток изменяет это же поле (и не знает об изменениях, сделанных первым потоком!)
5) второй поток записывает изменения в общую память
6) первый поток записывает изменения в общую память

бац — мы потеряли изменения, сделанные во втором потоке. первый поток их перезаписал + второй поток даже не знал, что объект был изменен в первом потоке!


Как этого избежать:

• synchronized — запрещает нескольким потокам работать с объектом (классом) одновременно

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

- synchronized(Object) — захватывает объект
- synchronized fun — можно читать как synchronized(this), захватывает объект
- static synchronized fun — захватывает класс (а не объект)


• volatile — синхронизирует чтение и запись

сработает, если один поток только читает, а другой только пишет

не сработает, если оба потока изменяют объект — потому что операции между чтением и записью не синхронизированы


• atomic

атомарная операция = неделимая операция изменения объекта

с объектами, имеющими атомарные операции изменения (например, увеличение на единицу для AtomicInteger), можно работать в разных потоках без ручной синхронизации

в Java есть атомарные примитивы, коллекции и класс AtomicReference для работы с произвольными классами

....

а нужно ли думать о синхронизации переменной в корутинах?
сработают ли там эти подходы?
🔥26👍174🐳1
с нг! 🍻🍻🍻

желаю каждому вкусно покушать, порадоваться жизни и всего хо-ро-ше-го!

....

по поводу канала — постов нет не потому что их нет, а потому что копим ману на следующий год

как говорил один разоблачитель клоунов: "Ведь если я способен вернуться — способен каждый!"
🎉34👍3🍾3🎄3
наша постоянная рубрика — "Вечер рекомендаций"

всё что ниже — не реклама, а просто делюсь годнотой

....


• сообщество Coffee&Code — мобильные разработчики собираются оффлайн (!) каждую неделю

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

короче, супер круто, всем советую

вот московский чатик. через него можно выйти на остальные города

иногда сам туда заезжаю, да и в целом там куча крутых мужиков и девчонок, а также разных селебрити)



• канал об Android-разработке — Android Easy Notes

тут сказать особо нечего, просто клево, интересно и не душно. и темы прикольные


....

если тоже знаете что-то крутое (или сами делаете крутое), айда делиться ссылками в комментариях)
7🤓6🥰3🤡3👍2🔥2
​​Animation — сколько нужно классов, чтобы красиво переместить вьюшку?


Глоссарий:

интерполятор — функция, описывающая изменение значения во времени

например, значение может изменяться линейно или по экспоненте

pivotX, pivotY — координата точки внутри View, относительно которой будет применена анимация (например, вращение)



Как можно сделать анимацию:

ValueAnimator — задаем начальное и конечное значение числа, назначаем интерполятор, вешаем колбек, получаем значение каждые n миллисекунд, делаем с ним что хотим

ObjectAnimator — то же самое, что и ValueAnimator, только сразу принимает название параметра View, который нужно менять

например, ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)

ViewPropertyAnimator — оптимизированный ObjectAnimator

имеет более удобный синтаксис, синхронизирует внутри себя анимации над одним и тем же параметром


LayoutTransition — определяет анимацию появления/удаления элемента

например, есть дефолтная реализация для LinearLayout, включается параметром android:animateLayoutChanges="true"

Scene — анимация между состояниями экрана

создается две xml — со стартовым и конечным положением вьюшек

MotionLayout — наследник ConstraintLayout с API для анимации сложных перемещений вьюшек, с которыми взаимодействует пользователь

WindowAnimation — анимашки перехода между Activity

AnimationDrawable — описание анимации в drawable


Дополнительное:

AnimatorSet — упрощает комбинацию анимашек

например, можно запустить анимации последовательно или параллельно с некоторой задержкой

Fling Animation — придает "физичность" спискам

когда мы быстро свайпаем список, а потом отрываем палец, список сначала быстро разгоняется, а потом медленно останавливается, как будто есть сопротивление воздуха

Spring animation — придает "физичность" смахиваемым элементам

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

LottieAnimation — иногда дизайнерам легче скинуть готовый Lottie-файл с анимацией. нужна сторонняя библиотека

....

как дела с анимацией в Compose, все потребности уже закрыты?
👍143🕊3❤‍🔥1
​​Навигация через диплинки

В некоторых приложениях навигация между экранами осуществляется через диплинки, а не хардкодится в приложении xml-графом или просто кодом


Теоретические описание:
открываешь ссылку — открывается страница в приложении

Практическое описание:
в манифесте описываются диплинки, которые собирается перехватывать приложение

через Intent их получает Activity и выбирает Fragment, который нужно открыть



Плюсы подхода:

• гибкость

не нужна хардкодить, какой экран открывает кнопка. можно прислать с бека диплинк, который будет открываться по нажатию кнопки

• возможность открывать сразу нужную страницу приложения из других приложений

например, отправить диплинк пользователю в смс или зашить в рекламный баннер

• можно на беке строить актуальный граф экранов

• можно передать параметры в диплинке

например, "sheme://testPage?couponCode=123"

удобно для аналитики маркетинга и передачи параметров с экрана на экран/устройства на устройство

• из коробки получаем открытие любого экрана по QR-коду



Минусы:

• нужно следить за тем, чтобы на некоторые экраны были доступ только из своего приложения

я бы не хотел, чтобы мошенник мог прислать диплинк на экран "автоматически отправить все фотографии с устройства на номер..."

• нужно следить за тем, что Activity возвращает через onActivityResult

другое приложение может открыть диплинк, а после получить секретные данные в onActivityResult

• бывают сложные сценарии

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

например, страница ввода кода из смс для авторизации

• никто не запретит другим приложениям-мошенникам перехватить диплинк

пользователь увидит диалог "через какое приложение открыть ссылку?" и выберет не вас
😎15🔥7👏6🦄5🤷‍♂1👍1
​​SmartTV — это Android?

Меня долго мучал этот вопрос

Вскрытие показало, что существует несколько видов прошивок:


AndroidTV практически тот же самый Android

но с переделанным лаунчером и урезанным функционалом

причем урезано даже приложение "Настройки", из-за чего нельзя, например, установить сертификат или переключить темную тему на светлую. приходится осваивать adb


GoogleTV практически тот же самый AndroidTV

только красивее

и AndroidTV, и GoogleTV разрабатывает Google


• кастомный AndroidTV практически тот же самый AndroidTV, но с переделанным лаунчером и чем-нибудь еще

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

компании могут взять образ системы и изменить в нем UI, добавить функционал, опмизировать, вставить рекламы


SmartTV а вот тут уже интереснее

SmartTV может быть как на базе Android, так и нет

например, SmartTV от Samsung использует их собственную Tizen OS, которая основана на Linux, а не Android

если вы захотите разработать что-то под Tizen OS, то придется скачать специальную Tizen Studio и освоить .NET/JS+HTML+CSS/С++

то есть поставить .apk на Samsung SmartTV не получится

аналогичная ситуация со SmartTV от LG они прошивают телевизоры WebOS (Linux based)
🐳17👍15🥴10👌2🤡2
Один аккаунт на два приложения

Авторизовался в приложении А, а потом открыл приложение Б

А там окошко "Хотите войти под тем же аккаунтом?"

Как?

Ответ: через посредника == системный сервис == AccountManager

Что произошло под капотом:

1. Приложение А получило ваш логин и пароль

2, Приложение А проверило через бекенд, что вы существуете

3, Приложение А обратилось к системному AccoutManager, создало в нем Account и положило в него ваш логин и пароль

4. Приложение Б в момент запуска тоже обратилось к AccountManager, вязало Account и увидела ваш логин и пароль

Готово, приложение Б может авторизовать вас под тем же аккаунтом, не заставляя вспоминать пароль


Схема начинает выглядеть более безопасно, если учесть два факта:

• Account можно передавать только между приложениями, имеющими одну подпись

• прихранивать логин и пароль в сыром виде не стоит. вместо этого можно передавать обезличенный токен, зашифрованный на бекенде


Почему вам это надо:

• пользователю не нужно вспоминать пароль == увеличивается шанс, что он авторизуется

• компании не надо тратить деньги на СМС с кодом авторизации
🔥32👍7🕊3🐳1
Итоги года 2023

Постов: 7

то есть один пост раз в полтора месяца. знаете ли вы более продуктивного автора?)


Статьи на хабр:
Прикладное adb (Android Debug Bridge) для зумеров
Ускоряем поиск по коду в Android Studio
Android. Принять себя или Cookie?


Планы на следующий год:
• дальше, больше, come on everybody

....

с новым годом! 🎁
желаю хорошо отдохнуть и потом еще раз хорошо отдохнуть 🥂
🍾30👍6🔥5🎉3🆒2
​​Автоподстановка кода из SMS

Есть такая магия— SMS приходит на телефон, а приложение его автоматически читает и подставляет в поле для ввода OTP (one-time password)

На первый взгляд механизм понятен:

система получает SMS -> кидает Intent в приложение -> Broadcast Receiver получается Intent с текстом SMS


Но есть подвох — это опасно. Если все приложения будут получать этот Intent, то они смогут перехватить OTP (читать "аккаунт украден")

Поэтому вводятся дополнительные степени защиты

Первая:
1. Ограничение по времени — приложение получает Intent только в течение 5 минут после начала ожидания OTP

А вторая на выбор:
2.1 Или показываем пользователю системную диаложку "Вы ТОЧНО хотите вставить этот код в это приложение?"
2.2 Или в SMS должен быть зашит hash-код приложения

Подход 2.1 называется One-tap SMS verification (потому что требует от юзера одного нажатия на кнопку "да, ТОЧНО"
Подход 2.2 вообще не требует от юзера тапов

hash-код для 2.2 формирутеся из подписи приложения и заранее сообщается бекенду, который отправляет SMS

бекенд вставляет hash-код в конец текста SMS

Например,

"Никому не сообщайте код 110011, Ваш Банк💋 Sdnjh!a+iDn",
где Sdnjh!a+iDn — hash-код приложения
🔥48👍3🦄31👎1👏1
Из чего состоит apk

,apk это .zip

Если переименовать расширение .apk на .zip, то спокойно можно увидеть содержимое ,apk

Или можно открыть .apk в Android Studio

А там такое:

• .dex-файлы — скомпилированный java/kotlin-код, часто обфусцированный

у .dex-формата есть ограничение по объему, поэтому код приложения разбивается на несколько файлов

• .so-либы — это скомпилированный нативный код, то есть написанный на C/C++

они кстати жрут много мегабайт, так как компилируются сразу под разные архитектуры процессоров

java/kotlin код компилируется в единственном экземпляре, потому что запускается через JVM

• в папке r или res—- картинки, строки и т.д.

• в файле resources.arsc лежит мапа ресурсов и их id

• AndroidManifest.xml — итоговый манифест, смерженный из всех модулей и библиотек

• куча всякой мета-информации о приложении, библиотеках, котлине и даже градле

• META-INF — хэши файлов, информация о подписи приложения и еще мета-информация о библиотеках

пост про .aab
👍33👏13🔥10🦄4👌3🗿2
​​Многомодульность зачем нужна?

С первых шагов в мобильной разработке каждый день слышал, что многомодульность — это топ, это канон

И этому веришь на подсознательном уровне. Много — круто, модули — круто, получается многомодульность тоже круто

А зачем она нужна?

....

Первое, что приходит в голову — ради чистой архитектуры, ради группировки кода, ради presentation, domain, data

Но кто мешает разделять код с помощью обычных папочек и пакетов?

Из них тоже можно построить древо любой сложности. Модуль в этом плане отличается только цветом иконки в студии

....

Второй ложный аргумент — в большом проекте у каждой команды должен быть свой модуль

Но право апрувить код настраивается на папки, git про модули ничего не знает

Единственное исключение здесь — модификатор internal в kotlin, позволяющий ограничить видимость кода на уровне модуля (команды)

....

На самом деле модуль нужен для системы сборки

Работает это примерно так:

• система сборки (обычно gradle) считает хэш от всех файлов в модуле

• считает хэш всех модулей / библиотек, от которых зависит модуль

• если хэши не изменились, то компилировать заново модуль не надо — можно взять его из кэша

соответственно, бить проект на модули нужно только для ускорения сборки

....

или есть еще причины?)
🔥25👍4🦄43🤔2🍌1
​​DNS в Android


• Когда дергается DNS-сервер?


У каждого сетевого запроса есть домен (например ya.ru)

Но для отправки интернет-запроса нужен не домен, а IP

Поэтому существуют DNS-сервера, которые обменивает домен в IP

Но на практике вручную отправлять запрос на DNS сервер не нужно — сетевой клиент это делает самостоятельно

Когда приложение дергает любую ручку, то клиент ищет в своих кэшах IP для этого домена. Если не находит, то делает запрос к DNS-серверу.


• Откуда клиент знает, на какой DNS-сервер слать запрос?


Клиент берет IP адреса DNS-сервера у системы. В нее обычно зашиты наиболее популярные DNS-сервера (например, гугловские 8.8.8.8)

При этом можно для каждой Wi-Fi сети указать свой DNS-сервер в настройках сети

Более того — для каждого OkHttpClient-а внутри приложения можно указать свой DNS-сервер

Под капотом там такая цепочка:

OkHttpClient -> DNS (интерфейс-колбек) -> InetAddresses (класс, который разруливает кэши и возвращает IP, связанные с доменом) -> Inet4Address/Inet6Address (классы, хранящие IP и мета-информацию)


• В какой момент домен обменивается на IP?

Когда в первый раз улетает запрос на определенный домен, клиент сначала выполняет запрос к DNS, а потом только сам запрос на ваш бекенд

При этом полученный IP сохраняется в DNS-кэш клиента

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


• Как долго живет DNS-кэш?

Точных данных не нашел, но судя по исходникам java net кэш очищается раз в какое-то время и при смене сети (переключении Wi-Fi или переходе на моб сеть)


• Можно ли вручную очиcтить DNS-кэш?

Неа, нет API для этого. Но можно в OkHttpClient передать свой DNS-колбек и самостоятельно делать запрос к DNS-серверу/организовать свой кэш
🔥18🍓6👍4🍾4
Подписать переменную понятно
Data class vs Typealias vs Value class

Наприме, есть 2 переменных типа String — в одной храним полное ФИО, а в другой инициалы

и эти два параметра нужно перекидывать во множество функций


варианты решения:


• выбираем понятные длинные названия для переменных и везде используем их

val fullName: String
val shortName: String


минусы: нельзя сделать проверку типа, ведь обе переменные String, значит легко их перепутать


• создаем data-класс и упаковываем два поля туда

data class Names(fullName: String, shortName: String)


минусы: если играть в оптимизацию по памяти, то проиграешь + возможно не везде эти два поля нужно передавать вместе


• создаем typealias

typealias FullName = String
typealias ShortName = String


минус:
• typealias — не новый класс, поэтому у него не может быть своих функций, полей, конструкторов и т.д.
• typealias не даст сделать проверку типа, ведь FullName == String


• создаем value class

value class FullName(value: String)
value class ShortName(value: String)


плюсы:
• компилятор сам решит, где стоит оптимизировать и заинлайнить переменную, а где стоит оставить класс-обертку
• можно добавить функции и поля каждому value class
• можно делать проверку типа

минусы:
• нужно внимательно следить за .toString() — результат зависит от того, подставит компилятор value-класс или сразу его значение
🔥24🦄94🎃1
Как открыть URL в нативном приложении

Например, хотите дать возможность крутить казик прямо в вашем блокноте (осуждаю)

Для этого есть 5 вариантов:
• WebView
• GeckoView
• ChromeTabs
• TWA
• внешний браузер



WebView

Это обычная вьюшка, которая встраивается в вашу верстку

Плюсов много:
• можно отобразить хоть в диалоге, хоть на треть экрана
• можно контролировать каждый запрос (есть куча колбеков)
• можно передать любые куки и хедеры (в том числе токены пользователя, чтоб не заставлять пользователя заново авторизовываться)
• можно ограничить список ссылок, которые можно открывать
• можно настроить JS Bridge (вызывать нативный код прямо с сайта)


GeckoView

Это аналог WebView от Firefox

Сам не использовал, ничего сказать не могу. Интуиция подсказывает, что проблем будет больше, чем с WebView. Но API довольно обширное — это плюс. Если кто пользовался, поделитесь опытом/эмоциями, пожалуйста)


ChromeTab

ChromeTab — это Activity браузера Chrome, открытое поверх вашего приложения в вашей Task-e

Из плюсов:
• в ChromeTab доступен весь функционал браузера (кнопка "поделиться", поиск по странице...)
• общее с Chrome хранилище данных о юзере (если открыть ссылку, где юзер раньше авторизовывался, то он сразу окажется в своем аккаунте)
• поддерживает App Links (автоматический редирект в нативное приложение, если оно установлено)

Минусы есть:
• нельзя встроить в верстку
• колбеков практически нет, настройки минимальные
• можно передать только определенные хедеры/куки
• нельзя вытащить куки обратно


TWA (Trusted Web Activities)

Обертка над ChromeTab. Используется для связи PWA (progressive web app) и нативных возможностей приложения. Позволяет опубликовать PWA в Play Market.


Внешний браузер

Тут просто пуляем Intent с Intent.ACTION_VIEW в систему, а система уже находит приложение, которое этот URL может обработать

Из минусов:
• нельзя передать информацию о юзере, ему заново придется авторизовываться
• кнопка "назад" не вернет юзера в приложение, а будет листать сайт и отбрасывать юзера на главную браузера
👍17🦄4🐳1
Сейчас, как и лет 8 назад, опять обрели моду click-to-earn игры, ну или кликеры за монетки

Раньше там рекламу надо было смотреть и донатить за энергию, теперь ждать листинга монеты... прогресс это хорошо

В тик-токе и на ютубе куча крутых видео, как люди изобретают целые механизмы, которые автоматически или полуавтоматически кликают на экран и приносят авторам доход

Но мобильным разработчикам еще легче — у нас есть adb

Достаточно зациклить команду:

adb shell input tap X Y 


где X и Y координаты тапа на экран

готово

p.s. когда разбогатеете на этом, не забудьте, кто дал вам удочку
😁52🏆104👍1