TSM - Rețeta unui HMI impecabil bazat pe AAOS

Ioana Deac - Software Architect @ P3 Romania

Pe măsură ce tehnologia avansează, așteptările utilizatorilor de vehicule moderne s-au schimbat dramatic. Nu mai este suficient ca un sistem de infotainment auto să ofere doar funcționalități de bază. În zilele noastre, interfețele HMI (Human-Machine Interface) trebuie să fie intuitive, rapide și să ofere o experiență de utilizare fluidă. Acesta este contextul în care Android Automotive OS (AAOS) joacă un rol esențial.

AAOS reprezintă sistemul de operare Android pentru vehicule și oferă dezvoltatorilor posibilitatea de a crea experiențe HMI personalizate și eficiente, unde producătorii pot integra funcții avansate precum navigația, controlul media și conectivitatea, funcții care prioritizează siguranța și ușurința în utilizare.

Un HMI impecabil pentru noi reprezintă o experiență optimă pentru utilizator, cât și pentru dezvoltator, concentrându-ne astfel pe două aspecte importante: performanța sistemului (aplicații pre-integrate/de platformă) și calitatea codului (arhitectura de bază).

În acest articol, vom explora cei mai ușori pași pe care îi putem face pentru un impact maximal asupra performanței și a experienței utilizatorului, atunci când vine vorba despre funcționalități HMI.

Performanța aplicațiilor pre-integrate sau de platformă

În cazul aplicațiilor pre-integrate, Google ne pune la dispoziție un plugin relativ nou care ne oferă o modalitate simplă pentru a optimiza fluxurile aplicațiilor, astfel încât utilizatorul să se poată bucura de o experiență cât mai fluidă.

Plugin-ul se numește Baseline Profile (profil de bază) și are un set predefinit de reguli, pe baza execuției codului pentru cazurile esențiale. Aceste fluxuri critice sunt fundamentale pentru utilizator, precum este cazul afișării programelor într-o aplicație radio.

Cum funcționează? Când o aplicație care are integrat un profil de bază este instalată pe dispozitiv, Android Runtime va folosi profilul de bază pentru a compila în avans (Ahead-of-Time compilation) clasele și metodele folosite în dezvoltarea codului ce acoperă fluxurile principale sau clasele și metodele ce vin cu dependințele aplicației.

În continuare, vom explora cum să configurăm, să generăm și să măsurăm timpii de execuție. E important să parcurgem secțiunea următoare cu un set de recomandări în minte.

TTFD (Time to Full Display) și TTID (Time to Interactive Display) sunt metrici esențiale pentru performanța aplicațiilor Android. Ideal, TTFD ar trebui să fie sub 2 secunde, pentru a oferi utilizatorului o interfață rapidă și complet afișată, iar TTID ar trebui să fie sub 3-4 secunde, pentru ca aplicația să fie interactivă cât mai repede. Aceste recomandări provin din cercetări de UX și practici din industrie, care subliniază importanța unui timp de răspuns rapid pentru a menține utilizatorii angajați și pentru a evita abandonul aplicației din cauza întârzierilor.

  1. Configurare pentru baseline profile

    1. Folosește un șablon. Din Android Studio, se poate selecta adăugarea unui nou modul de tip baseline profile, această opțiune fiind disponibilă începând cu Android Studio Iguana și Android Gradle Plugin (AGP) 8.2. Modulul și clasele de sub android test vor fi generate automat.

    2. Creează totul manual

      1. Creează un nou modul com.android.test—de exemplu, :baseline-profile.

      2. Configurează fișierul build.gradle.kts pentru :baseline-profile:

      3. Aplică plugin-ul androidx.baselineprofile.

      4. Asigură-te că targetProjectPath indică spre modulul :app.

      5. Opțional, adaugă un dispozitiv gestionat de Gradle.

      6. Aplică configurația dorită.

      7. Asigură-te că ai adăugat includeInStartupProfile = true în clasa generatorului tău de profil de bază. Acest lucru va activa, de asemenea, profilurile de startup.
  2. Generarea profilului

    1. Pentru mediul AAOS trebuie să comutăm la utilizatorul 0 înainte de a genera profilul. Altfel, pentru ceilalți utilizatori, vom obține erori de permisiune. Se poate folosi comanda: adb shell am switch-user 0.

    2. Pentru a rula generatorul de profil din IDE, fă click dreapta pe clasa BaselineProfileGenerator și selectează 'Run BaselineProfileGenerator`.

    3. Pentru a rula generatorul de profil din linia de comandă: ./gradlew :modules:yourappmodule:generateVariantBaselineProfile.

    4. În urma rulării cu succes, un fișier numit baseline-prof.txt va fi adăugat la path-ul varianta_build/generated/baselineprofiles/, unde varianta_build poate fi debug, release etc.
  3. Măsurarea timpilor de execuție. Într-un context AOSP, este important să măsurăm timpul de start-up al aplicației, astfel încât să nu îngreunăm navigarea utilizatorului de la o componentă la alta. Clasa de tip AndroidJUnit, numită default BaselineProfileGenerator, va măsura automat timpii de execuție pentru flow-ul targetat, astfel încât vom avea o valoare măsurată înainte de a aplica baseline profile și o valoare măsurată după ce am aplicat baseline profile.

Trecem la aplicațiile de platformă, integrate cu Soong și fișiere blueprint (.bp), pentru care Perfetto este recomandat în special pentru măsurarea performanței. Perfetto, un instrument esențial în AOSP, ajută la identificarea rapidă a problemelor de performanță și ne oferă o interfață prietenoasă unde să putem inspecta resursele folosite de aplicația noastră.

  1. Configurare pentru Perfetto

    1. Asigură-te că ai instalat Perfetto și că acesta este configurat corect în mediul tău de dezvoltare. Perfetto vine integrat în AOSP, dar este important să ai o versiune recentă pentru cele mai noi funcționalități.

    2. Pornește un dispozitiv emulator sau fizic cu o versiune compatibilă de Android (în mod ideal, una dintre cele mai recente pentru a evita incompatibilități).

    3. Configurează fișierul trace_config.pbtxt pentru a specifica evenimentele pe care vrei să le monitorizezi (de exemplu, CPU, memorie, activități HMI).

    4. Fișierul trebuie să fie localizat în directorul proiectului sau într-o locație accesibilă prin ADB.
  2. Rularea Perfetto

    1. Deschide terminalul și conectează-te la dispozitiv folosind adb (Android Debug Bridge).

    2. Rulează următoarea comandă pentru a porni captura datelor: adb shell perfetto -c /data/local/tmp/trace_config.pbtxt -o /data/local/tmp/trace_file.perfetto-trace. -c specifică fișierul de configurare, iar -o indică locația unde va fi salvat fișierul cu rezultatele capturate.

    3. După ce rularea a fost completă și ai capturat suficiente date, oprește procesul și transferă fișierul pe computer: adb pull /data/local/tmp/trace_file.perfetto-trace.
  3. Analiza datelor capturate

    1. Deschide Perfetto UI în browser și încarcă fișierul capturat (trace_file.perfetto-trace).

    2. Analizează secțiunile specifice (de exemplu, timpii de răspuns ai interfeței HMI, utilizarea CPU și memorie) pentru a identifica potențialele optimizări.

    3. Poți folosi filtrele și vizualizările oferite de Perfetto pentru a investiga în detaliu comportamentul aplicației.

Arhitectura Clean într-o aplicație Android Automotive

Arhitectura Clean se bazează pe separarea responsabilităților în straturi distincte, fiecare având propriul model de date. Într-o aplicație Android Automotive, arhitectura poate fi organizată în următoarele straturi:

1) Stratul de Prezentare

Acest strat este responsabil pentru interacțiunea utilizatorului cu aplicația. Este compus din activități, fragmente sau componente specifice sistemului automotive, cum ar fi serviciile care gestionează interfața cu utilizatorul. Stratul de prezentare nu ar trebui să conțină logică de business, ci să se bazeze pe cazurile de utilizare pentru a obține datele necesare. Stratul de prezentare se ocupă strict de ceea ce ține de interfața grafică sau decomponentele de acces principale ale sistemului de operare Android. Stratul de prezentare permite testarea prin component tests, cu librării specifice simulării concrete a interacțiunii, precum Espresso, UI Automator, Apium sau Kakao pentru Kotlin.

2) Stratul de Cazuri de Utilizare (Use Case)

Acest strat include logica specifică aplicației și definește ce trebuie să se întâmple ca răspuns la acțiunile utilizatorului. De exemplu, într-o aplicație Android Automotive, cazurile de utilizare pot gestiona scenarii precum reglarea volumului sau gestionarea unei sesiuni de navigație. Acest strat nu depinde de interfața utilizator sau de metoda de stocare a datelor, oferind o flexibilitate mare. Stratul de use case permite încadrarea în întregime a logicii de business, putând fi testată prin unit tests bucată cu bucată, indiferent de aplicația în cadrul căreia sunt scrise aceste logici de business sau de sursele de date puse la dispoziție.

3) Stratul de Date

Stratul de date gestionează sursa efectivă a datelor, fie că acestea provin de la API-uri externe, baze de date locale sau senzorii interni ai mașinii. Acest strat implementează concret contractele definite de cazurile de utilizare și de entități. Stratul de date permite accesarea surselor de date fără a ține cont de ce cazuri de utilizare sunt folosite și, cu atât mai mult, fără a necesita cunoștințe despre felul prezentărilor datelor către utilizator. Acest strat poate fi testat atât prin component tests, cât și prin unit tests, însă, de cele mai multe ori, nu este nevoie, deoarece stratul doar preia date din surse deja testate de către dezvoltatorii surselor de date.

Beneficii și provocări ale utilizării arhitecturii Clean în Android Automotive

Beneficii:

  1. Separarea clară a responsabilităților: Fiecare strat are o responsabilitate bine definită, ușor de înțeles și de întreținut.

  2. Flexibilitate și extensibilitate: Arhitectura permite adăugarea de noi funcționalități fără a afecta straturile deja existente.

  3. Testabilitate crescută: Datorită separării stricte a straturilor, este ușor să testezi fiecare componentă în mod izolat. De exemplu, fiecare dezvoltator specializat pe o anumită funcționalitate își poate testa independent munca sau poate chiar ruga testerii să o facă într-un mod ce nu necesită deloc cunoștințe despre tehnologiile sau funcționalitățile din alte straturi, respectiv componente / cerințe funcționale.

  4. Independență față de detalii: Straturile nu depind de detaliile tehnice ale altor straturi. În Android Automotive, acest lucru este util pentru a izola codul specific platformei de logica de business și face posibilă prelucrarea punctuală în cazul în care versiunea sistemului de operare Android se schimbă, dar, în mare parte, face posibilă preluarea în întregime a aplicațiilor sau a unor straturi ale acesteia și integrarea lor în noul sistem sau în alte aplicații.

Provocări:

  1. Complexitatea inițială: Aplicarea principiilor Clean Architecture necesită o înțelegere profundă a acestei abordări și poate adăuga complexitate suplimentară la început. În special pentru aplicațiile Android Automotive, care deja au o serie de constrângeri tehnice, această complexitate poate deveni o provocare. Complexitatea survine din înțelegerea profundă a tuturor cerințelor funcționale și a interfațării minime necesare între componente, satisfăcând cerințele non-funcționale, precum utilizabilitatea, extensibilitatea, mentenabilitatea, etc.

  2. Gestionarea dependințelor de platformă: Automotive include componente și servicii specifice care pot fi dificil de abstractizat. De exemplu, integrarea cu sistemele vehiculului poate introduce interdependințe greu de izolat în anumite straturi. De asemenea, fluxul de date trebuie să fie analizat cu atenție pentru a satisface corect cerințele de securitate.

  3. Performanță: Deși Clean Architecture asigură flexibilitate și modularitate, poate introduce un overhead în ceea ce privește performanța aplicației, în special atunci când se comunică frecvent între straturi. Acest lucru este esențial în aplicațiile Android Automotive care necesită răspunsuri rapide pentru interacțiuni în timp real, o înțelegere a gestionării memoriei, a constrângerilor privitoare la procesele și a user-ului pe care rulează ele, precum u0 de sistem sau uXX (ex: u10) de client.

În concluzie, crearea unui HMI impecabil bazat pe AAOS necesită o atenție deosebită asupra performanței aplicațiilor și a calității codului. Utilizarea unor instrumente precum Baseline Profiles și Perfetto permite optimizarea experienței utilizatorului prin timpi de răspuns rapizi și o interfață fluidă. Arhitectura Clean oferă o separare clară a responsabilităților și flexibilitate în dezvoltare. Astfel, aplicarea acestor bune practici ajută la dezvoltarea unor interfețe performante și eficiente, esențiale pentru experiența utilizatorului.