隨著 Android Auto 和 Android Automotive OS 車輛的車內螢幕越來越大,開發人員面臨獨特的雙重挑戰:在停車時提供豐富、自動調整的體驗,以及在駕駛時提供安全、以範本為基礎的體驗。在這場 30 分鐘的完整技術講座中,我們將探討端對端架構和開發方法,瞭解如何讓 Android 應用程式適用於車輛。
從設計車用應用程式的基本概念,到打造媒體體驗的最新方法 (例如在車輛從行駛狀態切換為停車狀態時,將音訊切換為影片),您將瞭解所有必要知識,讓應用程式在車上順利運作。
轉錄稿
現在我們來做個小測驗
大家好。
感謝各位參加「車輛就是你的畫布」講座。
本課程將介紹如何開發 Android Auto 應用程式。
我是 Paul Lammertsma。
我是 Thomas Weathers。
我們是車輛專用 Android 團隊的開發人員關係工程師。
相信許多人都花費大量時間和精力,確保應用程式在各種螢幕和裝置 (手機、摺疊式裝置、平板電腦等) 上,都能呈現精美外觀和良好功能。
今天,我們要著重介紹一個你可能沒想過的螢幕。
並以獨特的方式觸及使用者。
也就是車內螢幕。
首先,我們來簡單介紹一下車輛專用 Android。
車輛專用 Android 並非單一系統。
您可以指定兩種平台。
首先是 Android Auto,這是一種由手機驅動的投影系統。
使用 Android Auto 時,應用程式會在手機上執行,並透過有線或無線連線將 UI 投影到車內螢幕。
第二種是 Android Automotive OS,這是直接在車輛上執行的 Android 變體。
Android Automotive OS 獨立於手機運作,可直接存取車輛硬體,例如空調和收音機,而且重要的是,它還能管理自己的數據連線。
部分搭載 Android Automotive OS 的車輛內建 Google 服務。
內建 Google 服務的車輛也隨附 Google 應用程式和服務,例如 Play 商店、Gemini 和 Google 地圖。
雖然有兩個平台,但我們的目標是讓您輕鬆建構一次,就能部署在兩個平台上提供優質體驗的應用程式。
以下範例說明使用 Car App Library 建構的媒體應用程式,在 Android Auto 和 Android Automotive OS 上,以及在各種不同螢幕尺寸和形狀上的外觀。
您可以為許多不同類型的裝置和介面建構應用程式。
這就引出一個問題:為什麼要為汽車提供這項服務?
首先,這裡的規模非常龐大。
目前路上有超過 2.5 億輛與 Android Auto 相容的汽車,以及 16 個品牌、超過 100 款內建 Google 服務的車型。
當應用程式進入車內,您就能在使用者日常通勤、往返家庭活動、辦事,甚至公路旅行時,觸及他們。
根據美國汽車協會 (AAA) 2024 年美國駕駛調查,美國駕駛人平均每天開車 60 分鐘。
不過,除了在使用者開車時觸及他們,
以及車輛停妥時。
例如在電動車充電站充電,或在學校接送區排隊。
隨著乘客專用螢幕的興起,我們預期未來會有更多機會提升使用者參與度。
現在我們來做個小測驗
接著來看看可以為車輛打造哪些類型的體驗。
如要瞭解如何建構車用應用程式,建議您先瞭解應用程式在車內執行時的兩種主要情況和狀態。
首先,有些應用程式適合在開車時使用。
這些體驗經過最佳化,適合在車輛行進時使用。
包括畫面上列出的類別,例如音訊應用程式 (通常稱為媒體)、訊息和通訊應用程式、導航等。
行車最佳化體驗必須遵循嚴格的分心防護標準。
建構應用程式 UI 時,必須特別考量如何確保車輛行駛時能安全使用。
目前有幾種不同的方法可以建構行車最佳化應用程式。
不過,這場講座會著重在使用 Car App Library。
這是一組 Jetpack 程式庫,提供範本式架構,除了提供針對駕駛情境最佳化的使用者介面,也能配合車輛專用的各種硬體設定進行調整。
因此,請考量輸入模式、螢幕大小和顯示比例等因素。
雖然這不是為車輛打造行車最佳化體驗的唯一方法,但我在這場演講中想著重介紹的方法,是使用車輛專用 Android 應用程式庫建構。
我們通常簡稱為 CAL。
如稍早所述,Car App Library 是一組程式庫,可協助您建構車輛專用的應用程式。
這個程式庫提供範本式架構,除了提供針對駕駛情境最佳化的使用者介面,也能配合不同的硬體設定進行調整。
您可以放心建構應用程式,無論是在 Android Auto 或 Android Automotive OS 上執行,都能在所有不同的設定中順暢運作。
使用 CAL 建構的應用程式可以依賴這個主機用戶端架構。
主機應用程式是系統轉譯器,可能是 Android Auto 本身,或內建 Google 服務的車輛中的 Google Automotive App Host。
最終,它會控制要在使用者螢幕上顯示的內容。
因此,身為應用程式開發人員,您最終不會繪製像素。
而是將結構化資料和資料狀態提供給 Car App Library 範本。
接著,這個主機就會接收這項資料,並以符合安全規範的方式,安全地繪製資料,並採用分心因素排除功能最佳化的版面配置。
接著,我們來看看 CAL 中的類別,以及建構應用程式時需要擴充哪些類別,首先是 CarAppService。
這是 Android 服務的子類別,可做為主機應用程式與用戶端應用程式通訊的進入點,用途是建立工作階段例項,供主機與其互動。
您可以將工作階段視為在車輛螢幕上執行的用戶端應用程式例項。
這個類別與其他 Android 元件一樣擁有專屬生命週期,可用於在整個工作階段例項存在期間初始化及拆卸各種資源。
CarAppService 和工作階段之間是一對多關係。
舉例來說,CarAppService 可能有兩個工作階段例項,一個用於主要螢幕,另一個用於儀表板螢幕 (適用於支援儀表板螢幕的導航應用程式)。
以下是範例:車輛有主要螢幕和儀表板螢幕,每個螢幕都與專屬工作階段相關聯,這也是應用程式開發人員支援多螢幕車輛的方式。
在工作階段中,畫面例項負責產生由主機應用程式轉譯的使用者介面。
這些使用者介面由範本類別表示,其中每個模型代表一個特定類型的版面配置,例如網格或清單。
因此,每個工作階段都會管理一組畫面例項,用於處理應用程式不同部分的使用者流程。與工作階段一樣,畫面也有自己的生命週期,可供您掛鉤。
不過,雖然主機負責轉譯 UI,且您使用的是 Car App Library 應用程式庫範本,但這不代表您會受到嚴格限制,或無法自訂應用程式。
您可以使用豐富的範本和元件,也可以混搭使用,打造理想體驗。
範本分為一般用途和特殊用途,前者適用於 Car App Library 中的所有應用程式類型,後者則專為特定應用程式體驗而設計,例如導航或搜尋點。
不過,在詳細說明如何打造行車最佳化體驗之前,我想先介紹另一種在車內執行應用程式的情況:車輛停妥時。
某些類別的應用程式只能在車輛停妥時使用,包括影片應用程式、遊戲和瀏覽器。
但車輛靜止時,駕駛人分心等級規範就會解除,您可以提供更符合現有自動調整 UI 的體驗。
閒置應用程式是以活動為基礎,與其他裝置 (例如手機或平板電腦) 上使用的活動類似,甚至相同。我們會在下一節詳細介紹閒置應用程式。
雖然車輛停妥時的應用程式體驗會重複使用現有的自動調整活動,但您仍須遵守車輛專屬的品質指南,包括資訊清單宣告、螢幕凹口,以及車輛開始移動時暫停播放等。
為協助您自動完成這項工作,我們推出了新的 Android 技能,可處理擱置的應用程式。
因此,開始之前,請確認您已安裝 Android CLI,您可以透過這個短連結找到。
設定完成後,請執行 android skills list,探索可用的技能。
android skills list
android skills add android-for-cars-parked-apps您可以運用許多不同的技能,讓 Android 開發作業更上一層樓。
如果是 Android for Cars,您可以使用 android skills add android-for-cars-parked-apps 將技能新增至代理的結構定義。
現在,無論您偏好哪種 AI 代理,都能取得逐步指引,瞭解如何設定 Android Auto 或 Android Automotive OS 的資訊清單、處理車窗插邊和返回瀏覽等事項、強制執行駕駛人分心等級播放限制,甚至還能在您將影片、遊戲或瀏覽器應用程式提交至 Google Play 前,先進行稽核。
因此,由於這些應用程式類別的概念,建構車用應用程式也有些獨特之處。
如前所述,某些類別的應用程式可在行車時使用,其他類別的應用程式則可在停車時使用。
醒目顯示的類別可在開車時使用。
其中有些會標示 CAL,如我所說,這是 Car App Library,這個類別的應用程式會使用我們剛才看到的範本。
現在我會快速帶過這些內容,不會深入探討。
首先是媒體應用程式,這類應用程式會播放音訊,而非影片。
有兩種方法可以建立。
最上方的 MediaBrowserService 是最簡單的選項。
您只需提供中繼資料,資訊娛樂螢幕介面就會以車用最佳化方式,完整呈現播放和媒體瀏覽器 UI。
另一個選項是使用 Car App Library 建構媒體應用程式,這樣一來,您就能進一步控管及自訂媒體應用程式的樣式和呈現方式,Thomas 稍後會進一步說明。
導航應用程式當然是主要用途,可提供路線和即時路線指示。
共乘司機應用程式也屬於這個類別。
搜尋點應用程式可協助你尋找附近的實用地點,例如電動車充電樁或停車位。
物聯網 (IoT) 應用程式可讓您隨時隨地存取智慧型裝置,例如管理住家。
此外,天氣應用程式也相當實用,可提供即時雷達、暴風警報和特定地點的天氣預報等資訊。
最後,通訊類別可細分為三個子類別:將基本通知延伸至車輛、訊息應用程式和視訊通話應用程式。
此外,還有車輛停妥時才能使用的類別:影片串流應用程式、遊戲和網路瀏覽器。
很高興宣布,幾週前,我們已開放所有開發人員在 Android Auto 和 Android Automotive OS 發布遊戲。
您會發現有些類別標示為「開放」,有些標示為「Beta 版」,有些則標示為「即將推出」。
「開放」表示任何開發人員都能將這個類別的應用程式直接提交至正式版。
Beta 版代表您可以將應用程式發布至內部或封閉測試群組,但除非您獲准加入搶先體驗計畫,否則無法發布至正式版群組。
各類別的說明文件包含更多資訊,以及意願調查表的連結。
「即將推出」則是指目前不支援,但我們計劃日後支援的類別。
我們的最終目標是整合平台。
我們希望您能透過一次編寫,同時發布至 Android Auto 和 Android Automotive OS,無論類別為何,都能建構並獲得最大觸及率。
接下來由 Thomas 介紹媒體。
祝一切順心!
太好了!
因此,我們在去年推出 Car App Library 1.8 版,為媒體應用程式提供範本化的瀏覽 UI 和自訂動作,並支援 Android Auto。
包括左側顯示的媒體播放範本。
在今年的 I/O 大會上,我們透過 Car App Library 1.9 推出更多可瀏覽的 UI。
CAL 1.9 進入 Beta 版後,您現在可以解鎖 Android Auto 和 Android Automotive OS 的重要功能更新。
重點功能包括透過應用程式運算式自訂品牌,讓您在維持安全規則的同時,使用主機轉譯的版面配置自訂顏色和形狀符記,以及新的 UI 元件,例如焦點區段、精簡清單項目、可捲動方塊和常駐迷你播放器。
此外,CAL 1.9 完整支援 AAOS 的媒體範本,真正整合兩個生態系統的平台。
Car App Library 的下一個版本即將推出:CAL 第 10 版。
CAL 第 10 版將為通訊應用程式提供更多自訂範本,讓使用者在開車時享有更完善的自訂通話體驗。
我們也新增了應用程式類別和範本,主打行車時的對話式體驗。
最後,我們也正在開發更多功能,讓使用者能透過 Car App Library 範本,更順暢地從停車模式切換至駕駛模式。
我們很快就會推出更多功能,敬請密切關注。
使用 Car App Library 的停車到駕駛功能,應用程式可在車輛靜止時動態轉換模式並顯示自動調整內容,並在駕駛時順暢切換回 Car App Library 範本。
主要應用包括在車輛靜止時提供量身打造的驗證流程,或在車輛停妥時啟用專屬體驗,例如串流播放影片 Podcast 或音樂短片。
這項功能會直接與媒體播放範本整合。
因此,車輛靜止時,駕駛人可以完整存取應用程式的原生 Android 活動,並啟用影片串流等互動式多媒體。
車輛一排檔,防分心機制就會啟動,促使應用程式切換回 Car App Library 媒體範本,確保音訊播放符合規範且不中斷。
車內體驗正處於轉型階段。
媒體應用程式希望超越傳統的瀏覽和播放介面,為駕駛和乘客提供更豐富、更身歷其境且互動性更高的體驗。
接下來,我們將深入探討如何使用 Media3 程式庫新增原生影片活動,以便在車輛切換至停車狀態時開始播放。
首先,請將影片算繪介面直接連結至背景播放例項。
override fun onStart() {
super.onStart()
...
controllerFuture = MediaController.Builder(
this,
sessionToken
).buildAsync()
controllerFuture?.addListener({
mediaController = controllerFuture?.get()
playerView.player = mediaController
}, ContextCompat.getMainExecutor(this))
}您不希望在這個活動中例項化獨立的媒體播放器,而是想在主要服務 (例如 Media3 媒體庫服務) 中維護中央 ExoPlayer 或媒體播放器,同時處理音訊和影片串流。
因此,在行駛期間,如果沒有繫結任何有效服務,播放器只會解碼音訊,但當駕駛人在停車時開啟影片活動,您會想將其算繪介面直接附加至基礎服務播放器。
因此,使用 Media3 時,mediaController 會以原生方式實作播放器,因此可以直接指派,如您所見,playerView set-- playerView.player 直接設定為 mediaController。
接下來,您需要使用 ParkedOnlyOnClickListener 註冊原生進入點。
val watchVideoAction = Action.Builder()
.setTitle("Watch Video")
.setIcon(videoCarIcon)
.setOnClickListener(
ParkedOnlyOnClickListener.create {
val intent = Intent(
carContext,
VideoPlayerActivity::class.java
)
.apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
)
}
carContext.startActivity(intent)
})
.build()這個包裝函式會在媒體播放介面的標頭列中提供意圖觸發條件。
因此,請加入啟動影片播放的動作,並將其選取事件監聽器包裝在 ParkedOnlyOnClickListener 中。
因此在行車模式下,主機應用程式會攔截這些輕觸動作,並顯示系統安全訊息,因此您無法在駕駛時進入該畫面,但當您停車時,回呼會觸發 carContext 內的 startActivity,啟動指定的全螢幕活動。
請注意,在這些 Car App Library 應用程式中,您必須在資訊清單中將影片目標標記為 distractionOptimized=false,作業系統才會辨識出這只是停車工作流程。
<activity
android:name=".VideoPlayerActivity"
android:exported="false"
android:launchMode="singleTop">
<meta-data
android:name="distractionOptimized"
android:value="false" />
</activity>最後,我們來看看駕駛人從停車檔切換到前進檔時會發生什麼事。
如果活動未排除分心因素,且在車輛行駛時仍保持啟用狀態,AAOS 會顯示系統封鎖疊加層,基本上就是要求你停車才能繼續使用應用程式。
為避免畫面遭到封鎖,我們的目標是順暢返回媒體播放範本,不中斷音訊。
因此,當 isRequiresDistractionOptimization 在這裡評估為 true 時,我們基本上會從基礎媒體工作階段清除影片轉譯介面,並在播放器活動上呼叫 finish,因為它是在 CAL 體驗之上啟動,完成後會直接返回媒體播放範本,並繼續播放音訊。
class VideoPlayerActivity : ComponentActivity(){
override fun onStart() {
…
if (uxRestrictionsManager?
.currentCarUxRestrictions?
.isRequiresDistractionOptimization
== true) {
revertToCarAppTemplate()
}
}
private fun revertToCarAppTemplate() {
mediaControllerCompat
.sendCommand("CMD_CLEAR_VIDEO_SURFACE",
null, null)
finish()
}
}維持音訊不中斷播放是提供良好使用者體驗的關鍵,因此只要將媒體工作階段保持為前景服務,音訊就能在這些狀態變更期間持續播放。
<activity
android:name="androidx.car.app.activity.CarAppActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK" />
</intent-filter>
<meta-data
android:name="distractionOptimized"
android:value="true" />
</activity>您也應覆寫 CAL 畫面中的 onNewIntent,擷取 SHOW_MEDIA_PLAYBACK 意圖。
class MyCarAppSession : Session() {
override fun onCreateScreen(intent: Intent): Screen {
// 1. Link your active MediaSession token with Car App Library's MediaPlaybackManager
val mediaPlaybackManager = carContext.getCarService(MediaPlaybackManager::class.java)
mediaPlaybackManager.registerMediaPlaybackToken(mediaSessionToken)
// 2. Handle deep link if cold-started from the vehicle's media widget
handlePlaybackIntent(intent)
return HomeScreen(carContext)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// 3. Handle deep link when the session is already running
handlePlaybackIntent(intent)
}
}這樣一來,每當使用者與車輛主畫面上的車輛系統媒體小工具互動時,深層連結就會將他們直接導向 Car App Library 中的「現正播放」檢視畫面。
因此,在整個開發過程中驗證這些轉場效果非常簡單。
您可以在 AAOS 上執行 ADB 指令。
在本例中,當您準備好在活動之間切換時,會是 emulate-driving-state drive 或 emulate-driving-state park。
以下範例使用全新的 Media3 媒體範例應用程式,並搭配 Car App Library,在車輛從停車狀態切換至行駛狀態時播放影片。
歡迎前往樓下的 Android 自動調整式版面配置攤位,或在 GitHub 上的 car-samples 存放區查看。
Android Automotive OS 目前提供停車到駕駛功能,媒體應用程式只要整合車輛 CAL,就能提供駕駛體驗,並在停車時提供更強大的體驗。
請密切關注 developer.android.com,我們很快就會在說明文件中介紹這項功能。如果您想在應用程式中加入這項功能,請在該網站上表達意願。
Paul。
太好了!
謝謝,Thomas。
在結束之前,我想分享幾種方法,讓大家進一步瞭解今年稍晚推出的新功能。
正如 Thomas 所說,CAL 1.9 目前為 Alpha 版。
很快就會進入 Beta 版階段。
CAL 1.10 將在年底前進入 Alpha 測試階段,屆時您就能開始評估對話式 UI,以及最新的通訊和停車到駕駛功能。
最後,很高興能與大家分享,Android Auto 和 Android Automotive OS 現已支援遊戲,因此現在正是為車輛最佳化遊戲的最佳時機。
如要進一步瞭解實作細節,請務必參閱「打造車用遊戲」指南中的說明文件。
如要進一步瞭解如何調整遊戲以適用於車輛,並掌握 Android for Cars 的其他新功能和異動,請參閱「新功能」頁面。
希望您已瞭解車輛和使用者已準備好使用您的應用程式。
您可以在現有的自動調整式應用程式上,建構豐富的停車體驗,並使用各種車輛應用程式庫範本,打造安全駕駛體驗。
發布前,您可以使用電腦版車用運算主機、模擬器、在 Pixel Tablet 上執行的 AAOS,或 Firebase Test Lab 進行全面測試。
您已具備為這個生態系統建構應用程式的技能,而且有數百萬輛車等著使用您的應用程式,因此請立即著手開發。
android skills list
android skills add android-for-cars-parked-appsoverride fun onStart() {
super.onStart()
...
controllerFuture = MediaController.Builder(
this,
sessionToken
).buildAsync()
controllerFuture?.addListener({
mediaController = controllerFuture?.get()
playerView.player = mediaController
}, ContextCompat.getMainExecutor(this))
}val watchVideoAction = Action.Builder()
.setTitle("Watch Video")
.setIcon(videoCarIcon)
.setOnClickListener(
ParkedOnlyOnClickListener.create {
val intent = Intent(
carContext,
VideoPlayerActivity::class.java
)
.apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
)
}
carContext.startActivity(intent)
})
.build()<activity
android:name=".VideoPlayerActivity"
android:exported="false"
android:launchMode="singleTop">
<meta-data
android:name="distractionOptimized"
android:value="false" />
</activity>class VideoPlayerActivity : ComponentActivity(){
override fun onStart() {
…
if (uxRestrictionsManager?
.currentCarUxRestrictions?
.isRequiresDistractionOptimization
== true) {
revertToCarAppTemplate()
}
}
private fun revertToCarAppTemplate() {
mediaControllerCompat
.sendCommand("CMD_CLEAR_VIDEO_SURFACE",
null, null)
finish()
}
}<activity
android:name="androidx.car.app.activity.CarAppActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK" />
</intent-filter>
<meta-data
android:name="distractionOptimized"
android:value="true" />
</activity>class MyCarAppSession : Session() {
override fun onCreateScreen(intent: Intent): Screen {
// 1. Link your active MediaSession token with Car App Library's MediaPlaybackManager
val mediaPlaybackManager = carContext.getCarService(MediaPlaybackManager::class.java)
mediaPlaybackManager.registerMediaPlaybackToken(mediaSessionToken)
// 2. Handle deep link if cold-started from the vehicle's media widget
handlePlaybackIntent(intent)
return HomeScreen(carContext)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// 3. Handle deep link when the session is already running
handlePlaybackIntent(intent)
}
}