1. 시작하기 전에
이 Codelab의 의도하지 않은 용도
- Android Auto 및 Android Automotive OS용 미디어(오디오 - 예: 음악, 라디오, 팟캐스트) 앱을 만드는 방법에 관한 가이드. 이러한 앱을 빌드하는 방법에 관한 자세한 내용은 자동차용 미디어 앱 빌드를 참고하세요.
필요한 항목
- 최신 Android 스튜디오
- 기본적인 Kotlin 사용 경험
- Android Virtual Device를 생성하고 Android Emulator에서 실행한 경험.
- Jetpack Compose에 관한 기본 지식.
- Android Automotive OS용 주차 앱 빌드 및 테스트 Codelab에서 다룬 것처럼 Android Automotive OS용 주차 앱을 빌드하는 방법에 관한 이해
빌드할 항목
이 Codelab에서는 휴대기기 및 Android Automotive OS 기기를 모두 지원하는 기존 앱인 Road Reels에서 운전 중 오디오 지원을 추가하는 방법을 알아봅니다.
사용자 환경 제한이 활성화된 동안 재생을 일시중지하는 시작 버전입니다. | 완성된 버전의 앱은 사용자 환경 제한이 활성화된 동안 재생을 계속합니다. |
학습할 내용
- Android Automotive OS의 동영상 앱에서 백그라운드 오디오 재생을 사용 설정하는 방법
2. 설정
코드 가져오기
- 이 Codelab의 코드는
car-codelabs
GitHub 저장소의build-a-parked-app
디렉터리에서 찾을 수 있습니다. 코드를 클론하려면 다음 명령어를 실행합니다.
git clone https://github.com/android/car-codelabs.git
- 또는 저장소를 ZIP 파일로 다운로드할 수도 있습니다.
프로젝트 열기
- Android 스튜디오를 시작한 후
build-a-parked-app/end
디렉터리만 선택하여 프로젝트를 가져옵니다.build-a-parked-app/audio-while-driving
디렉터리에는 솔루션 코드가 포함되어 있습니다. 솔루션 코드는 도움이 필요한 경우 또는 전체 프로젝트를 살펴보고 싶을 때 언제든지 참조할 수 있습니다.
코드 숙지하기
- Android 스튜디오에서 프로젝트를 열고 시작 코드를 살펴봅니다.
3. Android Automotive OS용 주차 앱 알아보기
주차 앱은 Android Automotive OS에서 지원하는 앱 카테고리의 하위 집합을 구성합니다. 주차 앱은 본 Codelab 작성 시점을 기준으로 동영상 스트리밍 앱, 웹브라우저, 게임으로 구성되어 있습니다. 이들 앱은 Google이 내장된 차량에 사용되는 하드웨어와 전기자동차의 높아진 보급률이라는 조건에 매우 적합합니다. 전기자동차의 충전 시간은 운전자와 승객이 이러한 유형의 앱을 활용할 좋은 기회가 됩니다.
자동차는 여러 가치 측면에서 태블릿이나 폴더블과 같은 다른 대형 화면 기기와 유사합니다. 자동차의 터치스크린은 태블릿 및 폴더블과 크기, 해상도, 가로세로 비율이 비슷하며 세로 또는 가로 방향일 수 있습니다(단, 태블릿과 달리 방향은 고정되어 있음). 자동차는 네트워크에 연결되거나 연결이 끊어질 수 있는 연결된 기기이기도 합니다. 이러한 모든 점을 고려했을 때, 대형 화면에 최적화된 앱에 적은 양의 변경사항만 적용하면 자동차에서 우수한 사용자 경험을 제공할 수 있습니다.
자동차용 앱에도 대형 화면과 마찬가지로 앱 품질 등급이 있습니다.
- 3등급 - 자동차 지원: 앱이 대형 화면과 호환되며 자동차가 주차되어 있는 동안 사용할 수 있습니다. 자동차에 최적화된 기능은 없지만 사용자는 다른 대형 화면 Android 기기에서와 마찬가지로 앱을 경험할 수 있습니다. 이러한 요구사항을 충족하는 모바일 앱은 자동차 지원 모바일 앱 프로그램을 통해 있는 그대로 자동차에 배포할 수 있습니다.
- 2등급 - 자동차 최적화: 앱이 자동차의 센터 스택 디스플레이에서 우수한 경험을 제공합니다. 이를 위해 앱의 카테고리에 따라 운전 모드 또는 주차 모드에서 사용할 수 있는 기능을 포함하도록 앱에 자동차 전용 엔지니어링이 적용됩니다.
- 1등급 - 자동차 차별화: 앱이 자동차의 다양한 하드웨어에서 작동하도록 빌드되었으며 운전 모드와 주차 모드에 맞게 환경을 조정할 수 있습니다. 1등급은 자동차의 여러 화면(중앙 콘솔, 계기판, 여러 프리미엄 자동차에서 볼 수 있는 파노라마 디스플레이와 같은 추가 화면)에 맞게 설계된 최상의 사용자 경험을 제공합니다.
이 Codelab에서는 1등급 기능인 운전 중 오디오를 구현하여 앱을 자동차 차별화 앱으로 만듭니다.
4. Android Automotive OS 에뮬레이터에서 앱 실행
Play 스토어 시스템 이미지로 Automotive 설치
- 먼저 Android 스튜디오에서 SDK Manager를 열고, 아직 선택하지 않았다면 SDK Platforms 탭을 선택합니다. SDK Manager 창의 오른쪽 하단에서 Show package details 체크박스가 선택되어 있는지 확인합니다.
- Add generic system images에 나열된 API 34 Android Automotive 에뮬레이터 이미지 중 하나를 설치합니다. 이미지는 자신과 동일한 아키텍처(x86/ARM)를 사용하는 컴퓨터에서만 실행됩니다.
Android Automotive OS Android Virtual Device 만들기
- 기기 관리도구를 열고 창 왼쪽의 Category 열에서 Automotive를 선택합니다. 그런 다음 목록에서 Automotive (1408p landscape) 번들 하드웨어 프로필을 선택하고 Next를 클릭합니다.
- 다음 페이지에서 이전 단계의 시스템 이미지를 선택합니다. Next를 클릭하고 원하는 고급 옵션을 선택한 다음 Finish를 클릭하여 AVD를 만듭니다.
앱 실행
app
실행 구성을 사용하여 방금 만든 에뮬레이터에서 앱을 실행합니다. 플레이어 화면으로 이동하고 운전을 시뮬레이션하여 앱 동작을 테스트합니다.
5. 운전 중 오디오 지원 감지
운전 중 오디오는 일부 차량에서만 지원되므로 현재 기기에서 이 기능을 지원하는지 감지하고 그에 따라 앱 동작을 조정해야 합니다. 이렇게 하려면 androidx.car.app:app
라이브러리의 CarFeatures
클래스를 사용하면 됩니다.
androidx.car.app:app
아티팩트의 종속 항목을 추가합니다.
libs.version.toml
[versions]
...
carApp = "1.7.0-rc01"
[libraries]
...
androidx-car-app = { group = "androidx.car.app", name = "app", version.ref = "carApp" }
build.gradle.kts(모듈 :app)
implementation(libs.androidx.car.app)
- 그런 다음
RoadReelsPlayer
에서 기능이 지원되는지 확인하고shouldPreventPlay
값을 계산하는 데 사용되는 로직을 업데이트할 수 있습니다.
RoadReelsPlayer.kt
if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
...
val isBackgroundAudioWhileDrivingSupported = CarFeatures.isFeatureEnabled(
context,
CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING
)
shouldPreventPlay = !isBackgroundAudioWhileDrivingSupported &&
carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
invalidateState()
carUxRestrictionsManager.registerListener { carUxRestrictions: CarUxRestrictions ->
shouldPreventPlay = !isBackgroundAudioWhileDrivingSupported &&
carUxRestrictions.isRequiresDistractionOptimization
...
}
}
변경사항을 적용한 앱을 다시 실행하고 운전을 시뮬레이션합니다. 이제 앱의 UI가 시스템에 의해 가려져도 오디오 재생이 계속됩니다. 하지만 아직 완료되지 않았습니다. 모든 요구사항을 충족하려면 몇 가지 사항을 더 변경해야 합니다.
6. 백그라운드 재생 지원
현재 앱의 미디어 재생은 활동에서 처리합니다. 따라서 사용자 환경 제한이 활성화되고 활동이 가려진 후에도 미디어 재생이 잠시 계속될 수 있지만 결국 앱이 시스템에 의해 캐시되어 재생이 종료됩니다.
장기 재생을 지원하려면 서비스를 사용하여 재생을 처리하도록 앱을 업데이트해야 합니다. Media3 MediaSessionService
를 사용하여 이 작업을 실행할 수 있습니다.
MediaSessionService 만들기
- Project 창에서
com.example.android.cars.roadreels
패키지를 마우스 오른쪽 버튼으로 클릭하고 New > Kotlin Class/File을 선택합니다. 파일 이름으로PlaybackService
를 입력하고 Class 유형을 클릭합니다. - 다음과 같은
PlaybackService.
구현을 추가합니다.MediaSessionService
에 관한 자세한 내용은 MediaSessionService를 사용한 백그라운드 재생을 참고하세요.
PlaybackService.kt
import androidx.media3.common.util.UnstableApi
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
@UnstableApi
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val player = RoadReelsPlayer(this)
mediaSession = MediaSession.Builder(this, player).build()
}
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
if (controllerInfo.isTrusted || controllerInfo.packageName == packageName) {
return mediaSession
}
return null
}
}
- 매니페스트 파일에 다음
<uses-permission>
요소를 추가합니다. 미디어 재생이 포그라운드 서비스를 사용하여 처리됩니다.
AndroidManifest.xml
<manifest ...>
...
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
...
</manifest>
- 매니페스트에서도
PlaybackService
를 선언합니다.
AndroidManifest.xml
<application ...>
...
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
</application>
PlaybackService를 사용하도록 PlayerViewModel 업데이트
PlaybackService
가MediaSession
을 직접 만들고 노출하므로PlayerViewModel
이 더 이상 MediaSession을 만들 필요가 없습니다. 다음 줄과 변수에 대한 모든 참조를 찾아 삭제합니다.
PlayerViewModel.kt
private var mediaSession: MediaSession? = null
- 다음으로
RoadReelsPlayer
를 인스턴스화하는init
블록의 섹션을MediaController
를 사용하여 앱을PlaybackService
에 연결하는 다음 코드로 바꿉니다.
PlayerViewModel.kt
import android.content.ComponentName
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.example.android.cars.roadreels.PlaybackService
import com.google.common.util.concurrent.MoreExecutors
...
init {
viewModelScope.launch {
...
}
val sessionToken = SessionToken(
application,
ComponentName(application, PlaybackService::class.java)
)
val mediaControllerFuture =
MediaController.Builder(getApplication(), sessionToken).buildAsync()
mediaControllerFuture.addListener(
{ _player.update { mediaControllerFuture.get() } },
MoreExecutors.directExecutor()
)
}
앱을 다시 테스트하면 앱이 시스템에 의해 차단될 때 다른 UI가 표시됩니다. 이제 사용자는 이동 중에 재생을 제어할 수 있습니다. 휴식을 취할 때는 종료 버튼을 클릭하여 전체 앱 환경으로 돌아갈 수 있습니다.
수명 주기 재생 변경사항 삭제
이제 백그라운드 재생이 지원되므로 사용자가 앱을 종료할 때 재생이 계속될 수 있도록 PlayerScreen.kt
에서 두 개의 LifecycleEventEffect
블록을 삭제할 수 있습니다. 여기에는 이전 화면에 표시된 재생 컨트롤이 표시되는 경우도 포함됩니다.
플레이어 화면을 벗어날 때 재생 일시중지
이제 PlaybackService
에 포함된 실제 플레이어는 플레이어 화면을 벗어날 때 해제되지 않으므로 이전 동작을 유지하려면 플레이어 화면에서 나갈 때 재생을 일시중지하는 호출을 추가해야 합니다. 이렇게 하려면 PlayerViewModel
의 onCleared
메서드 구현을 업데이트하면 됩니다.
PlayerViewModel.kt
override fun onCleared() {
super.onCleared()
_player.value?.apply {
pause()
release()
}
}
7. 매니페스트 업데이트
마지막으로 앱이 운전 중 오디오를 지원한다고 표시하려면 앱의 매니페스트에 다음 <uses-feature>
요소를 추가해야 합니다.
AndroidManifest.xml
<application>
<uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false">
</application>
8. 축하합니다
동영상 앱에 운전 중 오디오 지원을 추가했습니다. 이제 배운 내용을 자체 앱에 적용해 보세요.
추가 자료
- 자동차용 주차 앱 빌드
- Android Automotive OS용 동영상 앱 빌드
- 주차 앱에 Android Automotive OS 지원 추가
- 자동차용 Android 앱 품질 페이지에서는 우수한 사용자 경험을 제공하고 Play 스토어 검토를 통과하기 위해 앱이 충족해야 하는 기준을 설명합니다. 앱의 카테고리로 필터링하세요.