This content originally appeared on DEV Community and was authored by linzhongxue
Hands-on Development of Smart Connected Car Applications Based on HarmonyOS Next
Opening: When HarmonyOS Meets Smart Vehicles
In the era of ubiquitous connectivity, automobiles have long transcended their role as mere transportation tools. As a developer with years of experience in vehicle connectivity, I’ve had the privilege to participate in multiple HarmonyOS in-car application projects. Today, through a complete in-car music application case study, I’ll demonstrate how to rapidly build professional-grade automotive applications using AppGallery Connect.
1. Fundamental Preparations for In-Car Application Development
1.1 Special Configuration of Development Environment
In-car application development requires special attention to the following configurations:
- Install the “Automotive” extension package in DevEco Studio
- Apply for in-car development permissions (requires enterprise developer account)
- Select automotive resolution (typically 1920×720) when configuring the emulator
// In-car environment detection tool
function checkAutomotiveEnvironment() {
try {
const systemInfo = device.getSystemInfoSync();
if (!systemInfo.isAutomotive) {
console.warn("Not in automotive environment, some APIs may be restricted");
return false;
}
console.log("Automotive system version:", systemInfo.osVersion);
return true;
} catch (e) {
console.error("Environment detection exception:", e);
return false;
}
}
1.2 In-Car UI Design Specifications
In-car applications must follow special interaction principles:
- Button size no smaller than 60px
- Text size no smaller than 24px
- Operation hierarchy no more than 3 levels
- Full-screen popups are prohibited
2. Developing an In-Car Music Player
2.1 Audio Service Integration
// Audio service wrapper class
class AudioService {
private audioSession: audio.AudioSession;
private player: audio.AudioPlayer;
constructor() {
this.initAudioSession();
}
private initAudioSession() {
// Create dedicated automotive audio session
this.audioSession = audio.createAudioSession({
usage: audio.StreamUsage.MUSIC,
device: audio.CommunicationDevice.CAR_AUDIO
});
// Configure audio focus policy
this.audioSession.setInterruptionMode(
audio.InterruptionMode.SHAREABLE
);
}
// Initialize player
async initPlayer(source: string) {
this.player = await this.audioSession.createPlayer({
source: {
uri: source
},
loop: false
});
// Set automotive audio properties
await this.player.setAudioProperties({
speed: 1.0,
pitch: 1.0,
fadeTime: 500
});
}
// Playback control
async play() {
try {
await this.player.play();
console.log("Playback started");
} catch (error) {
console.error("Playback failed:", error);
}
}
// Pause playback
async pause() {
await this.player.pause();
}
// Switch audio source
async switchSource(newSource: string) {
await this.player.reset();
await this.player.setSource({ uri: newSource });
await this.play();
}
}
2.2 Music List Component
// Dedicated in-car music list component
@Component
struct MusicList {
@State songs: Song[] = [];
@State currentIndex: number = -1;
// Column width adapts to in-car display
@StorageProp('displayMode') displayMode: string = 'single';
build() {
Grid() {
ForEach(this.songs, (song, index) => {
GridItem() {
Column() {
Image(song.cover)
.width(120)
.height(120)
.borderRadius(8)
Text(song.name)
.fontSize(24)
.margin({ top: 8 })
Text(song.artist)
.fontSize(18)
.opacity(0.8)
}
.padding(12)
.backgroundColor(
this.currentIndex === index ?
'#333333' : 'transparent'
)
.onClick(() => {
this.playSelected(index);
})
}
.colSpan(this.displayMode === 'single' ? 2 : 1)
})
}
.columnsTemplate(
this.displayMode === 'single' ?
'1fr' : '1fr 1fr'
)
.onAppear(() => {
this.loadMusicList();
})
}
private async loadMusicList() {
try {
const response = await fetch(
'https://api.example.com/car-music'
);
this.songs = await response.json();
} catch (error) {
console.error("Failed to fetch music list:", error);
}
}
private playSelected(index: number) {
this.currentIndex = index;
const audioService = AudioService.getInstance();
audioService.switchSource(this.songs[index].url);
}
}
3. Vehicle Data Interaction Module
3.1 Vehicle Status Subscription
// Vehicle data service
class VehicleDataService {
private static instance: VehicleDataService;
private vehicleApi: any;
private constructor() {
this.initVehicleApi();
}
public static getInstance(): VehicleDataService {
if (!VehicleDataService.instance) {
VehicleDataService.instance = new VehicleDataService();
}
return VehicleDataService.instance;
}
private initVehicleApi() {
try {
this.vehicleApi = require('@ohos.vehicle');
} catch (error) {
console.error("Vehicle API unavailable:", error);
}
}
// Get current speed
getCurrentSpeed(): Promise<number> {
return new Promise((resolve) => {
if (!this.vehicleApi) {
resolve(0);
return;
}
this.vehicleApi.getData(
'speed',
(err, data) => {
if (!err) {
resolve(data.value);
} else {
resolve(0);
}
}
);
});
}
// Subscribe to vehicle data changes
subscribe(dataType: string, callback: Function) {
if (!this.vehicleApi) return;
this.vehicleApi.subscribe(
dataType,
(err, data) => {
if (!err) {
callback(data.value);
}
}
);
}
}
3.2 Driving Mode Adaptation
// Driving mode awareness component
@Component
struct DrivingModeAware {
@State drivingMode: string = 'normal';
private vehicleService = VehicleDataService.getInstance();
build() {
Column() {
// Display different UI based on driving mode
if (this.drivingMode === 'sport') {
SportModeView()
} else if (this.drivingMode === 'night') {
NightModeView()
} else {
NormalModeView()
}
}
.onAppear(() => {
this.setupDrivingModeListener();
})
}
private setupDrivingModeListener() {
this.vehicleService.subscribe(
'driving_mode',
(mode: string) => {
this.drivingMode = mode;
}
);
}
}
4. Cloud Synchronization and User Preferences
4.1 User Configuration Synchronization
// User preference management
class UserPreference {
private cloudDB: cloud.CloudDBZone;
constructor() {
this.initCloudDB();
}
private async initCloudDB() {
const config = {
name: 'CarUserPrefs',
persistenceEnabled: true,
encryptionKey: 'user_specific_key'
};
this.cloudDB = await cloud.CloudDBZoneManager
.getInstance()
.openCloudDBZone(config);
}
// Save preference settings
async savePreference(key: string, value: any) {
const pref = {
userId: getCurrentUserId(),
prefKey: key,
prefValue: JSON.stringify(value),
updateTime: new Date().getTime()
};
await this.cloudDB.executeUpsert([pref]);
}
// Retrieve preference settings
async getPreference(key: string): Promise<any> {
const query = cloud.CloudDBZoneQuery
.where('UserPreference')
.equalTo('userId', getCurrentUserId())
.equalTo('prefKey', key)
.limit(1);
const snapshot = await this.cloudDB.executeQuery(query);
if (snapshot.hasNext()) {
const record = snapshot.next();
return JSON.parse(record.prefValue);
}
return null;
}
}
4.2 Multi-Device Synchronization Solution
// Cross-device synchronization controller
class SyncController {
private agcAuth = require('@hw-agconnect/auth-ohos');
private agcCloud = require('@hw-agconnect/cloud-ohos');
// Initialize synchronization channel
async initSyncChannel() {
const user = await this.agcAuth.getInstance()
.getCurrentUser();
if (!user) return false;
const config = {
syncPolicy: 'auto',
conflictHandler: this.resolveConflict
};
await this.agcCloud.getInstance()
.enableDataSync(config);
return true;
}
// Conflict resolution strategy
private resolveConflict(localData, serverData) {
// Use data with latest timestamp
return localData.updateTime > serverData.updateTime ?
localData : serverData;
}
// Trigger manual synchronization
async triggerManualSync() {
try {
await this.agcCloud.getInstance()
.executeSync();
return true;
} catch (error) {
console.error("Synchronization failed:", error);
return false;
}
}
}
5. Performance Optimization for In-Car Applications
5.1 Memory Management Techniques
// Resource monitoring component
@Component
struct ResourceMonitor {
@State memoryUsage: number = 0;
@State cpuUsage: number = 0;
private timer: number = 0;
build() {
Column() {
Text(`Memory usage: ${this.memoryUsage.toFixed(1)}MB`)
Text(`CPU usage: ${this.cpuUsage.toFixed(1)}%`)
}
.onAppear(() => {
this.startMonitoring();
})
.onDisappear(() => {
this.stopMonitoring();
})
}
private startMonitoring() {
this.timer = setInterval(() => {
this.updateMetrics();
}, 2000);
}
private stopMonitoring() {
clearInterval(this.timer);
}
private async updateMetrics() {
const stats = await performance.getMetrics();
this.memoryUsage = stats.memory / 1024 / 1024;
this.cpuUsage = stats.cpu * 100;
}
}
5.2 Efficient Rendering Strategies
// Optimized list rendering
@Component
struct OptimizedList {
@State data: any[] = [];
private visibleRange: number[] = [0, 10];
build() {
List() {
LazyForEach(this.data.slice(
this.visibleRange[0],
this.visibleRange[1]
), (item) => {
ListItem() {
ListItemContent(item)
}
})
}
.onScroll((offset: number) => {
this.updateVisibleRange(offset);
})
}
private updateVisibleRange(offset: number) {
const start = Math.floor(offset / 100);
this.visibleRange = [start, start + 10];
}
}
6. Testing and Release Process
6.1 Specialized Testing for In-Car Applications
- Driver distraction testing: Ensure the app doesn’t interfere with normal driving
- Extreme environment testing: Operational stability under high/low temperatures
- Long-duration testing: 24-hour continuous operation to check for memory leaks
- Voice interaction testing: Compatibility with in-car voice systems
6.2 Application Release Checklist
- Pass Huawei’s in-car application certification
- Complete all required metadata information
- Provide screenshots in at least 3 automotive resolutions
- Declare supported vehicle models
- Prepare automotive-specific application descriptions
Conclusion: Accelerating into the HarmonyOS Automotive Ecosystem
Developing this in-car music application has given me profound appreciation for HarmonyOS’ unique advantages in the automotive field. Particularly its distributed capabilities and hardware collaboration features bring entirely new possibilities to in-car scenarios.
I recommend developers stay updated on Huawei’s intelligent automotive solutions. The automotive UI component library I’ve accumulated during this project is now open-source—search for “harmony-auto-ui” in the open-source community. Looking forward to seeing more amazing applications in the HarmonyOS automotive ecosystem!
This content originally appeared on DEV Community and was authored by linzhongxue