All streams
Search
Write a publication
Pull to refresh
62
0
Сергей Бугаев @bugaevc

Разработчик

Send message

Наверно, я не очень удачно сформулировал. Конечно же, основной механизм освобождения памяти в Android — завершение всего неиспользуемого. Вопрос только в том, может ли приложение перейти из состояния «activity1 работает, acivity2 работает» напрямую в состояние «activity1 работает, acivity2 завершена», или только через состояние «activity1 заврешена, acivity2 завершена, процесс убит».


Переформулировал этот кусок в статье.

К сожалению, этот вопрос плохо описан в документации. Насколько я понимаю, здесь есть такие особенности:


  • при нехватке памяти система действительно завершает процессы целиком, при этом она может вызвать, а может и не вызвать onDestroy() у запущенных компонентов;
  • когда activity завершается не прямо перед завершением всего процесса (а, например, при повороте экрана), onDestroy() вызывается обязательно, но освобождение памяти самой activity происходит через обычный механизм сборки мусора (в том числе, если сохранить лишнюю ссылку, будет activity leak).

Но здесь нужно помнить, что запуск и завершение процесса — это всё-таки более низкий уровень, чем жизненный цикл компонентов приложения. Процитирую первую статью:


Android максимально абстрагирует понятие приложение запущено как от пользователя, так и от разработчика. Конечно, процесс приложения нужно запускать и останавливать, но Android делает это автоматически

То есть на этом более высоком уровне — как я описал в статье — может так получиться, что из двух копий activity одна запущена, а другая полностью завершена — например, если пользователь вернулся в одну из этих копий после того, как (на низком уровне) процесс был завершён из-за временной нехватки памяти, а потом запущен снова.

Да, ждёт в блокирующем вызове ioctl(binder_fd, BINDER_WRITE_READ, &bwd) — он работает примерно так же, как write() и read() подряд на этом дескрипторе. Вот реализация этого ioctl со стороны ядра.


Да, binder_fd поддерживает и poll, но обычно процессы блокируют свой основной поток (он же looper thread, он же UI thread в приложениях) на binder_write_read и делают остальное блокирующее I/O (например, сетевые запросы) в других потоках.

Туториал по практическому использованию FCM в приложениях под Android есть на официальном сайте, но там нет ничего особенно интересного. FCM/GCM — не какое-то новое решение, большинство приложений их уже использует, так что проблемы с синхронизацией, вероятно, вызваны чем-то другим.

Конечно есть! Обычные pipe-ы никуда не делись (Android is Linux), правда совсем не очевидно, как их прокидывать из одного приложения в другое. Основной механизм IPC на Android — Binder, про который я рассказывал в первой статье.


Я опять приведу в качестве примера Google Play Services — приложение, которое не имеет собственного интерфейса, но реализует разнообразную функциональность, которой пользуются (подключаясь к нему через Binder) другие приложения.

Да, именно в этом и есть смысл разделения на activity (GUI), которые могут запускаться, когда нужно, завершаться и перезагружаться; и сервисы, которые остаются запущенными даже без activity. Нет, всё это происходит в рамках одного приложения, конечно.


Но, как я рассказал в статье, Android 8 не позволяет таким приложениям поддерживать соединение в фоне (и вообще серьёзно ограничивает фоновые сервисы); вместо этого нужно использовать push-сервисы вроде Firebase Cloud Messaging. Вероятно, многие из этих приложений так и делают.


С другой стороны, пока что Viber и WhatsApp работают на Oreo в режиме совместимости (у обоих target SDK < 26), то есть на них эти ограничения не распространяются.

Честно говоря, именно с callback hell я в Android не встречался, но есть и много других, мягко говоря, сложностей, с которыми приходится сталкиваться разработчику — в основном это вопросы архитектуры приложения и правильной работы с lifecycle (в том числе фрагментов). Решается это использованием реактивных библиотек вроде RxJava или новых Android Architecture Components. Подробнее об этом я собираюсь рассказать в одной из следующих статей.

Я именно про Oreo и говорю, и выше процитировал именно ту страницу, на которую вы дали ссылку.

Да, это пример lock-in'а. С другой стороны, ничто не мешает кому-нибудь разработать свободный (или просто свой) аналог Play Services и использовать его.

Не просто с меньшей вероятностью.


Во-первых, это логично и правильно: это независимое действие, а не часть какой-то activity. (Аналогично: зачем разделять программу на функции и классы?)


Во-вторых, некоторые гарантии выполнения после выхода из activity всё-таки есть:


When an app goes into the background, it has a window of several minutes in which it is still allowed to create and use services. At the end of that window, the app is considered to be idle. At this time, the system stops the app's background services, just as if the app had called the services' Service.stopSelf() methods. Under certain circumstances, a background app is placed on a temporary whitelist for several minutes. While an app is on the whitelist, it can launch services without limitation, and its background services are permitted to run.

Почему зоопарк? С точки зрения разработчика приложений все разрешения выглядят и работают одинаково.


С точки зрения низкоуровневой реализации часть разрешений проверяется ядром (разрешение на доступ к сети, на доступ к файлам устройств, на доступ к файлам пользователя...), поэтому они должны быть экспортированы на уровень ядра в виде GID (в packages.xml они тоже записаны, конечно). Для остальных разрешений это не нужно и неудобно (при изменении GID приложение нужно перезапустить, что делается прозрачно для пользователя, но всё-таки лучше делать это реже).

> Если подумать об этих очень сложных продуктах и сказать, что ответ на все наши проблемы состоит в том, что у вас должен быть кто-то, кто ремонтирует и имеет доступ к частям, не рассматривает всю проблему (To think about these very complex products and say the answer to all our problems is that you should have anybody to repair and have access to the parts is not looking at the whole problem)

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

(To think about these very complex products and say (the answer to all our problems is that (you should have anybody to repair and have access to the parts))) is not looking at the whole problem.

AOT означает, что приложение компилируется до (ahead of) выполнения. Это можно делать сразу же при установке (и именно так это работало в Lollipop и Marshmallow) или в какое-то другое время между установкой и запуском (не обязательно прямо перед запуском; так это работает, начиная с Nougat). Новый способ заметно лучше старого — в том числе и потому, что позволяет потом перекомпилировать приложение с учётом данных профилирования.

То, что вы описали, и есть AOT.

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

Вот именно поэтому это и opt-in. Да, кастомные сборки могут насильно переносить приложение, даже если оно не рассчитано на то, что его части начнут внезапно отваливаться.

Возможность устанавливать приложения на SD-карту никуда не убрали, но это opt-in со стороны приложения — по умолчанию installLocation="internalOnly". Да, при этом данные приложения помещаются в специальный зашифрованный asec-контейнер на карточке. Как написано в документации, это в основном актуально для больших игр.

Я специально в этой серии статей почти везде пишу имена в оригинале — на мой взгляд, так получается лучше. Другими словами, не баг, а фича :)

UPD: про это будет в третьей статье.

Спасибо, вы совершенно правы. Исправил в статье.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Works in
Date of birth
Registered
Activity