如何把應用搬上車

本文做者:陸康、陳馳枻、聶帥前端

當前造車新勢力愈來愈火,汽車智能化成爲風口,不少手機應用但願拓展車機場景,雲音樂及旗下 Look 直播也在車機端場景進行了一些探索,下面分享過程當中的一些總結和心得體會java

目前車載開發的類型和特色

當前車載接入方式主要有三種,第一種是以華爲 Hicar 爲表明的手機app擴展接入,第二種是提供對外的 OpenApi,車企自行研發應用進行接入,最後一種是最爲廣泛的車機獨立 app 接入。android

1. 手機 app 擴展接入,以華爲 HiCar 爲例

這種方式並不要求給車機提供獨立的車載版 apk,而是由手機端的應用接入 Hicar sdk,直接在原有的工程上開發。git

目前多家手機廠商採用的車聯方案都是基於 Android 系統自帶的 MediaSession 框架進行模板化開發,手機端的應用只須要根據廠商提供的模板準備數據,具體的UI展現由車機設備完成,開發者無需關心屏幕適配及UI風格統一的問題,具體的播控指令同步也是經過 MediaSession 框架完成的。github

該接入方式須要本身制定 Media Data Tree 的結構。由於 ViewTree 的展現是交給外部進行渲染的,咱們每每只能經過 onPlayFromMediaId 回調裏的 mediaId 和 extras 來獲取車機上點擊播放的媒體信息,mediaId能夠構形成例如 tab -> page -> listId -> songId 的層級關係,咱們就能夠知道播放的是具體來自哪一個頁面中哪一個歌單中的哪首歌了,這也是 Android 官方的 Universal Android Music Player Sample 中採用的實現方式。shell

這種車載接入方式有以下特色api

  • 接入方便,直接在原有工程基礎上開發,基礎能力是現成的,交付形式爲手機apk,與原來保持一致
  • 適配方便,例如 Hicar 針對不一樣類型的應用,直接提供了模板化開發的能力,音頻應用只需專一於音頻數據的準備和播放服務的實現便可,其它繁瑣的工做,例如繪製車機界面並保證各分辨率兼容性、管理音頻桌面卡片和實現音頻任務接續等都由 HiCar 完成
  • 更新方便,只須要手機上的應用更新了便可更新車機展現邏輯,相比更新車機應用,引導用戶的成本低很多
  • 適用範圍的侷限性,即只與特定平臺綁定,好比 Hicar 只支持華爲手機,而且要求車機接入了華爲 Hicar 系統,目前來講,國產的幾家主流手機廠商都在嘗試推相似的生態,汽車廠商在互聯網造車的勢頭中,也加快了這些系統的引入,但從總量來講,仍然屬於車機中的少部分

2. OpenAPI 接入

這種實現方式是服務端根據咱們的服務內容提供對應的 OpenAPI 接口。廠商能夠自行設計需求方案和視覺方案,根據不一樣的需求範圍去調用不一樣的接口來獲取數據並展現,可是最後通常須要經過咱們的審覈才能發佈。這種接入方式中的開發資源也是由廠商本身提供的,承載的平臺包括 Linux、Android 等多個系統環境。因爲這種方式不是本文的重點,就不在此贅訴了。性能優化

這種接入方式有如下特色markdown

  • 我方投入的人力成本小,主要開發成本集中在廠商那一邊
  • 能夠適配各類環境,並不侷限於某一種車機系統
  • 可控性較小,數據的獲取有一部分依賴於廠商提供,我方只能拿到接口調用次數,在涉及到結算的問題上容易產生分歧
  • 迭代困難,依賴於廠商自身的開發資源

3. 獨立 app 接入

能夠看出上述 OpenAPI 的實現方式仍是存在一些比較關鍵的問題,因此通常來講咱們會優先採用獨立 app 接入的方式,這是目前更爲廣泛的方式,也是本文主要描述的接入方式。這種方式與手機應用開發其實相似,但也有一些特色網絡

  • 車機系統的碎片化相好比今比較成熟的手機生態(絕大多數份額在頭部廠商)更加嚴重,不少廠商基於 Android 研發本身的車機系統,針對方控、桌面 widget、儀表顯示等設備依賴能力,廠商每每都會提供本身的一套接入 SDK,因此渠道分包勢在必行
  • 車機應用的交互要求簡潔,突出重點,應用支持語音操做對於用戶來講會是很大的吸引點
  • 測試車機設備比較缺少
  • 系統版本跨度較大,目前接觸到的設備從 Android 4.3 能夠一直覆蓋到 Android 10
  • 性能通常較爲羸弱,在開發時要格外注意性能的瓶頸

方案設計

針對上文中提到的車載獨立 app 開發的一些特色,咱們在渠道分包、解耦車機依賴、語音操做接入、分辨率適配等方面進行了一系列探索,下面介紹幾個相關的方案設計

1. 多渠道接入能力抽象

上面提到車機系統比較碎片化,要實現車機的方控、桌面 widget、儀表顯示等控制,通常有兩種狀況

  1. 廠商的相關操控實現了 Android 原生的 MediaSession 規範,這種狀況下咱們要響應相關的 KeyEvent,並在各類播放相關時機調用 MediaSession api 更新狀態
  2. 廠商爲相關操控提供了 sdk 接入,這種狀況下咱們要按照廠商自定義的規範來

考慮到上層業務代碼最好能不感知平臺差別,決定對渠道接入能力作一層封裝隔離 如上圖所示,將渠道依賴的能力抽象爲 EnvironmentDependency 接口,不一樣渠道依賴各自的車機 sdk 實現該接口,Mediasession 規範單獨實現一個通用類。業務層看到的是渠道無關的DependcyWrapper 代理實例,只需在各業務處理時機調用代理的對應方法便可,規避了業務層寫渠道相關的代碼。方控響應能力抽象爲 EventCallback 接口,業務實現後注入對應 dependcy 實例,由其適時觸發。 針對分渠道打包問題,採用 AGP 自帶的 productFlavors 方案,不一樣的渠道包含不一樣的源文件夾,隔離 sdk 依賴。

flavorDimensions "channel"
    productFlavors {
        //小鵬
        xp {
            dimension "channel"
            buildConfigField("String", "channel", ""xp"")
        }
        //比亞迪
        byd {
            dimension "channel"
            buildConfigField("String", "channel", ""byd"")
        }
       ......
    }
複製代碼

2. 語音控制的設計實現

要作語音控制,首先須要思考以下問題

  1. 是應用本身實現仍是使用車機能力?

從對接經驗來看,目前提供車機語音開放能力的廠商並不廣泛,個別廠商即便提供,其接入和自定義流程也比較複雜,須要至關長的週期,因此應用本身集成三方sdk來實現是更合理的選擇,可是針對於一些須要支持車機自帶語音助手的廠商咱們也要提供出對應的方案 2. 語音控制如何喚起?(除了頁面點擊外,可否提供其餘快捷入口) 若是要實現特定短句喚起語音助手,就要求語音識別 sdk 在應用生命週期內長期收音,一直搶佔着 mic 焦點,致使車機系統自帶的語音助手沒法工做(有個別車機實現了多麥克風陣列,即系統收音使用單獨 mic 通道,但這種車機是極少數),所以,短句喚起方案是行不通的。那麼,可否藉助方控呢?方控廣泛能提供確認鍵的響應,若是應用業務自己不須要確認鍵(如應用爲直播業務,不須要暫停、恢復)則可直接使用確認鍵喚起語音助手,若是須要,也能夠設計某種點按方式喚起(好比長按或者雙擊,這能夠經過在業務層判斷按鍵事件的時間間隔作到),固然,對應的引導也須要跟上,好比在用戶首次進入時展現浮層加語音的引導 3. 如何從語音識別出的文字映射到對應操做?最方便的作法確定是客戶端直接判斷文字匹配性,好比識別到「下一首」就切換到下一個直播,可是這種作法容錯性較低,用戶稍微調整下說法就會失效,更加合理的作法是在語音轉文字環節後再加上語義識別環節,流程以下

解決了這些基本問題後,再來考慮下一個比較完善的語音助手的完整交互流程,助手喚起後,會首先進入詢問態並提示語音支持的操做類型,接着用戶輸入,若是輸入超時會提示助手即將關閉,正常輸入後進行請求解析,獲取結果後某些操做執行會直接關閉面板,而某些操做將直接在面板展現結果並回到詢問態,若沒法解析則直接提示並回到詢問態,因而可知客戶端上整個流程比較適合抽象爲一個狀態機

  1. 若是須要對接不一樣的車機自帶的語音助手,涉及到控制相關的指令和播放信息的回調須要抽離出更爲廣泛的接口去實現,對於常見的指令,好比播放、暫停、上一首、下一首、收藏、搜索點播等須要封裝成獨立的方法,不一樣的車機的 app 註冊不一樣的 server,客戶端的實現則由同一個 client 處理,同時能將客戶端處理後的結果返回給 server 端進行展現,這樣作的好處是與車機對接的部分徹底交給 server 進行處理,client 只須要根據下發的指令進行對應的操做便可,前半部分是解耦的,後半部分是複用的

3. 多分辨率適配

前置的視覺交互設計中,考慮到駕駛時的場景,經常使用的操做區域要儘可能放在靠近駕駛側的一邊,同時交互流程要儘量簡單,頁面跳轉層級不宜過多。除去主流的橫屏佈局以外,比亞迪、小鵬等車機屏幕也會存在豎屏的狀況。 常見的屏幕適配方案包括 smallestWidth 適配、頭條的修改 DisplayMetrics#density 方案、使用百分比佈局等。結合項目的實際狀況,咱們建議大部分的佈局都採用流式佈局,只須要在佈局中改變 recyclerView 的方向就能夠適配橫豎屏的切換,同時卡片佈局儘可能扁平化,ConstraintLayout 中的 Guideline、layout_constraintHeight_percent 等屬性都能幫助咱們很方便的實現百分比佈局,若是遇到比例特別奇怪的屏幕,頁面又不能使用流式佈局時,能夠考慮結合 sw 限定符的方案,讓視覺同窗給出佈局調整策略,單獨針對少許特殊的屏幕進行適配。 在進行視覺適配開發時,咱們的第一反應固然是讓廠商提供全部可能涉及的車機設備,然而這是不現實的,從咱們的對接經驗來看,測試車機是至關緊缺的,部分廠商甚至連車機都暫時沒法提供,只提供文檔,讓咱們自行適配後再內部測試。在這種狀況下,咱們只能模擬不一樣的分辨率設備。adb shell wm size 命令就是解決方法,其接受 總長度像素值x總寬度像素值 格式的參數,運行後便可調整成對應的長寬比,測試過程只須要在同一設備上運行不一樣參數的命令便可實現不一樣分辨率的模擬。

性能優化

上面提到車機相比於手機,整體性能上要落後不少。在一開始,一方面因爲歷史包袱、組件複用等因素,另外一方面編寫代碼時也每每忽略了性能相關問題,使得 app 運行在車機上的體驗至關糟糕,安裝慢、啓動速度慢、卡頓丟幀等性能問題很明顯的就暴露了出來,因而咱們作了一系列針對性的優化

1. 減少包體積

減少包體積包括代碼和資源兩方面,一般的作法以下:

  • 圖片壓縮
  • 資源混淆
  • 減小 Dex 數量

2. 減小進程數

多進程運行須要佔用更多的系統資源,在性能較弱的設備上,單app多進程的運行方式會給設備 CPU、內存等帶來更多壓力

3. 減小線程數

和進程類似,線程過多在啓動中頻繁切換帶來了很大的開銷成本,主線程獲得執行的時間也會減小

4. IO優化

啓動過程當中文件 IO 過多也會拖慢啓動速度,儘可能減小沒必要要的文件讀寫

5. 減小Activity的跳轉次數

爲了更快地展現界面或者執行某項具體功能,最好減小啓動流程中 Activity 的跳轉層級,每多一個 Activity 就會增長几百毫秒的耗時;在請求一些接口時,也要考慮到請求時機,是否能夠前置並行請求,或者合併請求,減小接口的 RT

6. 優化佈局層次,減小過分繪製

下面分享一個性能優化的實例,在與某家車廠的合做過程當中,廠商反饋語音喚起階段從冷啓動到開始播放速度特別慢,將近 8s 之久。咱們在手機上測試是徹底沒有問題的,可是受限於車機的性能,在先後反覆數輪的溝通聯調下,咱們主要作了如下優化

  • 大幅減小包體積,刪除大量無用業務代碼,包體積減小約 80%,因包體積大幅減少,啓動過程須要解壓的dex數量也相應減小,加載的類變少,速度有數秒提高
  • 將播放進程合入主進程,多進程改成單進程,並去除 aidl,去除 aidl 通訊先後的幾回文件讀寫,減小約2s左右耗時
  • 將 LoadingActivity 和首頁 Activity 合併爲一個,減小啓動鏈路過程 activity 的數量,減小約數百毫秒耗時
  • 將多個接口合併成一個減小網絡請求,減小約 200 毫秒耗時

最終將時間壓縮到 3s 內,咱們的優化過程從前期對耗時明顯部分着重優化,效果明顯,到後期分析啓動日誌,一點點摳細節,最終經過廠商方面的驗收。在開始着手優化前,須要量化好具體指標,明確好目標再着手進行,用數據來衡量優化效果能讓優化過程更加順暢

踩坑指南

車載開發過程當中,還遇到了一些以前手機應用開發不常見的問題,印象深入,也在這裏分享下

1. 上了預裝,RN頁面咋都不行了

車載場景,用戶主動下載及更新 app 的頻率相對手機來講要低不少,因此預裝是很重要的鋪量手段,但當咱們好不容易與某渠道談成預裝後,卻發現一個奇怪的問題,全部用 RN 實現的頁面進入進入或者預加載就會引發應用的 crash,崩潰堆棧提示的直接緣由是 libjsexcutor.so 這個 RN 依賴的 js 解析庫加載失敗了,因而初步看了下 RN 崩潰位置的源碼,發現 RN 的 so 庫都是經過 SoLoader 這個 facebook 的工具加載的(官方文檔說主要用來兼容 4.3 如下版本的 so 加載依賴問題),而應用中其餘業務 so 的都是正常工做的,因此就猜想 SoLoader 在應用預裝場景會存在問題,因而復現並重點查看 Soloader 相關的日誌 上圖爲問題渠道上的 RN 加載日誌,而下圖爲正常場景下的 RN 加載日誌 能夠看到二者的區別就在於問題渠道上,標紅處的 so 查找路徑沒有被添加(該路徑實際就是應用安裝後的 so 路徑的軟連接),而正常渠道上是在該路徑上找到了 RN 相關的 so 並進行了加載,順着該思路查看了下 SoLoader 的源碼,發現有以下邏輯 即判斷當前應用是系統應用後,就不將 app 默認 so 路徑加入查找路徑,致使 RN 相關用 Soloader 加載的庫都會失敗,定位到緣由後,再仔細過了下 SoLoader 加載 so 相關源碼,發現其提供了 setSystemLoadLibraryWrapper 的設置接口,能夠由上層來定義針對系統應用場景如何加載依賴的 so,因此咱們只要設置該場景用應用本來的 so 加載方式便可解決問題,以下代碼所示

SoLoader.setSystemLoadLibraryWrapper {
    ReLinker.loadLibrary(context, it)
}
複製代碼

2. 車機測試設備上的奇怪問題

  1. 某個渠道的測試車機連上公司 wifi 後,始終沒法訪問網絡,與廠商溝通,他們告知也是首次提供測試車機給外部,內部使用是沒問題的,因而只能本身定位。考慮到大機率與網絡環境有關,遂用 iptables 工具查看車機網絡規則( iptables 是運行在用戶空間的應用軟件,經過控制 Linux 內核 netfilter 模塊,來管理網絡數據包的處理和轉發,數據包的詳細流轉流程以下圖所示,能夠在各個環節增長規則來攔截)

查看後果真發現部分規則比較特殊,猜想是測試車機原本是隻給廠商內部使用的,爲了防止流出後產生問題,對網絡環境作了識別,一旦發現非廠商公司內網就丟棄數據包,因而用以下命令清理規則,問題解決

iptables -F
iptables -X
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
複製代碼
  1. 某個渠道的車機,開發過程發現部分接口報錯。仔細看了下,發現報錯的接口都是 https 協議(開發階段還在測試環境,大部分接口是 http 協議),adb 日誌裏看到的報錯內容大體以下
javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: 
Could not validate certificate: Certificate not valid until Wed Dec 16 
09:00:05 GMT+08:00 2015 (compared to Sun Oct 12 16:20:03 GMT+08:00 1980)
複製代碼

看起來是時間和證書有效期對不上,查看系統時間發現確實不對,原來該車機每次啓動後都會重置系統時間,而 SSL 客戶端的校驗過程是包含證書有效期校驗的,調整系統時間後便可解決問題 上述可見,測試車機會由於一些特殊設定而帶來一些奇怪的開發問題,不過比較好的一點是這些測試車機每每是已經 root 過的,因此命令權限足夠大,能夠進行深刻地分析。

技術以外的體會

參與車載應用從啓動到正式上架的全過程,技術以外,還有一些其餘的體會

  • 車廠項目管理和互聯網產品有較大區別,其做風比較嚴謹細緻,求穩不求快,沒有互聯網快速迭代的理念,每每不太能接受部分問題先帶上線後續迭代 fix 的作法,因此其測試周期一般比較長,問題反饋輪次較多,反饋問題的角度也比較多樣(產品設計、內容運營、技術點),應用方須要有心理準備,耐心處理。
  • 在與車廠初步溝通時,就要對齊好交付標準,好比適配的需求範圍、應用的性能指標等等,避免由於交付標準的不統一形成來回的溝通和返工,根據咱們自身的項目狀況也要制定好本身的標準基線,平時經過 Monkey 和性能自動化測試保證 app 的穩定性
  • 目前各車廠接入 app 的總體流程還不能說很完善,存在文檔欠缺、測試車機欠缺、模擬器不穩定、反饋問題響應較慢等問題,這就要求應用方早作功課,對依賴項要儘早梳理,和廠商及時溝通,預知風險,後面隨着應用接入愈來愈廣泛,廠商這塊的建設應該會有改進。

小結

本文介紹了目前車載開發的一些現狀,分享了一些開發過程的設計思路和遇到的典型問題,但願能對你們的應用上車有所幫助!

本文發佈自 網易雲音樂大前端團隊,文章未經受權禁止任何形式的轉載。咱們常年招收前端、iOS、Android,若是你準備換工做,又剛好喜歡雲音樂,那就加入咱們 grp.music-fe(at)corp.netease.com!

相關文章
相關標籤/搜索