10 октября 2017 г.

Изменения в поведениях Android 8.0. Часть 2: Приложения с target Android 8.0 (API level 26)

    Перевод второй половины статьи https://developer.android.com/about/versions/oreo/android-8.0-changes.html. Свои комментарии я пометил курсивом. Как и ранее, я рекомендую читать оригинал.
Перевод первой части статьи: https://myachinqa.blogspot.ru/2017/09/android-80-1-target-api-level.html
 
    Эти изменения поведений затрагивают исключительно те приложения, у которых в targetSdkVersion указано Android 8.0 (API level 26) и выше. То есть если вы собираете приложение с таким параметром, вам нужно удостовериться, что оно поддерживает все эти изменения.

Алерты

   Приложения, использующие пермишен SYSTEM_ALERT_WINDOW, больше не смогут использовать следующие типы окон для показа своих окон поверх других приложений и системных окон:
   Вместо этого, приложения должны использовать новый тип окон, который называется TYPE_APPLICATION_OVERLAY.
   Когда вы используете окна TYPE_APPLICATION_OVERLAY для отображения алертов вашего приложения, помните о следующих характеристиках этого  нового типа:
  • Алерты приложений всегда возникают ПОД критическими системными окнами, такими как статус бар и IME (прим. перев.: методы ввода)
  • Система может перемещать или изменять размеры окон с типом TYPE_APPLICATION_OVERLAY, дабы улучшить их представление на экране
  • Пользователь может открыть область уведомлений и, через специальную настройку, запретить приложению отображать окна с типом TYPE_APPLICATION_OVERLAY

Уведомления Content change

   В Android 8.0 (API level 26), для приложений, с соответствующим таргетом, изменилось поведение ContentResolver.notifyChange() и registerContentObserver(Uri, boolean, ContentObserver).
   Теперь эти API требуют, надёжного ContentProvider, который определён для заказчика во всех Uri. Задание ContentProvider с ясными правами помогут защитить ваше приложение от изменения данных вредоносными приложениями и предотвратить потенциальную утечку приватных данных из него.

Фокус View объектов

   Теперь кликабельные объекты View по умолчанию могут получать фокус. Если вы хотите, чтобы View объект был кликабельным, но не получал фокус, установите для него атрибут android:focusable в false в layout XML или же задайте false для setFocusable() в UI логике вашего приложения.

Безопасность

   Изменения Android 8.0 (API level 26), связанные с безопасностью:
  • Если ваше приложение настроено так, чтобы не работать в небезопасной сети (opts out), то объекты WebView в приложении не смогут загружать вебсайты по HTTP. Каждый WebView объект должен использовать только HTTPS.
  • Системная настройка Разрешить установку из недоверенных источников была удалена. Теперь устанавливать неизвестные приложения из неизвестных источников могут приложения, которым одобрен пермишен Install unknown apps. Больше информации о новом пермишене смотрите по ссылке Unknown App Install Permissions.
   Больше информации и рекомендаций о том, как сделать приложения более более безопасными, смотрите Security for Android Developers.

Доступ к учётным записям и открытость

   В Android 8.0 (API level 26) приложения больше не могут получить доступ к учётным записям пользователя, если только пользователь сам не предоставил доступ или же это не приложение владельца учётной записи. Пермишена GET_ACCOUNTS больше недостаточно. Для получения доступа к учётной записи, приложения должны использовать либо AccountManager.newChooseAccountIntent(), либо метод, предоставляемый приложением-владельцем этой учётной записи. После получения доступа к учётным записям приложения могут вызывать AccountManager.getAccounts().
   В Android 8.0 LOGIN_ACCOUNTS_CHANGED_ACTION объявляется устаревшим. Теперь, чтобы быть в курсе изменений в учётных записях, используйте addOnAccountsUpdatedListener().
   Больше информации о новых API и методах, добавленных для работы с учётными записями, их открытости и доступу к ним, смотрите Account Access and Discoverability, раздел «Новые API».

Приватность

   В Android 8.0 (API level 26) есть следующие изменения, связанные с приватностью:
  • Системные свойства net.dns1, net.dns2, net.dns3 и net.dns4 больше недоступны.
  • Для получения информации о сети, например DNS серверах, приложение с пермишеном ACCESS_NETWORK_STATE должно зарегистрировать NetworkRequest или NetworkCallback. Эти классы доступны начиная с Android 5.0 (API level 21)
  • Build.SERIAL объявлен устаревшим. Приложения, которым нужно знать аппаратный серийный номер, должны использовать новый метод Build.getSerial(), который защищён пермишеном READ_PHONE_STATE
  • LauncherApps API больше не позволяет приложениям из рабочего профиля получать информацию об основном профиле. Когда пользователь находится в рабочем профиле, LauncherApps API ведёт себя так, будто в других профилях из той же группы нет установленных приложений. Как и ранее, попытка доступа к не связанным профилям вызывает SecurityExceptions.

Разрешения

   До Android 8.0 (API level 26), если во время работы приложение запрашивало разрешение для чего-либо и пользователь его предоставлял, система, что было неправильно, предоставляла доступ всей группе разрешений, в которую входил тот, который был запрошен и которые были зарегистрированы в манифесте.
   Для приложений с таргетом Android 8.0 поведение станет правильным. Приложение будет получать только то разрешение, которое явно запрашивает. Однако, если пользователь одобрил приложению какой-то пермишен, все последующие запросы всех пермишенов из этой же группы будут одобрены автоматически.
   К примеру, пусть в манифесте приложения объявлены READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE. Приложение запросило разрешение READ_EXTERNAL_STORAGE и пользователь одобрил запрос. Если приложение имеет target API level 24 или ниже, то система одновременно одобрит и WRITE_EXTERNAL_STORAGE, потому что они оба являются частью группы разрешений STORAGE и оба зарегистрированы в манифесте. Если же приложение имеет target Android 8.0 (API level 26), система в этот момент одобрит только READ_EXTERNAL_STORAGE. Но, если позднее приложение запросит ещё и WRITE_EXTERNAL_STORAGE, система автоматически одобрит это разрешение без каких-либо запросов у пользователя.

Медиа

  • Фреймворк может самостоятельно выполнять automatic audio ducking. В этом случае, когда стороннее приложение запрашивает фокус с AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, приложение, которое сейчас удерживает фокус, понижает громкость звука. Но обычно оно не получает колбэк onAudioFocusChange() и не будет отпускать аудиофокус. Новые API позволяют менять такое поведение и теперь приложения смогут ставить воспроизведение на паузу вместо приглушения звука.
  • Когда пользователь принимает телефонный звонок, все активные медиа на время звонка становятся беззвучными.
  • Для управления сценариями работы со звуком нужно использовать AudioAttributes вместо audio stream type’ов. Продолжать использовать audio stream type’ы можно только для управления громкостью. Все другие сценарии использования стрим тайпов хотя и будут работать (например, аргумент streamType в устаревшем конструкторе AudioTrack), но в системном журнале будет это будет зарегистрировано как ошибка.
  • Если при использовании AudioTrack приложение запрашивает достаточно большой аудио буфер, то фреймворк, по возможности, будет пытаться использовать глубокий буфер (прим. перев.: т. е. 100 мс и больше, см. https://groups.google.com/d/msg/android-porting/0iq6rha9qLA/1OBn0ZVjPA8J).
  • В Android 8.0 (API level 26) изменилась обработка событий кнопок медиа (media button events):
    1. Обработка медиа кнопок в UI активити осталась без изменений: активити на переднем плане всё ещё самые приоритетные.
    2. Если активити на переднем плане не обрабатывает события медиа кнопок, то система перенаправляет эти события тому приложению, которое  недавно проигрывало аудио. Для определения, кто будет получателем события, не имеют значения статус активности, флаги и состояние воспроизведения.
    3. Если медиа сессия приложения уже была отпущена, система посылает событие медиа кнопки в MediaButtonReceiver приложения, если он есть.
    4. Во всех прочих случаях система отклоняет событие медиа кнопки.

Нативные библиотеки

   Если приложение имеет таргет Android 8.0 (API level 26), нативные библиотеки больше не будут загружаться, если в них есть загружаемый сегмент, который одновременно и записываемый, и исполняемый. Некоторые приложения, если они использовали нативные библиотеки с кривыми сегментами загрузки, не будут работать.
   Дополнительную информацию можно найти здесь: Writable and Executable Segments.

Обработка коллекций

   В Android 8.0 (API level 26) Collections.sort() реализован поверх List.sort(). При этом в Android 7.x (API levels 24 and 25) было наоборот: по умолчанию реализация List.sort() вызывала Collections.sort().
   Это изменение дало Collections.sort() преимущества оптимизированных реализаций List.sort(), но накладывает следующие ограничения:
  • Реализация List.sort() не должна вызывать Collections.sort(), потому что это приведёт к переполнению буфера из-за бесконечной рекурсии. Если вы хотите в вашей реализации List поведение по умолчанию, вам не нужно переопределять sort().
  • Если в родительском классе не целесообразно реализовывать sort(), нормальным подходом будет переопределить List.sort() в реализации, построенной поверх List.toArray(), Arrays.sort() и ListIterator.set(). Например:
@Override
public void sort(Comparator<? super E> c) {
 Object[] elements = toArray();
 Arrays.sort(elements, c);
 ListIterator<E> iterator = (ListIterator<Object>) listIterator();
 for (Object element : elements) {
   iterator.next();
   iterator.set((E) element);
 }
}
   В большинстве случаев вы можете переопределить List.sort() в своей реализации так, чтобы, в зависимости от уровня API, использовались разные реализации. Например:
@Override
public void sort(Comparator<? super E> comparator) {
 if (Build.VERSION.SDK_INT <= 25) {
   Collections.sort(this);
 } else {
   super.sort(comparator);
 }
}
  • Если вы делаете последнее только для того, чтобы иметь метод sort() на всех уровнях API, подумайте о том, чтобы задавать уникальные имена, типа sortCompat(), а не переопределять sort().
  • Теперь Collections.sort() считается структурной модификацией реализации List, который вызывается sort(). Например, если версия платформы ниже чем Android 8.0 (API level 26), то при итерировании по ArrayList и вызове sort() из List.sort(), итерирование выбросит ConcurrentModificationException. Collections.sort() же не выбрасывает исключения.
  • Это изменение унифицирует поведение платформы: теперь каждый из подходов приведёт к ConcurrentModificationException.

Поведение загрузчиков классов

   При загрузке новых классов Android 8.0 (API level 26) выполняет проверку, не нарушают ли загрузчики классов обещаний рантайма. Эти проверки выполняются вне зависимости от того, ссылаются ли на класс из Java (из forName()), байткода Dalvik bytecode или JNI. Платформа не перехватывает прямые вызовы метода loadClass() из Java и не проверяет результаты этих вызовов. Это поведение не влияет на функциональность нормально спроектированных загрузчиков классов.
   Платформа проверяет, что дескриптор класса, которое отдаёт загрузчик класса, совпадает с ожидаемым дескриптором. Если возвращённый дескриптор не совпадает с ожидаемым, платформа выбрасывает ошибку NoClassDefFoundError и сохраняет в исключении подробное сообщение с указанием несоответствия.
   Кроме того, платформа проверяет, что дескриптор запрашиваемого класса корректен. Эта проверка ловит JNI вызовы, которые косвенно загружают классы (к примеру GetFieldID()), передавая в них неправильные дескрипторы. Например, поле с сигнатурой java/lang/String будет не найдено, потому что эта сигнатура неправильная. Сигнатура должна быть Ljava/lang/String;.
   Здесь мы видим отличие JNI вызова для FindClass() где java/lang/String является корректным квалифицированным именем.
   Android 8.0 (API level 26) не может иметь несколько загрузчиков классов, пытающихся определять классы, использующих один и тот же объект DexFile. При попытке сделать это, Android runtime выбросит ошибку InternalError с сообщением "Attempt to register dex file <filename> with multiple class loaders" («Попытка зарегистрировать dex файл <имяфайла> несколькими загрузчиками классов»).
   DexFile API объявляется устаревшим и вместо него настоятельно рекомендуется использовать один из загрузчиков классов, предоставляемых платформой, к примеру PathClassLoader или BaseDexClassLoader.
   Замечание. Вы можете создавать множество загрузчиков классов, которые ссылаются на один и тот же  APK или файл JAR где-то в файловой системе. Это совершенно нормально и не приведёт к повышенному потреблению памяти: если DEX файлы в контейнере не сжаты (т.е. stored, а не compressed), платформа сможет выполнять операцию mmap вместо извлечения файла. Однако, если платформе необходимо будет извлечь DEX файл из контейнера, то в этой ситуации ссылки на DEX могут съесть много памяти.
   Все загрузчики классов в Android работают параллельно. Когда несколько потоков в гонке загружают один и тот же класс одним и тем же загрузчиком класса и первый поток успешно выполняет задачу, то этот результат используется для всех других тредов. Это происходит вне зависимости  от того, вернул ли загрузчик класса тот же самый класс, другой класс или выбросил исключение. Подобные исключения платформа молча игнорирует.
   Осторожно. В версиях платформы ниже чем Android 8.0 (API level 26), эти соглашения сломаны и могут привести к загрузке одних и тех же классов несколько раз, повреждению кучи из-за путаницы классов и к другим нежелательным эффектам.

Комментариев нет:

Отправить комментарий