Android 17 представляет множество новых функций и API для разработчиков. В следующих разделах приведено краткое описание этих функций, которое поможет вам начать работу с соответствующими API.
Подробный список новых, измененных и удаленных API см. в отчете об изменениях API . Подробную информацию о новых API см. в справочнике Android API — новые API выделены для большей наглядности.
Также следует проанализировать области, где изменения платформы могут повлиять на ваши приложения. Для получения дополнительной информации см. следующие страницы:
- Изменения в поведении, влияющие на приложения при использовании Android 17.
- Изменения в поведении, затрагивающие все приложения независимо от
targetSdkVersion.
Основная функциональность
В Android 17 добавлены следующие новые функции, относящиеся к основным возможностям Android.
Новые триггеры ProfilingManager
В Android 17 в ProfilingManager добавлено несколько новых системных триггеров, которые помогут вам собрать подробные данные для отладки проблем с производительностью.
Новые триггеры:
-
TRIGGER_TYPE_COLD_START: Триггер срабатывает во время холодного запуска приложения. В ответе он предоставляет как пример стека вызовов, так и трассировку системы. -
TRIGGER_TYPE_OOM: Триггер срабатывает, когда приложение генерирует ошибкуOutOfMemoryErrorи в ответ предоставляет дамп кучи Java. -
TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Триггер срабатывает, когда приложение завершает работу из-за ненормального и чрезмерного использования ЦП, и в ответ предоставляет пример стека вызовов.
Чтобы понять, как настроить системный триггер, ознакомьтесь с документацией по профилированию на основе триггеров , а также с документацией по получению и анализу данных профилирования .
API JobDebugInfo
В Android 17 представлены новые API JobDebugInfo , которые помогут разработчикам отлаживать задания JobScheduler — выяснять, почему они не выполняются, как долго они выполнялись, и получать другую сводную информацию.
Первый метод расширенного API JobDebugInfo — getPendingJobReasonStats() , который возвращает карту причин, по которым задание находилось в состоянии ожидания выполнения , и их соответствующую суммарную продолжительность ожидания. Этот метод объединяет методы getPendingJobReasonsHistory() и getPendingJobReasons() , предоставляя информацию о причинах, по которым запланированное задание выполняется не так, как ожидалось, но упрощает получение информации, предоставляя в одном методе как продолжительность, так и причину выполнения задания.
Например, для указанного jobId метод может вернуть PENDING_JOB_REASON_CONSTRAINT_CHARGING и длительность 60000 мс, указывая на то, что задание находилось в состоянии ожидания в течение 60000 мс из-за несоблюдения ограничения на зарядку.
Конфиденциальность
В Android 17 добавлены следующие новые функции для повышения конфиденциальности пользователей.
средство выбора контактов для Android
Интерфейс выбора контактов Android Contact Picker — это стандартизированный, удобный для просмотра интерфейс, позволяющий пользователям делиться контактами с вашим приложением. Доступный на устройствах под управлением Android 17 и выше, этот интерфейс предлагает альтернативу широкому разрешению READ_CONTACTS обеспечивающую конфиденциальность. Вместо запроса доступа ко всей адресной книге пользователя, ваше приложение указывает необходимые поля данных, такие как номера телефонов или адреса электронной почты, а пользователь выбирает конкретные контакты для обмена. Это предоставляет вашему приложению доступ на чтение только к выбранным данным, обеспечивая детальный контроль и предоставляя единообразный пользовательский опыт со встроенным поиском, переключением профилей и возможностью множественного выбора без необходимости разработки или поддержки пользовательского интерфейса.
Для получения более подробной информации см. документацию по средству выбора контактов .
Безопасность
В Android 17 добавлены следующие новые функции для повышения безопасности устройств и приложений.
Расширенный режим защиты Android (AAPM)
Расширенный режим защиты Android (AAPM) предлагает пользователям Android мощный новый набор функций безопасности, что является значительным шагом в защите пользователей — особенно тех, кто подвержен повышенному риску — от сложных атак. Разработанный как функция с возможностью выбора, AAPM активируется с помощью одной настройки конфигурации, которую пользователи могут включить в любое время, чтобы применить определенный набор мер защиты.
Эти основные настройки включают блокировку установки приложений из неизвестных источников (боковая загрузка), ограничение передачи данных по USB и обязательное сканирование с помощью Google Play Protect, что значительно уменьшает площадь поверхности атаки устройства. Разработчики могут интегрировать эту функцию с помощью API AdvancedProtectionManager для определения статуса режима, что позволяет приложениям автоматически переходить в режим повышенной безопасности или ограничивать работу с функциями высокого риска, если пользователь дал на это согласие.
Подключение
В Android 17 добавлены следующие функции для улучшения взаимодействия устройств и приложений.
Ограниченные спутниковые сети
Внедряет оптимизации, позволяющие приложениям эффективно работать в сетях спутниковой связи с низкой пропускной способностью.
Пользовательский опыт и пользовательский интерфейс системы
В Android 17 внесены следующие изменения для улучшения пользовательского опыта.
Передавать
Handoff — это новая функция и API, которые появятся в Android 17 и которые разработчики приложений смогут интегрировать для обеспечения непрерывности работы приложений на разных устройствах. Она позволяет пользователю запустить приложение на одном устройстве Android и перевести его на другое. Handoff работает в фоновом режиме на устройстве пользователя и отображает доступные приложения с других находящихся рядом устройств пользователя через различные точки входа, такие как панель запуска и панель задач, на принимающем устройстве.
Приложения могут настроить Handoff на запуск того же самого нативного приложения Android, если оно установлено и доступно на принимающем устройстве. В этом процессе взаимодействия между приложениями пользователь получает прямую ссылку на указанное действие. В качестве альтернативы, в качестве резервного варианта может быть предложен Handoff для взаимодействия между приложением и веб-приложением или он может быть реализован напрямую с помощью URL Handoff.
Поддержка Handoff реализована для каждой активности отдельно. Чтобы включить Handoff, вызовите метод setHandoffEnabled() для соответствующей активности. Для восстановления состояния на принимающем устройстве при передаче данных могут потребоваться дополнительные данные. Реализуйте функцию обратного вызова onHandoffActivityRequested() которая будет возвращать объект HandoffActivityData , содержащий подробную информацию о том, как Handoff должен обрабатывать и восстанавливать активность на принимающем устройстве.
Обновление в реальном времени - API семантического цвета
В Android 17 функция Live Update запускает API семантической раскраски для поддержки цветов с универсальным значением.
Следующие классы поддерживают семантическую раскраску:
-
Notification -
Notification.Metric -
Notification.ProgressStyle.Point -
Notification.ProgressStyle.Segment
Раскрашивание
- Зеленый : ассоциируется с безопасностью. Этот цвет следует использовать в тех случаях, когда он дает понять окружающим, что вы находитесь в безопасной ситуации.
- Оранжевый : Используется для обозначения предупреждающих знаков и физических опасностей. Этот цвет следует использовать в ситуациях, когда пользователям необходимо проявлять осторожность для обеспечения более надежной защиты.
- Красный цвет : Обычно указывает на опасность, необходимо остановиться. Его следует использовать в случаях, когда требуется срочное внимание людей.
- Синий : Нейтральный цвет для информационного контента, который должен выделяться на фоне остального контента.
В следующем примере показано, как применить семантические стили к тексту в уведомлении:
val ssb = SpannableStringBuilder()
.append("Colors: ")
.append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
.append(", ")
.append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
.append(", ")
.append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
.append(", ")
.append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
.append(", ")
.append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)
Notification.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle("Hello World!")
.setContentText(ssb)
.setOngoing(true)
.setRequestPromotedOngoing(true)
API UWB Downlink-TDoA для Android 17
Метод определения местоположения по разности времени прихода сигнала в нисходящем канале (DL-TDoA) позволяет устройству определять свое положение относительно нескольких опорных точек путем измерения относительного времени прихода сигналов.
Следующий фрагмент кода демонстрирует, как инициализировать менеджер определения дальности , проверить возможности устройства и запустить сессию DL-TDoA:
Котлин
class RangingApp {
fun initDlTdoa(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Register for device capabilities
val capabilitiesCallback = object : RangingManager.CapabilitiesCallback {
override fun onRangingCapabilities(capabilities: RangingCapabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
startDlTDoASession(context)
}
}
}
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
}
fun startDlTDoASession(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Create session and configure parameters
val executor = Executors.newSingleThreadExecutor()
val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
val rangingRoundIndexes = intArrayOf(0)
val config: ByteArray = byteArrayOf() // OOB config data
val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)
val rangingDevice = RangingDevice.Builder().build()
val rawTagDevice = RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build()
val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()
val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(SessionConfig.Builder().build())
.build()
// Start the ranging session
rangingSession.start(preference)
}
}
private class RangingSessionCallback : RangingSession.Callback {
override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
// Process measurement results here
}
}
Java
public class RangingApp {
public void initDlTdoa(Context context) {
// Initialize the Ranging Manager
RangingManager rangingManager = context.getSystemService(RangingManager.class);
// Register for device capabilities
RangingManager.CapabilitiesCallback capabilitiesCallback = new RangingManager.CapabilitiesCallback() {
@Override
public void onRangingCapabilities(RangingCapabilities capabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.getUwbCapabilities() != null && capabilities.getUwbCapabilities().isDlTdoaSupported) {
startDlTDoASession(context);
}
}
};
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback);
}
public void startDlTDoASession(Context context) {
RangingManager rangingManager = context.getSystemService(RangingManager.class);
// Create session and configure parameters
Executor executor = Executors.newSingleThreadExecutor();
RangingSession rangingSession = rangingManager.createRangingSession(executor, new RangingSessionCallback());
int[] rangingRoundIndexes = new int[] {0};
byte[] config = new byte[0]; // OOB config data
DlTdoaRangingParams params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes);
RangingDevice rangingDevice = new RangingDevice.Builder().build();
RawRangingDevice rawTagDevice = new RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build();
RawDtTagRangingConfig dtTagConfig = new RawDtTagRangingConfig.Builder(rawTagDevice).build();
RangingPreference preference = new RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(new SessionConfig.Builder().build())
.build();
// Start the ranging session
rangingSession.start(preference);
}
private static class RangingSessionCallback implements RangingSession.Callback {
@Override
public void onDlTdoaResults(RangingDevice peer, DlTdoaMeasurement measurement) {
// Process measurement results here
}
}
}
Внеполосные (OOB) конфигурации
Приведённый ниже фрагмент кода представляет собой пример данных конфигурации DL-TDoA OOB для Wi-Fi и BLE:
Java
// Wifi Configuration
byte[] wifiConfig = {
(byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
// BLE Configuration
byte[] bleConfig = {
(byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
Если вы не можете использовать конфигурацию OOB из-за её отсутствия, или если вам нужно изменить значения по умолчанию, которых нет в конфигурации OOB, вы можете создать параметры с помощью DlTdoaRangingParams.Builder , как показано в следующем фрагменте кода. Вы можете использовать эти параметры вместо DlTdoaRangingParams.createFromFiraConfigPacket() :
Котлин
val dlTdoaParams = DlTdoaRangingParams.Builder(1)
.setComplexChannel(UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
.build()
Java
DlTdoaRangingParams dlTdoaParams = new DlTdoaRangingParams.Builder(1)
.setComplexChannel(new UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(new byte[]{0x01, 0x02, 0x03, 0x04})
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(new byte[]{0x01, 0x05})
.build();