Closed fornewid closed 2 years ago
전년 대비 거의 3배의 활성화 증가율을 보이고 있다. 앱 성능을 측정하고 최적화하기 위한 모범 사례와 함께 현대적이고 전력 효율적인 Wear OS 앱 개발에 대해 알아본다.
작년부터 Jetpack Compose를 Wear OS로 가져오려고 노력하고 있다.
Jetpack Compose는 UI 개발을 단순화하고 가속화하는 데 도움이 되는 현대적인 선언형 UI 툴킷이다. 또한 이미 Wear Material Design을 준수하는 다양한 UI 구성 요소를 제공하므로 디자인 가이드라인을 더 쉽게 따를 수 있다.
Wear OS용 Compose는 Jetpack Compose와 모든 원칙과 기본 구성 요소를 공유한다. 또한 시계 UX에 최적화된 고유한 구성 요소도 있다.
즉, 프로덕션 앱을 만드는데 사용할 수 있고, API가 Stable 임을 의미한다. 올해 하반기에 1.0 release 버전이 출시될 예정이고, 성능과 기존 구성 요소를 연마하는 데 중점을 둘 것.
오픈 소스 커뮤니티의 피드백과 참여를 통해 Compose for Wear OS를 개발해왔다. 개발자 프리뷰 이후로 Navigation, Scale LazyLists, 입력 및 제스처 지원 등과 같은 많은 구성 요소를 추가했다.
Google의 스마트워치 플랫폼용 UI를 구현할 때, Compose for Wear OS 사용을 권장한다.
Compose for Wear OS는 이미 View와 함께 사용할 수 있는 더 많은 구성 요소를 제공하며 최신 Wear OS 디자인 지침을 준수하는 사용자 환경을 만드는 데 도움이 되도록 설계되었다.
오늘은 베타 버전에 적용된 주요 변경 사항에 중점을 둔다. Compose for Wear OS의 기본 구성 요소 및 원칙에 대해 자세히 알아보려면 개발자 사이트를 방문하세요.
swipe-to-dismiss 제스처를 지원하는 Navigation composable을 제공한다. 개념은 Mobile과 비슷해서 Compose 지식을 그대로 적용할 수 있다.
val navController = rememberSwipeDismissableNavController()
SwipeDismissableNavHost(
navController = navController,
startDestination = Screen.MainScreen.route
) {
composable(route = Screen.MainScreen.route) {
MyListScreen( ... )
}
composable(route = Screen.DestailsScreen.route + "/{$ID}", ...) {
MyDetailScreen( ... )
}
}
ScalingLazyColumn은 스크롤할 때 가장자리의 항목이 축소되고 투명해지는 컴포넌트.
ScalingLazyColumn(
autoCentering = AutoCenteringParams() // default value
) {
item {
ListHeader { Text("Title") }
}
items(20) {
Chip(
label = { Text("List item $it") },
colors = ChipDefaults.secondaryChipColors()
)
}
}
AutoCentering: 첫 번째 항목과 마지막 항목을 포함한 모든 항목이 스크롤되어 뷰포트 중앙에 표시된다.
항목의 중심 또는 가장자리가 뷰포트 중심선에 정렬되어야 하는지 여부를 제어한다.
ItemStart | ItemCenter |
---|---|
val state = rememberScalingLazyListState()
ScalingLazyColumn(
state = state,
flingBehavior = ScalingLazyColumnDefaults
.snapFlingBehavior(
state = state,
snapOffset = 0.dp, // default
decay = exponentialDecay()
)
) { item { ... } }
Fling 동작을 커스터마이징 할 수도 있다.
Picker를 사용하면 스크롤되는 목록에서 항목을 선택할 수 있다. 기본적으로 선택가능한 목록은 회전하는 실린더의 느낌을 주기 위해 양방향으로 무한 반복된다.
Picker(
state = rememberPickerState(
initialNumberOfOptions = 12,
initiallySelectedOption = 5,
repeatItems = false
),
modifier = Modifier.size(64.dp, 100.dp),
option = { hour: Int -> Option(0, "%2d".format(hour + 1)) },
flingBehavior = PickerDefaults.flingBehavior(state),
readOnly = selectedColumn != 0,
readOnlyLabel = { Text("PickerLabel") }
)
ScalingLazyColumn과 유사하게 Picker에는 flingBehavior 매개변수가 있으며 기본값은 스크롤할 때 가장 가까운 옵션으로 스냅된다.
또한 여러 Picker가 있는 화면에서 한 번에 하나의 Picker만 편집할 수 있도록 ReadOnly 모드를 추가했다. Picker가 ReadOnly 모드인 경우 현재 선택한 옵션과 레이블(제공된 경우)만 표시한다.
InlineSlider를 사용하면 값 범위에서 선택할 수 있다. 주로 볼륨이나 밝기와 같은 설정을 조정하는데 적합하다.
var value by remember { mutableStateOf(2f) }
InlineSlider(
value = value,
onValueChange = { value = it },
valueRange = 1f..4f,
segmented = true,
steps = 7,
increaseIcon = { Icon(...) },
decreaseIcon = { Icon(...) }
)
Stepper는 사용자가 값 범위에서 선택할 수 있도록 하는 전체 화면 제어 구성 요소입니다. 예를 들어, 헤드폰의 볼륨을 제어할 때 가운데 슬롯에 텍스트가 있어야 한다.
@Composable
fun StepperScreen(
displayValue: Int,
onValueChange: (Int) -> Unit
) {
Stepper(
value = displayValue,
onValueChange = onValueChange,
valueProgression = 1..10
) { Text("Value: $displayValue") } // show the current steps
}
Dialog는 전체 화면 대화 상자를 표시하며 다른 콘텐츠 위에 겹쳐진다. 경고 또는 확인 메시지로 예상되는 단일 슬롯이 필요하고, swipe-to-dismiss를 지원한다.
Box {
var showDialog by remember { mutableStateOf(false) }
// launches the Alert Dialog by setting the value of showDialog
Chip(
onClick = { showDialog = true },
label = { Text("Show dialog") },
)
if (showDialog) {
Dialog(onDismissRequest = { showDialog = false }) {
Alert(...) // alert details
}
}
}
CircularProgressIndicator는 원형으로 진행 상황을 표시하는 데 사용된다.
CircularProgressIndicator() // infinite progress indicator
CircularProgressIndicator(
modifier = Modifier.fillMaxSize().padding(all = 1.dp),
progress = 0.3f,
strokeWidth = 5.dp,
startAngle = 295.5f,
endAngle = 245.5f
)
CircularProgressIndicator를 사용하는 방법에는 몇 가지 옵션이 있다.
var progress by remember { mutableStateOf(0.1f) }
val animatedProgress by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator(
progress = animatedProgress
)
CompactChip(
modifier = Modifier.width(90.dp),
onChip = { if (progress < 1f) progress += 0.1f },
)
}
Android Studio Electric Eel을 사용하면, Compose for Wear OS로 개발할 때 자동완성이나 Live Edit 등 다양한 기능들을 사용할 수 있다. Wear OS를 시작할 때 도움되도록 Wear OS용 Compose 새 프로젝트 템플릿을 추가했다.
import androidx.compose.ui.tooling.preview
@Preview(
apiLevel = 26,
uiMode = Configuration.UI_MODE_TYPE_WATCH,
showSystemUi = true,
device = Devices.WEAR_OS_LARGE_ROUND
)
@Composable
fun PreviewCustomComposable() {
CustomComposable(...)
}
Live Edit: https://youtu.be/jpUVamtoKOs?t=851
코드를 다시 컴파일하거나 앱을 설치할 필요가 없다. Live Edit는 편집기에서 변경 사항을 적용할 때 빠른 피드백을 제공하고 미리보기 창이나 장치의 앱에 UI를 즉시 반영한다.
Wear OS용 Compose에서 제공하는 기능을 확장하는 Wear 라이브러리 세트를 제공하는 Google 오픈 소스 프로젝트.
Making your app snappy
시계는 휴대전화보다 센서가 더 많지만 배터리는 훨씬 작다. 따라서 시작부터 성능에 초점을 맞추는 것이 중요하다.
성능 테스트를 일상적인 개발 흐름과 통합하는 것이 좋다. 이렇게 하면 Play 스토어에 게시하기 전에 문제를 식별하는 데 도움이 되므로, 최종 사용자와 관련된 성능 문제를 제거하는데 들어가는 전체 소요 시간을 줄일 수 있다.
지금부터 앱 성능을 개선하는 데 도움이 되는 몇 가지 간단한 팁을 소개한다.
릴리스 빌드로만 성능을 측정하라.
JankStats 라이브러리를 설치하여 앱을 시작하면 프레임 시간을 확인할 수 있고, 앱 내부 테스트 및 빌드에서 chunky 동작에 대한 세부 정보를 수집할 수 있다.
콜백을 구현하고, Jank 프레임을 로깅하는 예제 코드.
앱 시작 시간을 측정하기 위해 Macobenchmark 라이브러리를 설정한다.
Macrobenchmark는 최초 실행의 콜드 스타트부터 완전히 최적화된 후 웜 스타트까지 다양한 시나리오에서 앱 시작을 정확하게 측정한다.
시작 프로파일링 및 프레임 타이밍에 대한 예제를 보려면 Android Performance 샘플을 참고할 것.
이것은 추가 작업이 필요하지 않다.
사용자가 Play 스토어에서 앱을 다운로드하면 기준 프로필이 시작되어 Compose for Wear OS 및 기타 여러 Jetpack 라이브러리와 같은 라이브러리를 자동으로 최적화한다. 이렇게 하면 시작 성능이 향상되고 앱이 Play 스토어에서 다운로드될 때 버벅거림이 줄어든다.
Macrobenchmark를 사용하여 로컬에서 테스트하여 효과를 측정하거나 앱에 대한 Baseline Profiles 규칙을 생성하여 성능을 더욱 향상시킬 수 있다.
항상 오프라인 사용을 잘 처리할 수 있도록 앱을 만들어라. 네트워크가 연결되지 않은 경우에도 잘 작동해야 한다.
LTE만 사용할 수 있는 경우에는, 모든 화면에서 콘텐츠를 새로 고치거나 사용자가 목록을 스크롤할 때 단일 이미지를 로드하는데 LTE를 사용하지 마십시오. 빠른 Wi-Fi가 연결되어 있고 시계가 충전 중일 때, 나중에 필요할 수 있는 이미지를 미리 가져와 캐시해 보십시오.
대부분의 네트워크 사용 최적화는 WorkManager 같은 Android 프레임워크를 사용하여 쉽게 수행할 수 있다. 새로 고침 작업을 즉시 실행하는 대신 적절한 빈도로 적절한 조건이 충족될 때 실행되도록 예약하면 된다.
Health Services는 건강 데이터를 효율적으로 수집할 수 있다.
Power-efficient health and fitness apps
스마트워치는 수백만 명의 사람들이 건강과 피트니스 관리에 보다 적극적인 역할을 할 수 있도록 지원한다. Wear OS에서는 개발자가 이러한 사용자를 위한 훌륭한 경험을 구축할 수 있도록 돕는 것이 핵심이다.
그리고 Health Services를 이용하면 Wear OS 시계에서 생성된 센서 데이터를 활용하는 전력 효율적인 앱을 훨씬 쉽게 구축할 수 있다. 작년에 우리는 알파로 Health Services를 출시했고, 올해는 API가 곧 베타 버전으로 출시될 예정이다.
Health Services를 개선하기 위해 작년에 수많은 앱 개발자와 광범위하게 협력했다.
개발자는 Health Services를 사용하여 실시간 센서 데이터를 수집한 다음, 해당 데이터를 사용자 휴대폰의 Health Connect에 저장할 수 있다. 사용자 권한이 있는 앱은 이 데이터 저장소에 액세스하고 서로 동기화할 수 있다.
이 세션에서는 배터리 수명, 개발자 경험, 미래 보장이라는 세 가지에 초점을 맞추고 싶다.
먼저 배터리 수명을 살펴본다.
건강 및 피트니스 추적을 위해서 이상적으로는 여러 날에 걸쳐 운동, 일일 활동 및 수면과 같은 항목을 측정하기 위해 높은 빈도로 센서가 항상 켜져 있어야 한다.
구형 스마트워치에서 이러한 센서와 알고리즘은 주요 애플리케이션 프로세서의 각 앱에서 관리했으며, 이는 심박수와 같은 데이터를 처리해야 할 때마다 깨어난다.
이 접근 방식은 배터리 수명에 큰 영향을 미쳤다.
Health Services를 통해 앱은 최신 스마트워치 아키텍처를 활용할 수 있고, 이 아키텍처에서는 훨씬 더 많은 데이터 수집 및 처리를 저전력 프로세서로 내릴 수 있으므로 장기간 동안 애플리케이션 프로세서를 일시 중단할 수 있다.
이는 Health Services가 다른 API보다 훨씬 적은 전력을 소비하면서도 여전히 빈도가 높은 데이터를 제공할 수 있음을 의미한다.
다음으로 개발자 경험에 대해 몇 가지 얘기하고 싶다.
Health Services를 사용하면 기본 하드웨어의 모든 차이점에 우아하게 적응하는 경험을 통해 앱을 한 번만 작성할 수 있다. 베타 릴리스에서는 건강 및 피트니스 경험의 핵심 사용 사례에 해당되는 세 가지 클라이언트를 제공한다.
그 중 하나인 ExerciseClient를 사용하여 운동을 시작하는 예제 코드. 위치, 속도 및 심박수, 평균 속도, 전체 거리와 같은 메트릭을 계산할 수 있다.
운동을 시작한 후 업데이트를 수신하는 UpdateListener를 만드는 예제 코드. 활성 또는 일시 중지와 같은 데이터 및 운동 상태도 Health Services에서 관리한다.
마지막으로 미래 보장에 대해서. 스마트워치 기술은 빠르게 발전하고 있고, 매년 이러한 장치에 새로운 센서와 더 나은 알고리즘이 탑재된다.
Health Services는 이러한 변경이 이루어짐에 따라 성장하도록 구축되어 앱이 Wear 기기 전반에 걸쳐 일관된 API를 통해 개선사항을 활용할 수 있도록 한다.
새로운 시계에서 새로운 센서를 사용할 수 있게 되면 API에 새로운 데이터 유형을 추가하고 해당 측정항목에 쉽게 액세스할 수 있다.
Compose for Wear OS
Performance
Health Services
https://www.youtube.com/watch?v=jpUVamtoKOs
목차