在万物互联时代,华为鸿蒙系统(HarmonyOS)凭借其分布式架构、一次开发多端部署的特性,正成为开发者新的蓝海。据统计,纯生鸿蒙生态设备数量已突破两千多万台,开发者年薪普遍比Android/iOS开发者高出20%-30%!
本文将带你从零开始,全面掌握鸿蒙应用开发,并附上可运行的实战案例。
DevEco Studio 是官方推荐的IDE,基于IntelliJ IDEA架构。
# 下载地址:https://developer.harmonyos.com/cn/develop/deveco-studio
# 系统要求:
# - Windows 10 64位
# - 8GB RAM(推荐16GB)
# - 至少10GB可用空间
配置步骤:
安装Node.js(v14.0.0+)安装Ohpm包管理器配置HarmonyOS SDK
// 项目结构说明
MyFirstApp/
├── entry/ # 主模块
│ ├── src/
│ │ ├── main/
│ │ │ ├── ets/ # ArkTS代码
│ │ │ │ ├── entryability/
│ │ │ │ ├── pages/ # 页面文件
│ │ │ │ └── utils/ # 工具类
│ │ │ ├── resources/ # 资源文件
│ │ │ └── module.json5 # 模块配置
│ └── build-profile.json5
└── build-profile.json5
超级终端模拟器支持多设备协同调试:
# 开启开发者模式
设置 > 关于手机 > 连续点击版本号7次
# 启用USB调试
设置 > 系统和更新 > 开发人员选项 > USB调试
鸿蒙应用由多个Ability组成,分为两种类型:
FA(Feature Ability) - 有UI界面的Ability
PA(Particle Ability) - 无UI界面的Ability
// EntryAbility.ts - 应用入口
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.log('EntryAbility onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
console.log('EntryAbility onWindowStageCreate');
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
console.error('Failed to load content. Error:' + JSON.stringify(err));
return;
}
console.info('Succeeded in loading content. Data: ' + JSON.stringify(data));
});
}
}
// module.json5
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet",
"tv",
"wearable"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
]
}
}
传统命令式UI:
// Android/传统方式
TextView textView = findViewById(R.id.text_view);
textView.setText("Hello World");
button.setOnClickListener(v -> {
textView.setText("Button Clicked");
});
鸿蒙声明式UI:
// ArkTS声明式
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button('点击我')
.onClick(() => {
this.message = '按钮被点击了!'
})
}
.width('100%')
.height('100%')
}
}
@Component
struct DecoratorExample {
@State count: number = 0 // 组件内部状态
@Link @Watch('onCountChange') linkedCount: number // 双向绑定
@Prop propValue: string // 单向绑定
@Provide('theme') theme: string = 'light' // 祖先提供数据
@Consume('theme') consumeTheme: string // 子孙消费数据
@StorageLink('StorageKey') storageValue: string // 持久化存储
onCountChange(propName: string): void {
console.log(`Count changed to: ${this.count}`);
}
build() {
Column() {
Text(`计数: ${this.count}`)
Button('增加')
.onClick(() => {
this.count++
})
}
}
}
@Entry
@Component
struct LayoutExample {
@State currentTab: number = 0
build() {
Column() {
// 顶部导航
Row() {
Button('首页')
.backgroundColor(this.currentTab === 0 ? '#007DFF' : '#F1F3F5')
.onClick(() => this.currentTab = 0)
Button('发现')
.backgroundColor(this.currentTab === 1 ? '#007DFF' : '#F1F3F5')
.onClick(() => this.currentTab = 1)
Button('我的')
.backgroundColor(this.currentTab === 2 ? '#007DFF' : '#F1F3F5')
.onClick(() => this.currentTab = 2)
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.padding(20)
// 内容区域
Stack() {
if (this.currentTab === 0) {
this.HomeTab()
} else if (this.currentTab === 1) {
this.DiscoverTab()
} else {
this.ProfileTab()
}
}
.layoutWeight(1) // 权重布局
.width('100%')
}
.height('100%')
.backgroundColor('#FFFFFF')
}
@Builder HomeTab() {
Column() {
// 网格布局
Grid() {
ForEach(Array.from({length: 6}), (item: number, index: number) => {
GridItem() {
Column() {
Image($r('app.media.icon'))
.width(50)
.height(50)
Text(`功能${index + 1}`)
.fontSize(14)
.margin({top: 8})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height(100)
.backgroundColor('#F7F8FA')
.borderRadius(16)
}
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(12)
.rowsGap(12)
.padding(20)
}
}
@Builder DiscoverTab() {
Scroll() {
Column() {
// 列表布局
ForEach(Array.from({length: 20}), (item: number, index: number) => {
ListItem() {
Row() {
Image($r('app.media.icon'))
.width(40)
.height(40)
.borderRadius(20)
Column() {
Text(`推荐内容 ${index + 1}`)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text('这是内容描述...')
.fontSize(14)
.opacity(0.6)
}
.layoutWeight(1)
.margin({left: 12})
Text('1小时前')
.fontSize(12)
.opacity(0.5)
}
.padding(16)
.borderRadius(12)
.backgroundColor('#FFFFFF')
.shadow({radius: 8, color: '#1A000000', offsetX: 0, offsetY: 2})
}
.margin({top: 8, left: 16, right: 16})
})
}
}
}
@Builder ProfileTab() {
Column() {
// 居中头像
Column() {
Image($r('app.media.avatar'))
.width(80)
.height(80)
.borderRadius(40)
.border({width: 3, color: '#007DFF'})
Text('开发者')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({top: 12})
Text('鸿蒙开发专家')
.fontSize(14)
.opacity(0.6)
.margin({top: 4})
}
.alignItems(HorizontalAlign.Center)
.width('100%')
.padding(40)
// 设置项列表
Column() {
this.SettingItem('账号设置', '➜')
this.SettingItem('通知设置', '➜')
this.SettingItem隐私设置', '➜')
this.SettingItem('关于我们', '➜')
}
.width('100%')
.padding(16)
}
}
@Builder SettingItem(title: string, value: string) {
Row() {
Text(title)
.fontSize(16)
Text(value)
.fontSize(14)
.opacity(0.5)
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.padding(20)
.backgroundColor('#F7F8FA')
.borderRadius(12)
.margin({bottom: 12})
.onClick(() => {
// 点击事件处理
})
}
}
WeatherApp/
├── entry/
│ ├── src/
│ │ ├── main/
│ │ │ ├── ets/
│ │ │ │ ├── entryability/
│ │ │ │ ├── pages/
│ │ │ │ │ ├── Index.ets # 主页
│ │ │ │ │ └── CityList.ets # 城市列表
│ │ │ │ ├── model/
│ │ │ │ │ ├── WeatherData.ets # 数据模型
│ │ │ │ │ └── ApiService.ets # 网络请求
│ │ │ │ ├── utils/
│ │ │ │ │ └── CommonUtils.ets # 工具类
│ │ │ │ └── components/
│ │ │ │ ├── WeatherCard.ets # 天气卡片组件
│ │ │ │ └── ForecastItem.ets # 预报项组件
// model/WeatherData.ets
export interface Location {
name: string;
lat: number;
lon: number;
}
export interface CurrentWeather {
temp: number;
feels_like: number;
humidity: number;
pressure: number;
wind_speed: number;
weather: WeatherCondition[];
}
export interface WeatherCondition {
main: string;
description: string;
icon: string;
}
export interface DailyForecast {
dt: number;
temp: {
day: number;
min: number;
max: number;
};
weather: WeatherCondition[];
}
export interface WeatherResponse {
current: CurrentWeather;
daily: DailyForecast[];
}
// model/ApiService.ets
import http from '@ohos.net.http';
import { WeatherResponse } from './WeatherData';
const API_KEY = 'your_api_key_here';
const BASE_URL = 'https://api.openweathermap.org/data/2.5';
export class WeatherService {
private httpRequest: http.HttpRequest = http.createHttp();
async getWeatherByLocation(lat: number, lon: number): Promise<WeatherResponse> {
try {
const url = `${BASE_URL}/onecall?lat=${lat}&lon=${lon}&exclude=minutely,hourly,alerts&appid=${API_KEY}&units=metric&lang=zh_cn`;
let promise = this.httpRequest.request(
url,
{
method: http.RequestMethod.GET,
connectTimeout: 60000,
readTimeout: 60000,
}
);
const response = await promise;
if (response.responseCode === 200) {
const result = JSON.parse(response.result as string) as WeatherResponse;
return result;
} else {
throw new Error(`HTTP Error: ${response.responseCode}`);
}
} catch (error) {
console.error('Weather API Error:', error);
throw error;
}
}
async searchCities(cityName: string): Promise<Location[]> {
// 实现城市搜索逻辑
return [];
}
}
// pages/Index.ets
import { WeatherService } from '../model/ApiService';
import { WeatherResponse, DailyForecast } from '../model/WeatherData';
import { WeatherCard } from '../components/WeatherCard';
import { ForecastItem } from '../components/ForecastItem';
@Entry
@Component
struct WeatherIndex {
@State weatherData: WeatherResponse | null = null;
@State isLoading: boolean = true;
@State errorMessage: string = '';
private weatherService: WeatherService = new WeatherService();
private currentLocation: string = '北京市';
aboutToAppear() {
this.loadWeatherData(39.9042, 116.4074); // 北京坐标
}
async loadWeatherData(lat: number, lon: number) {
try {
this.isLoading = true;
this.weatherData = await this.weatherService.getWeatherByLocation(lat, lon);
this.errorMessage = '';
} catch (error) {
this.errorMessage = '获取天气数据失败,请检查网络连接';
console.error('加载天气数据失败:', error);
} finally {
this.isLoading = false;
}
}
build() {
Column() {
// 顶部栏
Row() {
Text(this.currentLocation)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Blank()
Image($r('app.media.ic_search'))
.width(24)
.height(24)
.onClick(() => {
// 跳转到城市选择页面
})
}
.width('100%')
.padding({ left: 20, right: 20, top: 10 })
if (this.isLoading) {
// 加载状态
Column() {
LoadingProgress()
.width(50)
.height(50)
Text('加载中...')
.fontSize(16)
.margin({ top: 16 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
} else if (this.errorMessage) {
// 错误状态
Column() {
Image($r('app.media.ic_error'))
.width(80)
.height(80)
Text(this.errorMessage)
.fontSize(16)
.margin({ top: 16 })
Button('重试')
.margin({ top: 20 })
.onClick(() => this.loadWeatherData(39.9042, 116.4074))
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
} else if (this.weatherData) {
// 正常显示
Scroll() {
Column() {
// 当前天气卡片
WeatherCard({
current: this.weatherData.current,
location: this.currentLocation
})
// 24小时预报
Text('24小时预报')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 24, left: 20, bottom: 12 })
.alignSelf(ItemAlign.Start)
// 每日预报
Text('7天预报')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 24, left: 20, bottom: 12 })
.alignSelf(ItemAlign.Start)
Column() {
ForEach(this.weatherData.daily.slice(0, 7), (day: DailyForecast, index: number) => {
ForecastItem({ forecast: day, isToday: index === 0 })
})
}
.width('100%')
.padding({ left: 20, right: 20 })
}
}
.scrollable(ScrollDirection.Vertical)
}
}
.width('100%')
.height('100%')
.backgroundColor('#F0F4F8')
}
}
// components/WeatherCard.ets
@Component
export struct WeatherCard {
private current: CurrentWeather;
private location: string;
build() {
Column() {
Text(this.location)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text(`${Math.round(this.current.temp)}°`)
.fontSize(64)
.fontWeight(FontWeight.Light)
.fontColor('#FFFFFF')
.margin({ top: 8 })
Text(this.current.weather[0].description)
.fontSize(18)
.fontColor('#FFFFFF')
.margin({ top: 4 })
Row() {
Column() {
Text('体感温度')
.fontSize(12)
.fontColor('#E6FFFFFF')
Text(`${Math.round(this.current.feels_like)}°`)
.fontSize(16)
.fontColor('#FFFFFF')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center)
Column() {
Text('湿度')
.fontSize(12)
.fontColor('#E6FFFFFF')
Text(`${this.current.humidity}%`)
.fontSize(16)
.fontColor('#FFFFFF')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center)
.margin({ left: 32 })
Column() {
Text('风速')
.fontSize(12)
.fontColor('#E6FFFFFF')
Text(`${this.current.wind_speed} m/s`)
.fontSize(16)
.fontColor('#FFFFFF')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center)
.margin({ left: 32 })
}
.justifyContent(FlexAlign.SpaceAround)
.margin({ top: 32 })
}
.padding(24)
.width('90%')
.backgroundImage($r('app.media.weather_bg'))
.backgroundImageSize(ImageSize.Cover)
.borderRadius(24)
.margin({ top: 20 })
}
}
// 分布式任务迁移
import distributedObject from '@ohos.data.distributedDataObject';
class DistributedUtils {
private sourceRemote: distributedObject.DataObjectRemote | null = null;
// 发起迁移
async continueAbility(deviceId: string): Promise<void> {
try {
let want = {
deviceId: deviceId,
bundleName: 'com.example.weatherapp',
abilityName: 'EntryAbility',
parameters: {
'weatherData': this.weatherData
}
};
await globalThis.abilityContext.continueAbility(want);
} catch (error) {
console.error('迁移失败:', error);
}
}
// 分布式数据对象
createDistributedObject(obj: Object): distributedObject.DataObject {
return distributedObject.createDistributedObject(obj);
}
}
// form_config.json
{
"forms": [
{
"name": "weather_widget",
"description": "天气服务卡片",
"src": "./ets/widget/WeatherWidget.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 360,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": ["2*2", "2*4", "4*4"]
}
]
}
// 使用LazyForEach优化长列表
@Component
struct OptimizedList {
@State data: Array<string> = Array.from({length: 1000}, (_, i) => `Item ${i}`);
build() {
List() {
LazyForEach(this.data, (item: string, index: number) => {
ListItem() {
Text(item)
.fontSize(16)
.padding(12)
}
}, (item: string) => item)
}
}
}
// 避免不必要的重建
@Component
struct MemoizedComponent {
@State data: ExpensiveData;
aboutToUpdate() {
// 在更新前进行浅比较
if (!this.shouldUpdate(this.data)) {
// 阻止不必要的更新
}
}
}
// 及时释放资源
@Component
struct ResourceManagement {
private timer: number | null = null;
aboutToAppear() {
this.timer = setInterval(() => {
// 定时任务
}, 1000);
}
aboutToDisappear() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
}
# 生成密钥
keytool -genkey -alias "harmonyos" -keyalg EC -sigalg SHA256withECDSA
-keystore harmonyos.keystore -validity 18000
# 应用打包
./gradlew assembleRelease
鸿蒙生态正在快速发展,现在正是入场的黄金时期。通过本文的学习,你已经掌握了:
✅ 鸿蒙开发环境搭建
✅ ArkUI声明式编程
✅ 分布式应用开发
✅ 性能优化技巧
✅ 实际上线流程
立即行动,在万物互联的时代浪潮中抢占先机!
版权声明:本文为CSDN博主原创,转载请注明出处。如有问题欢迎在评论区留言讨论!
互动环节:你在鸿蒙开发中遇到了什么问题?欢迎在评论区留言,我们将一起讨论解决!
下期预告:《鸿蒙AI应用开发实战:从图像识别到语音交互》