1. 事前準備
這不是:
- 如何打造適用於 Android Auto 和 Android Automotive OS 的媒體 (音訊 - 例如音樂、無線電、Podcast) 應用程式相關指南。如要進一步瞭解如何建構這類應用程式,請參閱「打造車用媒體應用程式」一文。
軟硬體需求
- 最新版 Android Studio。
- 具備基本 Kotlin 使用經驗。
- 具備建立 Android 虛擬裝置並在 Android Emulator 中執行的經驗。
- 具備 Jetpack Compose 的基本知識。
- 瞭解如何建構可在停車狀態下使用的 Android Automotive OS 應用程式,例如在「建構及測試適用於停車狀態的 Android Automotive OS 應用程式」程式碼研究室中所述。
建構項目
在本程式碼研究室中,您將瞭解如何在 Road Reels 中新增行車期間的音訊播放支援功能,這是一款同時支援行動裝置和 Android Automotive OS 裝置的既有應用程式。
啟用使用者體驗限制時,起始版本會暫停播放。 | 啟用使用者體驗限制時,完成版應用程式會繼續播放。 |
課程內容
- 如何在 Android Automotive OS 影片應用程式中啟用背景音訊播放功能
2. 做好準備
取得程式碼
- 您可以在
car-codelabs
GitHub 存放區的build-a-parked-app
目錄中找到本程式碼研究室的程式碼。如要複製這個存放區,請執行下列指令:
git clone https://github.com/android/car-codelabs.git
- 或者,您也可以將存放區下載為 ZIP 檔案:
開啟專案
- 啟動 Android Studio 後,匯入專案並只選取
build-a-parked-app/end
目錄。build-a-parked-app/audio-while-driving
目錄內含解決方案程式碼;如果遇到困難,或只是想查看完整專案,都可以隨時參考。
熟悉程式碼
- 在 Android Studio 中開啟專案後,請花點時間瀏覽範例程式碼。
3. 瞭解可在車輛停妥時使用的 Android Automotive OS 應用程式
可在車輛停妥時使用的應用程式成為 Android Automotive OS 支援的部分應用程式類別。截至本文撰寫時為止,這些應用程式包含影片串流應用程式、網頁瀏覽器和遊戲等類別。這些應用程式非常適合在車上使用,因為車輛硬體內建 Google 服務且電動車的普及率不斷提高,駕駛和乘客可在充電期間與這類應用程式互動。
車輛在許多方面都類似於平板電腦和摺疊式裝置等其他大螢幕裝置。配備的觸控螢幕大小、解析度和顯示比例相似,也可能為直向或橫向 (但這類裝置的螢幕方向是固定的,與平板電腦不同)。此外,這類連結裝置隨時可能連上或中斷網路連線。考量上述所有因素後,自然會得出以下結論:已針對大螢幕完成最佳化的應用程式,通常還須投入最低限度的工作量,才能為車輛打造絕佳使用者體驗。
與大螢幕應用程式相似,車用應用程式也有應用程式品質等級:
- 第 3 級 - 可供車輛使用:應用程式與大螢幕相容,且可在車輛停妥後使用。雖然這類應用程式可能沒有任何針對車輛進行最佳化調整的功能,但使用者可以像在其他大螢幕 Android 裝置上一樣使用應用程式。符合上述規定的行動應用程式,可透過「車用行動應用程式」計畫,直接發布到車輛。
- 第 2 級 - 針對車輛完成最佳化調整:應用程式可在車輛中控台螢幕上提供絕佳體驗。為達成這個目的,應用程式需要一些車輛專屬的工程,根據應用程式的類別,提供可用於行車或停車模式的功能。
- 第 1 級 - 針對車輛提供差異化設計:應用程式可在各種車輛硬體上運作,並配合行車和停車模式分別提供相應的體驗。對於車輛中的不同螢幕 (例如中控台、儀表板,以及許多高級車會配備的全景螢幕等其他螢幕),應用程式都會提供最佳使用者體驗。
在本程式碼研究室中,您將實作行車期間的音訊播放功能 (這是第 1 級功能),讓應用程式成為「車輛差異化」應用程式。
4. 在 Android Automotive OS 模擬器中執行應用程式
安裝 Automotive with Play Store 系統映像檔
- 首先,在 Android Studio 中開啟 SDK Manager,然後選取「SDK Platforms」分頁標籤 (如果尚未選取)。在 SDK Manager 視窗的右下角,確認已勾選「Show package details」方塊。
- 安裝「Add generic system images」清單中的任一 API 34 Android Automotive 模擬器映像檔。映像檔只能在與本身架構 (x86/ARM) 相同的機器上執行。
建立 Android Automotive OS Android 虛擬裝置
- 開啟裝置管理工具後,選取視窗左側「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 (Module :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>
更新 PlayerViewModel 以使用 PlaybackService
PlaybackService
會自行建立並顯示MediaSession
,因此PlayerViewModel
就不需再建立媒體工作階段。找出並刪除以下這行和所有變數參照:
PlayerViewModel.kt
private var mediaSession: MediaSession? = null
- 接下來,將
init
區塊中用來建立RoadReelsPlayer
例項的區段,替換為以下程式碼,即可使用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 商店審查。請務必篩選出您的應用程式類別,符合相關標準規範。