Состояние в приложении — это любое значение, которое может меняться со временем. Это очень широкое определение, охватывающее все: от базы данных Room до переменной в классе.
Все приложения Android отображают состояние пользователю. Несколько примеров состояния в приложениях для Android:
- Снэкбар, который показывает, когда не удается установить сетевое соединение.
- Сообщение в блоге и связанные с ним комментарии.
- Пульсирующая анимация на кнопках, которая воспроизводится, когда пользователь нажимает на них.
- Стикеры, которые пользователь может нарисовать поверх изображения.
Jetpack Compose помогает вам четко указать, где и как вы храните и используете состояние в приложении Android. В этом руководстве основное внимание уделяется связи между состоянием и составными объектами, а также API-интерфейсам, которые Jetpack Compose предлагает для более простой работы с состоянием.
Состояние и состав
Compose является декларативным, и поэтому единственный способ его обновления — вызвать тот же составной объект с новыми аргументами. Эти аргументы представляют состояние пользовательского интерфейса. Каждый раз, когда состояние обновляется, происходит рекомпозиция . В результате такие вещи, как TextField
не обновляются автоматически, как в императивных представлениях на основе XML. Компонуемому объекту необходимо явно сообщить новое состояние, чтобы он мог соответствующим образом обновиться.
@Composable private fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField( value = "", onValueChange = { }, label = { Text("Name") } ) } }
Если вы запустите это и попытаетесь ввести текст, вы увидите, что ничего не происходит. Это связано с тем, что TextField
не обновляется сам — он обновляется при изменении его параметра value
. Это связано с тем, как в Compose работают композиция и рекомпозиция.
Чтобы узнать больше о первоначальной композиции и рекомпозиции, см. «Мышление в композиции» .
Состояние в составных объектах
Компонуемые функции могут использовать API remember
для хранения объекта в памяти. Значение, вычисленное функцией remember
сохраняется в композиции во время первоначальной композиции, и сохраненное значение возвращается во время повторной композиции. remember
можно использовать для хранения как изменяемых, так и неизменяемых объектов.
mutableStateOf
создает наблюдаемый MutableState<T>
, который является наблюдаемым типом, интегрированным со средой выполнения Compose.
interface MutableState<T> : State<T> {
override var value: T
}
Любые изменения в value
планируют рекомпозицию любых компонуемых функций, считывающих value
.
Существует три способа объявить объект MutableState
в составном объекте:
-
val mutableState = remember { mutableStateOf(default) }
-
var value by remember { mutableStateOf(default) }
-
val (value, setValue) = remember { mutableStateOf(default) }
Эти объявления эквивалентны и предоставляются в качестве синтаксического сахара для различных вариантов использования состояния. Вам следует выбрать тот, который создает наиболее простой для чтения код в компонуемом объекте, который вы пишете.
Синтаксис by
делегату требует следующего импорта:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Вы можете использовать запомненное значение в качестве параметра для других составных объектов или даже в качестве логики в операторах, чтобы изменить отображаемые составные объекты. Например, если вы не хотите отображать приветствие, если имя пустое, используйте состояние в операторе if
:
@Composable fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { var name by remember { mutableStateOf("") } if (name.isNotEmpty()) { Text( text = "Hello, $name!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } ) } }
Хотя remember
помогает сохранять состояние при рекомпозиции, оно не сохраняется при изменении конфигурации. Для этого вы должны использовать rememberSaveable
. rememberSaveable
автоматически сохраняет любое значение, которое можно сохранить в Bundle
. Для других значений вы можете передать собственный объект заставки.
Другие поддерживаемые типы состояний
Compose не требует использования MutableState<T>
для хранения состояния; он поддерживает другие наблюдаемые типы. Прежде чем читать другой наблюдаемый тип в Compose, вы должны преобразовать его в State<T>
, чтобы составные объекты могли автоматически перекомпоновываться при изменении состояния.
Compose поставляется с функциями для создания State<T>
из распространенных наблюдаемых типов, используемых в приложениях Android. Прежде чем использовать эти интеграции, добавьте соответствующие артефакты, как описано ниже:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
собирает значения изFlow
с учетом жизненного цикла, что позволяет вашему приложению экономить ресурсы приложения. Он представляет собой последнее значение, отправленное изState
Compose. Используйте этот API как рекомендуемый способ сбора потоков в приложениях Android.В файле
build.gradle
требуется следующая зависимость (это должна быть версия 2.6.0-beta01 или новее):
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
}
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.7"
}
collectAsState
похож наcollectAsStateWithLifecycle
, поскольку он также собирает значения изFlow
и преобразует их в ComposeState
.Используйте
collectAsState
для кода, не зависящего от платформы, вместоcollectAsStateWithLifecycle
, который доступен только для Android.Дополнительные зависимости для
collectAsState
не требуются, поскольку они доступны вcompose-runtime
.observeAsState()
начинает наблюдать за этимиLiveData
и представляет их значения черезState
.В файле
build.gradle
требуется следующая зависимость :
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.7.8")
}
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.7.8"
}
subscribeAsState()
— это функции расширения, которые преобразуют реактивные потоки RxJava2 (например,Single
,Observable
,Completable
) в ComposeState
.В файле
build.gradle
требуется следующая зависимость :
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.7.8")
}
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.7.8"
}
subscribeAsState()
— это функции расширения, которые преобразуют реактивные потоки RxJava3 (например,Single
,Observable
,Completable
) в ComposeState
.В файле
build.gradle
требуется следующая зависимость :
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.7.8")
}
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.7.8"
}
С состоянием и без гражданства
Компонуемый объект, использующий функцию remember
сохранить объект», создает внутреннее состояние, делая компонуемый объект сохраняющим состояние . HelloContent
— это пример составного объекта с сохранением состояния, поскольку он внутренне сохраняет и изменяет состояние своего name
. Это может быть полезно в ситуациях, когда вызывающему объекту не нужно контролировать состояние и он может использовать его без необходимости управлять состоянием самостоятельно. Однако составные элементы с внутренним состоянием, как правило, менее пригодны для повторного использования и их сложнее тестировать.
Составной объект без сохранения состояния — это составной объект, который не содержит никакого состояния. Простой способ добиться отсутствия гражданства — использовать подъем состояния .
При разработке многоразовых составных элементов часто требуется предоставить как версию одного и того же составного объекта как с сохранением состояния, так и без сохранения состояния. Версия с сохранением состояния удобна для вызывающих абонентов, которым не важно состояние, а версия без сохранения состояния необходима для вызывающих абонентов, которым необходимо контролировать или повышать состояние.
Государственный подъем
Поднятие состояния в Compose — это шаблон перемещения состояния к вызывающей стороне составного объекта, чтобы сделать составной объект без состояния. Общий шаблон подъема состояния в Jetpack Compose заключается в замене переменной состояния двумя параметрами:
-
value: T
: текущее значение для отображения -
onValueChange: (T) -> Unit
: событие, которое запрашивает изменение значения, гдеT
— предлагаемое новое значение.
Однако вы не ограничены onValueChange
. Если для компонуемого объекта подходят более конкретные события, вам следует определить их с помощью лямбда-выражений.
Состояние, поднятое таким образом, имеет несколько важных свойств:
- Единый источник истины: перемещая состояние вместо его дублирования, мы гарантируем наличие только одного источника истины. Это помогает избежать ошибок.
- Инкапсулированный: только компонуемые объекты с состоянием могут изменять свое состояние. Это полностью внутреннее.
- Возможность совместного использования: поднятое состояние можно использовать совместно с несколькими составными объектами. Если вы хотите прочитать
name
в другом компонуемом объекте, подъем позволит вам это сделать. - Перехватываемость: вызывающие объекты без сохранения состояния могут решить игнорировать или изменять события перед изменением состояния.
- Разделение: состояние компонуемых объектов без сохранения состояния может храниться где угодно. Например, теперь можно переместить
name
вViewModel
.
В данном примере вы извлекаете name
и onValueChange
из HelloContent
и перемещаете их вверх по дереву в компонуемый объект HelloScreen
, который вызывает HelloContent
.
@Composable fun HelloScreen() { var name by rememberSaveable { mutableStateOf("") } HelloContent(name = name, onNameChange = { name = it }) } @Composable fun HelloContent(name: String, onNameChange: (String) -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello, $name", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") }) } }
Поднимая состояние из HelloContent
, легче рассуждать о компонуемых объектах, повторно использовать их в различных ситуациях и тестировать. HelloContent
не связан с тем, как хранится его состояние. Разделение означает, что если вы измените или замените HelloScreen
, вам не придется менять способ реализации HelloContent
.

Схема, в которой состояние снижается, а события возрастают, называется однонаправленным потоком данных . В этом случае состояние меняется с HelloScreen
на HelloContent
, а события повышаются с HelloContent
на HelloScreen
. Следуя однонаправленному потоку данных, вы можете отделить составные элементы, отображающие состояние в пользовательском интерфейсе, от частей вашего приложения, которые сохраняют и изменяют состояние.
Дополнительную информацию см. на странице «Где поднять состояние» .
Восстановление состояния в Compose
API rememberSaveable
ведет себя аналогично remember
, поскольку сохраняет состояние при рекомпозиции, а также при воссоздании активности или процесса с использованием механизма состояния сохраненного экземпляра. Например, это происходит при повороте экрана.
Способы хранения состояния
Все типы данных, добавляемые в Bundle
сохраняются автоматически. Если вы хотите сохранить что-то, что нельзя добавить в Bundle
, есть несколько вариантов.
Упаковывать
Самое простое решение — добавить к объекту аннотацию @Parcelize
. Объект становится разделяемым и может быть объединен. Например, этот код создает тип данных City
и сохраняет его в состоянии.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
MapSaver
Если по каким-то причинам @Parcelize
не подходит, вы можете использовать mapSaver
, чтобы определить собственное правило преобразования объекта в набор значений, которые система сможет сохранить в Bundle
.
data class City(val name: String, val country: String) val CitySaver = run { val nameKey = "Name" val countryKey = "Country" mapSaver( save = { mapOf(nameKey to it.name, countryKey to it.country) }, restore = { City(it[nameKey] as String, it[countryKey] as String) } ) } @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
ListSaver
Чтобы избежать необходимости определять ключи для карты, вы также можете использовать listSaver
и использовать его индексы в качестве ключей:
data class City(val name: String, val country: String) val CitySaver = listSaver<City, Any>( save = { listOf(it.name, it.country) }, restore = { City(it[0] as String, it[1] as String) } ) @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
Государственные держатели в Compose
Простым подъемом состояния можно управлять в самих составных функциях. Однако если объем состояния, которое нужно отслеживать, увеличивается или возникает логика для выполнения в составных функциях, хорошей практикой является делегирование логики и ответственности за состояние другим классам: держателям состояний .
Дополнительную информацию см. в документации по Compose или, в более общем плане, на странице «Состояние состояний и состояние пользовательского интерфейса» в руководстве по архитектуре.
Повторный запуск запоминания вычислений при смене ключей
API remember
часто используется вместе с MutableState
:
var name by remember { mutableStateOf("") }
Здесь использование функции remember
позволяет значению MutableState
сохраняться при рекомпозиции.
В общем, remember
calculation
используется параметр лямбда. При первом запуске функции remember
вызывается лямбда-выражение calculation
и сохраняется его результат. Во время рекомпозиции remember
возвращает значение, которое было сохранено последним.
Помимо состояния кэширования, вы также можете использовать remember
для хранения любого объекта или результата операции в композиции, инициализация или вычисление которой требует больших затрат. Возможно, вы не захотите повторять это вычисление при каждой рекомпозиции. Примером является создание объекта ShaderBrush
, что является дорогостоящей операцией:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember
что значение сохраняется до тех пор, пока оно не покинет композицию. Однако есть способ сделать кэшированное значение недействительным. API remember
также принимает параметр key
или keys
. Если какой-либо из этих ключей изменится, в следующий раз, когда функция перекомпонует , remember
сделать кеш недействительным и снова выполнить расчет лямбда-блока . Этот механизм дает вам контроль над временем жизни объекта в композиции. Расчет остается действительным до тех пор, пока входные данные не изменятся, а не до тех пор, пока запомненное значение не покинет композицию.
Следующие примеры показывают, как работает этот механизм.
В этом фрагменте создается ShaderBrush
, который используется в качестве фоновой краски для составного Box
. remember
что экземпляр ShaderBrush
сохраняется, потому что его воссоздание обходится дорого, как объяснялось ранее. remember
что в качестве параметра key1
принимается avatarRes
, который представляет собой выбранное фоновое изображение. Если avatarRes
изменяется, кисть перекомпоновывается с новым изображением и повторно применяется к Box
. Это может произойти, когда пользователь выбирает другое изображение в качестве фона из средства выбора.
@Composable private fun BackgroundBanner( @DrawableRes avatarRes: Int, modifier: Modifier = Modifier, res: Resources = LocalContext.current.resources ) { val brush = remember(key1 = avatarRes) { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) } Box( modifier = modifier.background(brush) ) { /* ... */ } }
В следующем фрагменте состояние передается простому классу держателя состояния MyAppState
. Он предоставляет функцию rememberMyAppState
для инициализации экземпляра класса с помощью remember
. Предоставление таких функций для создания экземпляра, который выдерживает рекомпозицию, является распространенным шаблоном в Compose. Функция rememberMyAppState
получает windowSizeClass
, который служит key
параметром для remember
. Если этот параметр изменится, приложению необходимо воссоздать класс держателя простого состояния с последним значением. Это может произойти, если, например, пользователь поворачивает устройство.
@Composable private fun rememberMyAppState( windowSizeClass: WindowSizeClass ): MyAppState { return remember(windowSizeClass) { MyAppState(windowSizeClass) } } @Stable class MyAppState( private val windowSizeClass: WindowSizeClass ) { /* ... */ }
Compose использует реализацию равенства класса, чтобы определить, изменился ли ключ, и сделать сохраненное значение недействительным.
Сохранение состояния с ключами, не подлежащими рекомпозиции
API rememberSaveable
— это оболочка remember
, которая может хранить данные в Bundle
. Этот API позволяет состоянию пережить не только рекомпозицию, но и воссоздание активности и смерть процесса, инициированного системой. rememberSaveable
получает input
параметры с той же целью, что и remember
получает keys
. Кэш становится недействительным при изменении любого из входных данных . В следующий раз, когда функция будет перекомпоновываться, rememberSaveable
повторно выполнит расчетный лямбда-блок.
В следующем примере rememberSaveable
сохраняет userTypedQuery
до тех пор, пока typedQuery
не изменится:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
Узнать больше
Чтобы узнать больше о состоянии и Jetpack Compose, обратитесь к следующим дополнительным ресурсам.
Образцы
Mir 2: Return of the King 是 Actoz Soft 授权的优质《传奇》IP 移动游戏,由 HK ZHILI YAOAN LIMITED 使用 Unity 游戏引擎开发。 这款游戏不仅完美再现了韩国奇幻类 MMORPG 的代表作 Mir 2 的游戏氛围,还提供了许多最受欢迎的游戏内容,例如装备收集、大规模沙漠攻击和其他核心玩法。 该游戏使用了 Android Frame Pacing 库 (Swappy) 来提高帧速率的稳定性、实现流畅的渲染,并显著提升了 Android 鸣潮 是一款由 Kuro Games 开发的高保真动作角色扮演游戏。为了持续为长时间的游戏会话提供卓越的用户体验,优化功耗非常重要。 Android Studio 从 Hedgehog (2023.1.1) 开始引入了 功耗性能分析器 ,可帮助开发者根据设备端电源轨监视器 (ODPM) 了解功耗数据。 借助 Android Studio 中的功耗性能分析功能,您还可以 有效地对 Android 应用功能的功耗进行 A/B 测试 (如下所示)。 Kuro Games 首先使用 Android Godot Engine 是一个广受欢迎的多平台开源游戏引擎,对 Android 提供强大的支持。Godot 可用于制作几乎任何类型的游戏,并且支持 2D 和 3D 图形。Godot 4 版引入了新的渲染系统,该系统具有用于高保真图形的高级功能。Godot 4 渲染程序专为 Vulkan 等现代图形 API 而设计。 Godot Foundation 聘请了 The Forge Interactive 的图形优化专家,并与 Google 合作分析和进一步改进了 Godot 4 Vulkan Android 动态性能框架 (ADPF) 是 Google 推出的一款强大工具,适用于希望优化应用性能的开发者。ADPF 通过其热管理 API 提供有关设备热状态的实时信息,这些信息随后用于调整应用中的图形设置。 出于研究目的,Arm 使用 Unreal Engine 和 ADPF 开发了一个演示版,以研究如何使用 ADPF 优化游戏性能。 ADPF 会监控热状态,并相应地在游戏引擎中调整图形质量。 NCSoft《天堂 W》是由 NCSoft 开发的大型多人在线角色扮演游戏 (MMORPG)。这款游戏继承了原始 Lineage W 游戏的传统,为世界各地的玩家提供了一个环境,让他们可以通过全球服务器进行合作和竞争。《Lineage W》以独特的中世纪奇幻世界为背景,通过各种职业、技能和战斗系统为玩家提供深层次的游戏体验。 NCSoft 使用 Android 动态性能框架最大限度地提高了图形质量,同时避免了由温控调频导致的性能问题。 Android 动态性能框架 (ADPF) 改进性能和散热管理对于开发成功的 Android 游戏至关重要。传统上,开发者必须通过降低游戏保真度或进一步优化渲染程序来管理这些问题。这些更改往往针对特定游戏,并且往往不够灵活。 Android 生态系统中的多个参与者为开发者提供了自适应性能 API。为了简化自适应性能功能的集成并减少生态系统中的碎片化,Google 和 MediaTek 携手合作集成了我们的产品:Android 动态性能框架 (ADPF) 和 MediaTek 自适应游戏技术 (MAGT)。 借助 ADPF 使命召唤:战争地带移动版 是广受欢迎的《使命召唤》系列中的第一人称动作游戏。 超受欢迎的主机和 PC 游戏的移动实现利用低层级移动 API 来提供出色的玩家体验。 从技术角度来看,移动实现的目标是支持各种 Android 移动设备,同时尽可能使实现与主机版本保持一致,并确保图形管道和工具链与当前的主机和 PC 游戏及内容保持兼容。 Call of Duty 引擎使用名为 任务图渲染程序 的系统实现渲染提交管理的同步、内存分配和调度,该系统确定在 GPU 《魔灵召唤:克罗尼柯战记》 是韩国游戏开发商 Com2uS 于 2023 年 3 月面向全球发布的一款移动端大型多人在线角色扮演游戏。迄今为止,《魔灵召唤》在全球的下载量超过 1.8 亿,收入超过 27 亿美元。《魔灵召唤》是目前全球最受欢迎的手游之一,这款游戏展现了一个奇幻世界,玩家必须收集并训练各种怪物,才能与其他玩家对战。 近十年过去了,这款游戏庞大的玩家社区依然很是活跃,而且仍在扩展,部分原因在于 Com2uS 不断发布新的内容和更新,使这款游戏历久弥新、十分刺激。Com2uS 《魔灵召唤:克罗尼柯战记》 US(WW) 和 KR by Com2uS 专门利用 Vulkan 在 Android 上进行渲染,可将性能提升高达 30%。 Vulkan 是一种现代化的跨平台 3D 图形 API,旨在最大限度减少设备图形硬件与您的游戏之间的抽象。与 OpenGL ES 相比,Vulkan 的 CPU 开销更低,并且 Vulkan 提供更广泛的功能。 Com2uS 为 《魔灵召唤:克罗尼柯战记》 开发了高级渲染功能,包括: 《魔灵召唤:克罗尼柯战记》 对 Android 《Ares: Rise of Guardians 》是一款移动设备转 PC 的科幻 MMORPG 游戏,由 Second Dive 开发,后者是一家位于韩国的游戏工作室,以其动作角色扮演系列开发方面的专业知识而闻名。该游戏由 Kakao Games 发布。 《阿瑞斯》以广袤的宇宙为背景,采用细节满满的未来主义背景。《阿瑞斯》充满了刺激的玩法和画面精美的角色,涉及身着战斗服的战斗人员。然而,由于这些细节丰富的图形,一些用户的设备在处理游戏内容时有些吃力。 Cat Daddy Games 是一家全资 2K 工作室,位于华盛顿州柯克兰,是 NBA 2K Mobile 的开发者。该团队希望提高游戏的整体质量和稳定性,具体方法是减少“应用无响应”错误 (ANR)。如果 Android 应用的界面线程处于阻塞状态的时间过长,就会发生 ANR。发生这种情况时,负责更新界面的应用主线程将无法绘制或处理用户输入事件,这会引起用户的不满。如果应用在前台运行,系统会显示一个对话框,允许用户强制退出应用。 减少 ANR 一直是 Cat Daddy 的首要任务。QA Devsisters 是一家全球性的移动游戏开发商和发布商,专门制作基于《跑跑姜饼人》IP 的休闲游戏。他们最受欢迎的游戏包括 《跑跑姜饼人:烤箱大逃亡》 (跑步街机)和 《跑跑王国》 (社交 RPG),这两款游戏都深受全球用户的喜爱,尤其是在韩国、台湾和美国。虽然《跑跑姜饼人:烤箱大逃亡》是一款休闲游戏,但五年积累的资源使 CDN 容量提高到了 2.5GB,这使得 CDN 费用的增加。Devsisters 需要找到一种可持续的模式来宣传他们的大文件游戏。 Devsisters 发现,大量的 NEW STATE Mobile 是 Krafton 的一款大逃杀游戏于 2021 年 11 月面向全球发布,在发布后的第一个月便获得了超过 4500 万次下载。KRAFTON, Inc. 是一个由多个独立的游戏开发工作室组成的联合公司,旨在为全球游戏玩家打造富有吸引力的创新娱乐体验。该公司包括 PUBG Studio、Bluehole Studio、Striking Distance Studio、RisingWings、Dreamotion 和 Unknown Spokko 住在波兰,是一群雄心勃勃的创作者,他们致力于打造要求极为严苛的 IP。Spokko 是 CD PROJEKT 家族的成员,但是一家独立公司,已将 《巫师:怪物杀手》 的精彩世界转移到智能手机上。 《巫师:怪物杀手》是一款使用增强现实技术的基于位置的角色扮演游戏。这是一款计算密集型游戏,会给许多设备带来挑战。发布之初,Spokko 希望确保其游戏可以覆盖尽可能多的用户,同时为所有人提供高品质的体验。 Cat Daddy Games 是一家全资 2K 工作室,总部位于华盛顿州柯克兰。NBA 2K Mobile、NBA SuperCard 和 WWE SuperCard 系列背后的团队正在寻找一个解决方案来为用户提高游戏的整体质量,具体方法是在支持这些游戏的设备上提供更优质的素材资源。 他们实现了 Play Asset Delivery,以简单而更灵活的方式针对每位用户的设备配置生成并提供经过优化的 APK,并使用纹理压缩格式定位功能针对特定设备提供更好的美术资源并减少资源下载。 首先,Cat Unreal Engine 是由 Epic Games 开发的游戏引擎,可为各行各业的创作者提供自由和控制权,让他们提供先进的娱乐、富有吸引力的可视化内容和沉浸式虚拟世界。一些主要的 Android 游戏都是使用 Unreal Engine 构建的。 图 1. 在 Pixel 4 上运行的 Unreal Engine Suntemple 示例的屏幕截图 Epic 和其他游戏开发者使用 Android Studio 调试 C++、Kotlin 或 Java 编程语言,但许多游戏开发者都有以 Electronic Arts (EA) 是一家总部位于美国加利福尼亚州的游戏公司。它制作了各种不同类型的游戏,例如体育、动作、赛车和模拟游戏。EA 的开发工作室 Firemonkeys 因开发 真实赛车 3 、 模拟人生自由玩 和 Need For Speed: No Limits 而闻名于世。Firemonkeys 使用自定义游戏引擎来开发游戏,现在他们的所有 Android 游戏都在其开发工作流中使用 Android Game Development Extension (AGDE) 游戏开发商 CD Projekt RED (CDPR) 位于波兰华沙,他们重新构思了自己的迷你游戏《巫师 3》、 《巫师之昆特牌》 ,并于 2020 年 3 月在 Google Play 上以独立的免费畅玩形式发布。由于初始文件较大,且定期更新需要额外的设备存储空间,用户通常必须重新安装完整版游戏,才能获得更新的版本。这是游戏社区中最突出的挫败点。为了帮助进行差分修补,CDPR 通过实现 Play Asset Delivery 取得了巨大的成功。 CDPR 是实现 Play Asset 20 多年来, Gameloft 为数字平台打造了富有创意的游戏体验,从移动游戏到跨平台 PC 和主机游戏,不一而足。除了自有的知名游戏系列外,Gameloft 还为乐高、环球和长宝等热门品牌开发游戏。他们的游戏团队在全球拥有 3,600 名员工,每月可在 100 多个国家/地区覆盖 5500 万唯一身份玩家。 竞速街机游戏 Asphalt 9: Legends 于 2018 年首次发布,他们需要找到一种平衡性能、保真度和电池的方式。为此,Gameloft 怀着对游戏的热情以及将游戏带给世界各地玩家的渴望, Gameloft 于 2000 年成立。他们是开发移动游戏的先驱,现在旗下有超过 190 款游戏。Gameloft 的许多移动游戏是图形密集型游戏,下载大小较大。这使得他们成为 Google Play Asset Delivery (PAD) 早期开发阶段一个极具吸引力的合作伙伴。PAD 是一套基于我们的 app bundle 基础架构构建的游戏服务分发功能。PAD 会在适当的时间免费向适当的设备提供免费、动态的适当游戏素材资源。这引起了 RV AppStudios 是一家总部位于美国的游戏开发商,该公司旗下的休闲游戏、教育儿童应用和实用类应用到目前为止已有超过 2 亿的下载量。该团队在其应用 Puzzle Kids - Animals Shapes and Jigsaw Puzzles 中引入了 Google Play Asset Delivery,属于早期测试用户。他们希望优化应用的大小,节省资金,并在需要下载新的资源包时消除任何干扰,从而提升用户体验。 当用户安装 Puzzle Kids Pixonic 是一个总部位于莫斯科的视频游戏开发团队,其以抓住每一个机会升级自己的移动应用并覆盖更广泛的玩家群体为荣。该公司最著名的游戏之一是 《War Robots》 ,这是一款 12 人的玩家对战 (PVP) 游戏,玩家可以在即时战场上操作定制的机器人进行决斗。 《War Robots》于 2014 年发布,最初是专为 Android 的早期设备设计的,通过触控板操控游戏角色,用不到鼠标。Pixonic Gameloft 一直致力于成为首批在最新便携式硬件上发布游戏的开发商之一,以便随时随地为玩家提供激动人心的体验。因此,Gameloft 知道 ChromeOS 是适合其移动赛车系列最新游戏《狂野飙车 8:极速凌云》的平台。 Gameloft 非常了解如何针对不同的设备开发游戏,但将 Asphalt 体验转换为 Chromebook 特有的触摸屏/键盘混合控制(可随时切换)似乎具有挑战性。然而,结果证明这个过程没有想象的那么困难,值得一试。 利用 ChromebookMir 2 通过使用 Frame Pacing 库来提升渲染性能
Kuro Games 使用 Android Studio 功耗性能分析器和 ODPM 为 Wuthering Waves 降低了 9.68% 的功耗
适用于 Android 的 Godot Engine Vulkan 优化
在 Unreal Engine 中使用 Android 动态性能框架 (ADPF) 入门
NCSoft Lineage W 使用 ADPF 提高了持续性能并防止温控降频
MediaTek 提升了 Android SoC 的动态性能
《使命召唤:战争地带手游》使用 Vulkan 提升图形效果
Com2uS - Google Play 游戏电脑版
Com2uS 使用 Vulkan 提升图形效果
Kakao Games 通过 Android 自适应功能将 FPS 稳定性提升至 96%
2K 利用 Android Game Development Kit 将 ANR 发生率降低了 35%
《跑跑姜饼人:烤箱大逃亡》通过 Play Asset Delivery 节省了超过 20 万美元的 CDN 费用
新 STATE Mobile 利用 Android GPU 检查器将 GPU 使用量降低了 22%
《The Witcher: Monster Slayer》借助 Android Performance Tuner 扩大覆盖面
2K 借助 Play Asset Delivery 提供高清画质
“AGDE 简直太棒了!”;使用 Unreal Engine 进行 Android 开发
Firemonkeys 借助 AGDE 缩短了开发和调试时间
CD Projekt RED 借助 Play Asset Delivery 将游戏更新大小缩减了 90% 并将更新率提高了 10%
借助 Game Mode API,Gameloft 将设备功耗降低了 70%,从而使游戏时长延长了 35%
Gameloft 借助 Google Play Asset Delivery 将新用户数增加了 10%
RV AppStudios 借助 Google Play Asset Delivery 提高了用户留存率
Pixonic 针对大屏设备进行优化后,在 ChromeOS 上的互动度提高了 25%
Gameloft 针对 ChromeOS 进行优化后,收入迅速提升了 9 倍
Кодлабы
Видео
Блоги
{% дословно %}Пока рекомендаций нет.
Попытайтесь войти в свой аккаунт Google.