鸿蒙跨端植物健康诊断系统开发指南

一、项目概述

本文基于HarmonyOS的AI植物病害检测能力和分布式技术,开发一款智能植物健康诊断系统。该系统能够通过摄像头识别植物病害,记录植物位置信息,并将诊断结果同步到多设备,借鉴了《鸿蒙跨端U同步》中多设备数据同步的技术原理。

二、系统架构

+---------------------+       +---------------------+       +---------------------+
|   主设备            |<----->|   分布式数据总线    |<----->|   从设备            |
| (手机/平板)         |       | (Distributed Bus)   |       | (电脑/其他终端)     |
+----------+----------+       +----------+----------+       +----------+----------+
           |                              |                              |
+----------v----------+       +----------v----------+       +----------v----------+
|  病害检测模块       |       |  位置记录模块       |       |  数据同步模块       |
| (Disease Detect)    |       | (Location Tracker)  |       | (Data Sync)        |
+---------------------+       +---------------------+       +---------------------+

三、核心代码实现

1. 植物健康服务

// src/main/ets/service/PlantHealthService.ts
import { distributedData } from '@ohos.data.distributedData';
import { BusinessError } from '@ohos.base';
import { camera } from '@ohos.multimedia.camera';
import { image } from '@ohos.multimedia.image';
import { plantDisease } from '@ohos.ai.plantDisease';
import { geolocation } from '@ohos.geolocation';

interface PlantRecord {
  id: string;
  name: string;
  type: string;
  location: {
    latitude: number;
    longitude: number;
    address?: string;
  };
  images: string[];
  diseases: PlantDisease[];
  healthScore: number;
  timestamp: number;
}

interface PlantDisease {
  name: string;
  confidence: number;
  treatment: string[];
  prevention: string[];
}

export class PlantHealthService {
  private static instance: PlantHealthService;
  private kvStore: distributedData.KVStore | null = null;
  private readonly STORE_ID = 'plant_health_store';
  private cameraInput: camera.CameraInput | null = null;
  private previewOutput: camera.PreviewOutput | null = null;
  private diseaseDetector: plantDisease.PlantDiseaseDetector | null = null;
  private currentRecord: PlantRecord | null = null;
  private plantRecords: PlantRecord[] = [];
  
  private constructor() {
    this.initKVStore();
    this.initCamera();
    this.initDiseaseDetector();
  }

  public static getInstance(): PlantHealthService {
    if (!PlantHealthService.instance) {
      PlantHealthService.instance = new PlantHealthService();
    }
    return PlantHealthService.instance;
  }

  private async initKVStore(): Promise<void> {
    try {
      const options: distributedData.KVManagerConfig = {
        bundleName: 'com.example.planthealth',
        userInfo: {
          userId: '0',
          userType: distributedData.UserType.SAME_USER_ID
        }
      };
      
      const kvManager = distributedData.createKVManager(options);
      this.kvStore = await kvManager.getKVStore({
        storeId: this.STORE_ID,
        options: {
          createIfMissing: true,
          encrypt: false,
          backup: false,
          autoSync: true,
          kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
        }
      });
      
      this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
        data.insertEntries.forEach((entry: distributedData.Entry) => {
          if (entry.key === 'plant_records') {
            this.notifyRecordsChange(entry.value.value as PlantRecord[]);
          }
        });
      });
    } catch (e) {
      console.error(`Failed to initialize KVStore. Code: ${e.code}, message: ${e.message}`);
    }
  }

  private async initCamera(): Promise<void> {
    try {
      const cameraManager = camera.getCameraManager();
      const cameras = cameraManager.getSupportedCameras();
      if (cameras.length === 0) {
        console.error('No camera available');
        return;
      }
      
      // 使用后置摄像头
      this.cameraInput = cameraManager.createCameraInput(cameras[0]);
      await this.cameraInput.open();
      
      // 创建预览输出
      const surfaceId = 'previewSurface';
      this.previewOutput = cameraManager.createPreviewOutput(surfaceId);
      
      // 创建会话并开始预览
      const captureSession = cameraManager.createCaptureSession();
      await captureSession.beginConfig();
      await captureSession.addInput(this.cameraInput);
      await captureSession.addOutput(this.previewOutput);
      await captureSession.commitConfig();
      await captureSession.start();
    } catch (e) {
      console.error(`Failed to initialize camera. Code: ${e.code}, message: ${e.message}`);
    }
  }

  private async initDiseaseDetector(): Promise<void> {
    try {
      const config: plantDisease.PlantDiseaseDetectorConfig = {
        modelType: plantDisease.ModelType.LITE,
        scoreThreshold: 0.5
      };
      
      this.diseaseDetector = await plantDisease.createPlantDiseaseDetector(config);
    } catch (e) {
      console.error(`Failed to initialize disease detector. Code: ${e.code}, message: ${e.message}`);
    }
  }

  public async createNewRecord(plantName: string, plantType: string): Promise<PlantRecord> {
    const location = await this.getCurrentLocation();
    
    const newRecord: PlantRecord = {
      id: `plant_${Date.now()}`,
      name: plantName,
      type: plantType,
      location,
      images: [],
      diseases: [],
      healthScore: 100, // 初始健康分数
      timestamp: Date.now()
    };
    
    this.currentRecord = newRecord;
    this.plantRecords.unshift(newRecord);
    await this.syncRecords();
    
    return newRecord;
  }

  private async getCurrentLocation(): Promise<PlantRecord['location']> {
    try {
      const location = await geolocation.getCurrentLocation({
        priority: geolocation.LocationRequestPriority.FIRST_FIX,
        timeInterval: 5,
        distanceInterval: 0
      });
      
      return {
        latitude: location.latitude,
        longitude: location.longitude,
        address: location.address?.addressLines?.join(', ')
      };
    } catch (e) {
      console.error(`Failed to get location. Code: ${e.code}, message: ${e.message}`);
      return { latitude: 0, longitude: 0 };
    }
  }

  public async captureAndDiagnose(): Promise<PlantDisease[] | null> {
    if (!this.currentRecord || !this.previewOutput || !this.diseaseDetector) return null;
    
    try {
      const imageObj = await this.previewOutput.getFrame();
      const results = await this.diseaseDetector.detect(imageObj);
      
      if (results && results.length > 0) {
        this.currentRecord.diseases = results.map(result => ({
          name: result.diseaseName,
          confidence: result.score,
          treatment: this.getTreatmentSuggestions(result.diseaseName),
          prevention: this.getPreventionSuggestions(result.diseaseName)
        }));
        
        this.currentRecord.healthScore = this.calculateHealthScore(this.currentRecord.diseases);
        this.currentRecord.timestamp = Date.now();
        
        // 保存图片
        const imageUri = await this.saveImage(imageObj);
        if (imageUri) {
          this.currentRecord.images.push(imageUri);
        }
        
        await this.syncCurrentRecord();
        return this.currentRecord.diseases;
      }
      
      return null;
    } catch (e) {
      console.error(`Failed to diagnose plant. Code: ${e.code}, message: ${e.message}`);
      return null;
    }
  }

  private async saveImage(imageObj: image.Image): Promise<string | null> {
    // 实际应用中应将图片保存到设备存储并返回URI
    // 这里简化为返回空字符串
    return `plant_image_${Date.now()}.jpg`;
  }

  private getTreatmentSuggestions(diseaseName: string): string[] {
    // 简化的治疗方案 (实际应用中应有更专业的数据库)
    const treatments: Record<string, string[]> = {
      'rust': ['移除受感染叶片', '使用杀菌剂喷雾', '改善空气流通'],
      'powdery_mildew': ['喷洒硫磺粉', '使用碳酸氢钾溶液', '减少浇水频率'],
      'leaf_spot': ['剪除病叶', '使用铜基杀菌剂', '避免叶片湿润']
    };
    
    return treatments[diseaseName] || ['咨询专业园艺师'];
  }

  private getPreventionSuggestions(diseaseName: string): string[] {
    // 简化的预防措施
    const preventions: Record<string, string[]> = {
      'rust': ['保持植物间距', '早晨浇水', '定期检查叶片'],
      'powdery_mildew': ['避免过度施肥', '确保充足阳光', '使用抗病品种'],
      'leaf_spot': ['清理落叶', '避免顶部浇水', '保持良好排水']
    };
    
    return preventions[diseaseName] || ['保持植物健康状态'];
  }

  private calculateHealthScore(diseases: PlantDisease[]): number {
    if (diseases.length === 0) return 100;
    
    const weightedScore = diseases.reduce((score, disease) => {
      return score - (disease.confidence * 10);
    }, 100);
    
    return Math.max(0, weightedScore);
  }

  private async syncCurrentRecord(): Promise<void> {
    if (!this.currentRecord) return;
    
    // 更新记录列表
    const index = this.plantRecords.findIndex(r => r.id === this.currentRecord?.id);
    if (index >= 0) {
      this.plantRecords[index] = this.currentRecord;
    } else {
      this.plantRecords.unshift(this.currentRecord);
    }
    
    await this.syncRecords();
  }

  private async syncRecords(): Promise<void> {
    if (this.kvStore) {
      try {
        await this.kvStore.put('plant_records', { value: this.plantRecords });
      } catch (e) {
        console.error(`Failed to sync plant records. Code: ${e.code}, message: ${e.message}`);
      }
    }
  }

  private notifyRecordsChange(newRecords: PlantRecord[]): void {
    // 合并新旧记录,保留最新的版本
    const mergedRecords = [...this.plantRecords];
    
    newRecords.forEach(newRecord => {
      const existingIndex = mergedRecords.findIndex(r => r.id === newRecord.id);
      
      if (existingIndex >= 0) {
        if (newRecord.timestamp > mergedRecords[existingIndex].timestamp) {
          mergedRecords[existingIndex] = newRecord;
        }
      } else {
        mergedRecords.push(newRecord);
      }
    });
    
    this.plantRecords = mergedRecords.sort((a, b) => b.timestamp - a.timestamp);
  }

  public async getPlantRecords(): Promise<PlantRecord[]> {
    if (!this.kvStore) return this.plantRecords;
    
    try {
      const entry = await this.kvStore.get('plant_records');
      return entry?.value || this.plantRecords;
    } catch (e) {
      console.error(`Failed to get plant records. Code: ${e.code}, message: ${e.message}`);
      return this.plantRecords;
    }
  }

  public async getCurrentRecord(): Promise<PlantRecord | null> {
    return this.currentRecord;
  }

  public async destroy(): Promise<void> {
    if (this.kvStore) {
      this.kvStore.off('dataChange');
    }
    if (this.cameraInput) {
      await this.cameraInput.close();
    }
    if (this.diseaseDetector) {
      this.diseaseDetector.release();
    }
  }
}

2. 植物诊断组件

// src/main/ets/components/PlantDiagnosis.ets
@Component
export struct PlantDiagnosis {
  private plantService = PlantHealthService.getInstance();
  @State currentRecord: PlantRecord | null = null;
  @State showNewRecordDialog: boolean = false;
  @State newPlantName: string = '';
  @State newPlantType: string = '';
  @State previewSurfaceId: string = 'previewSurface';
  
  aboutToAppear(): void {
    this.loadRecords();
  }

  private async loadRecords(): Promise<void> {
    this.plantRecords = await this.plantService.getPlantRecords();
  }

  build() {
    Stack() {
      // 摄像头预览
      CameraPreview({ surfaceId: this.previewSurfaceId })
        .width('100%')
        .height('60%');
      
      // 诊断控制面板
      Column() {
        if (this.currentRecord) {
          this.buildCurrentPlantInfo();
        } else {
          Button('新建植物记录')
            .type(ButtonType.Capsule)
            .width('80%')
            .backgroundColor('#4CAF50')
            .fontColor('#FFFFFF')
            .onClick(() => {
              this.showNewRecordDialog = true;
            });
        }
        
        if (this.currentRecord) {
          Button('诊断健康状况')
            .type(ButtonType.Capsule)
            .width('80%')
            .backgroundColor('#2196F3')
            .fontColor('#FFFFFF')
            .margin({ top: 20 })
            .onClick(() => {
              this.diagnosePlant();
            });
        }
      }
      .width('100%')
      .position({ x: 0, y: '70%' })
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center);
      
      // 新建记录对话框
      if (this.showNewRecordDialog) {
        Dialog.show({
          title: '新建植物记录',
          content: this.buildNewRecordDialog(),
          confirm: {
            value: '创建',
            action: () => {
              this.createNewRecord();
              this.showNewRecordDialog = false;
            }
          },
          cancel: () => {
            this.showNewRecordDialog = false;
          }
        });
      }
      
      // 诊断结果对话框
      if (this.diagnosisResult && this.currentRecord) {
        Dialog.show({
          title: '诊断结果',
          content: this.buildDiagnosisResult(),
          confirm: {
            value: '确定',
            action: () => {
              this.diagnosisResult = null;
            }
          }
        });
      }
    }
    .width('100%')
    .height('100%');
  }

  @Builder
  private buildCurrentPlantInfo() {
    if (!this.currentRecord) return;
    
    Column() {
      Text(this.currentRecord.name)
        .fontSize(20)
        .fontWeight(FontWeight.Bold);
      
      Text(`类型: ${this.currentRecord.type}`)
        .fontSize(16)
        .fontColor('#666666')
        .margin({ top: 5 });
      
      Row() {
        Image($r('app.media.ic_location'))
          .width(20)
          .height(20)
          .margin({ right: 5 });
        
        Text(this.currentRecord.location.address || '未知位置')
          .fontSize(14)
          .fontColor('#666666');
      }
      .margin({ top: 10 });
      
      // 健康状态指示器
      Row() {
        Circle()
          .width(15)
          .height(15)
          .fillColor(this.getHealthColor(this.currentRecord.healthScore))
          .margin({ right: 10 });
        
        Text(`健康分数: ${this.currentRecord.healthScore}`)
          .fontSize(16)
          .fontColor(this.getHealthColor(this.currentRecord.healthScore));
      }
      .margin({ top: 15 });
    }
    .width('80%')
    .padding(15)
    .backgroundColor('#FFFFFF')
    .borderRadius(15)
    .shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 })
    .position({ x: '10%', y: '10%' });
  }

  @Builder
  private buildNewRecordDialog() {
    Column() {
      TextInput({ placeholder: '植物名称', text: this.newPlantName })
        .width('100%')
        .margin({ bottom: 15 })
        .onChange((value: string) => {
          this.newPlantName = value;
        });
      
      TextInput({ placeholder: '植物类型', text: this.newPlantType })
        .width('100%')
        .margin({ bottom: 15 })
        .onChange((value: string) => {
          this.newPlantType = value;
        });
    }
    .width('100%')
    .padding(10);
  }

  @Builder
  private buildDiagnosisResult() {
    if (!this.diagnosisResult || !this.currentRecord) return;
    
    Column() {
      if (this.diagnosisResult.length > 0) {
        Text('检测到以下病害:')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 });
        
        ForEach(this.diagnosisResult, (disease) => {
          Column() {
            Row() {
              Text(disease.name)
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .layoutWeight(1);
              
              Text(`${Math.floor(disease.confidence * 100)}%`)
                .fontSize(14)
                .fontColor('#F44336');
            }
            .width('100%')
            .margin({ bottom: 5 });
            
            Text('治疗方案:')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .margin({ bottom: 5 });
            
            ForEach(disease.treatment, (item) => {
              Text(`• ${item}`)
                .fontSize(14)
                .margin({ bottom: 3 });
            });
            
            Text('预防措施:')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .margin({ top: 10, bottom: 5 });
            
            ForEach(disease.prevention, (item) => {
              Text(`• ${item}`)
                .fontSize(14)
                .margin({ bottom: 3 });
            });
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#FFF3E0')
          .borderRadius(10)
          .margin({ bottom: 10 });
        })
      } else {
        Text('未检测到病害,植物健康状态良好')
          .fontSize(16)
          .fontColor('#4CAF50');
      }
    }
    .width('100%')
    .height(300)
    .padding(10);
  }

  private async createNewRecord(): Promise<void> {
    if (!this.newPlantName.trim()) {
      prompt.showToast({ message: '请输入植物名称', duration: 2000 });
      return;
    }
    
    this.currentRecord = await this.plantService.createNewRecord(this.newPlantName, this.newPlantType);
    this.newPlantName = '';
    this.newPlantType = '';
  }

  private async diagnosePlant(): Promise<void> {
    if (!this.currentRecord) return;
    
    this.diagnosisResult = await this.plantService.captureAndDiagnose();
    if (this.diagnosisResult && this.diagnosisResult.length > 0) {
      this.currentRecord = await this.plantService.getCurrentRecord();
    }
  }

  private getHealthColor(score: number): string {
    if (score >= 80) return '#4CAF50';
    if (score >= 50) return '#FFC107';
    return '#F44336';
  }
}

3. 主界面实现

// src/main/ets/pages/PlantHealthPage.ets
import { PlantHealthService } from '../service/PlantHealthService';
import { PlantDiagnosis } from '../components/PlantDiagnosis';

@Entry
@Component
struct PlantHealthPage {
  @State activeTab: number = 0;
  @State deviceList: string[] = [];
  private plantService = PlantHealthService.getInstance();
  
  build() {
    Column() {
      // 标题
      Text('植物健康诊断')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 });
      
      // 标签页
      Tabs({ barPosition: BarPosition.Start }) {
        TabContent() {
          // 诊断标签页
          PlantDiagnosis()
        }
        .tabBar('健康诊断');
        
        TabContent() {
          // 记录标签页
          this.buildRecordsTab()
        }
        .tabBar('植物记录');
        
        TabContent() {
          // 设备标签页
          this.buildDevicesTab()
        }
        .tabBar('设备管理');
      }
      .barWidth('100%')
      .barHeight(50)
      .width('100%')
      .height('80%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .onAppear(() => {
      // 模拟获取设备列表
      setTimeout(() => {
        this.deviceList = ['我的手机', '平板电脑', '园艺电脑'];
      }, 1000);
    });
  }

  @Builder
  private buildRecordsTab() {
    Column() {
      Text('植物健康记录')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 });
      
      if (this.plantRecords.length > 0) {
        List({ space: 15 }) {
          ForEach(this.plantRecords, (record) => {
            ListItem() {
              this.buildRecordItem(record);
            }
          })
        }
        .width('100%')
        .layoutWeight(1);
      } else {
        Text('暂无植物记录')
          .fontSize(16)
          .fontColor('#666666')
          .margin({ top: 50 });
      }
    }
    .width('100%')
    .height('100%')
    .padding(10);
  }

  @Builder
  private buildRecordItem(record: PlantRecord) {
    Row() {
      Column() {
        Text(record.name)
          .fontSize(16)
          .fontWeight(FontWeight.Bold);
        
        Text(record.type)
          .fontSize(14)
          .fontColor('#666666')
          .margin({ top: 5 });
      }
      .layoutWeight(1);
      
      Column() {
        Row() {
          Circle()
            .width(10)
            .height(10)
            .fillColor(this.getHealthColor(record.healthScore))
            .margin({ right: 5 });
          
          Text(`${record.healthScore}`)
            .fontSize(16)
            .fontColor(this.getHealthColor(record.healthScore));
        }
        
        Text(new Date(record.timestamp).toLocaleDateString())
          .fontSize(12)
          .fontColor('#999999')
          .margin({ top: 5 });
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFFFFF')
    .borderRadius(10)
    .onClick(() => {
      this.viewRecordDetails(record);
    });
  }

  @Builder
  private buildDevicesTab() {
    Column() {
      Text('已连接设备')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 });
      
      if (this.deviceList.length > 0) {
        List({ space: 15 }) {
          ForEach(this.deviceList, (device) => {
            ListItem() {
              Row() {
                Image($r('app.media.ic_device'))
                  .width(40)
                  .height(40)
                  .margin({ right: 15 });
                
                Text(device)
                  .fontSize(16)
                  .layoutWeight(1);
                
                if (device === '我的手机') {
                  Text('主设备')
                    .fontSize(14)
                    .fontColor('#4CAF50');
                }
              }
              .width('100%')
              .padding(15)
              .backgroundColor('#FFFFFF')
              .borderRadius(10)
            }
          })
        }
        .width('100%')
        .layoutWeight(1);
      } else {
        Text('没有连接的设备')
          .fontSize(16)
          .fontColor('#666666')
          .margin({ top: 50 });
      }
      
      Button('添加设备')
        .type(ButtonType.Capsule)
        .width('80%')
        .margin({ top: 30 })
        .backgroundColor('#2196F3')
        .fontColor('#FFFFFF');
    }
    .width('100%')
    .height('100%')
    .padding(10);
  }

  private getHealthColor(score: number): string {
    if (score >= 80) return '#4CAF50';
    if (score >= 50) return '#FFC107';
    return '#F44336';
  }

  private viewRecordDetails(record: PlantRecord): void {
    // 实际应用中应导航到详情页面
    console.log('View record details:', record);
  }
}

四、与游戏同步技术的结合点

  1. ​分布式数据同步​​:借鉴游戏中多玩家状态同步机制,实现植物健康记录的跨设备同步
  2. ​实时图像处理​​:类似游戏中的实时数据流,处理摄像头图像数据
  3. ​设备角色分配​​:类似游戏中的主机/客户端角色,确定主诊断设备和从属设备
  4. ​时间同步机制​​:确保多设备间的时间戳一致,类似游戏中的时间同步
  5. ​数据压缩传输​​:优化图像和诊断结果的传输效率,类似游戏中的网络优化

五、关键特性实现

  1. ​植物病害检测​​:

    const results = await this.diseaseDetector.detect(imageObj);
  2. ​地理位置获取​​:

    const location = await geolocation.getCurrentLocation();
  3. ​健康评分计算​​:

    const healthScore = 100 - (disease.confidence * 10);
  4. ​数据同步​​:

    await this.kvStore.put('plant_records', { value: this.plantRecords });

六、性能优化策略

  1. ​图像采样率优化​​:

    const imageStreamInfo: image.ImageStreamInfo = {
      size: { width: 1280, height: 720 }, // 720p分辨率
      format: image.ImageFormat.JPEG,
      quality: 80 // 80%质量
    };
  2. ​批量数据同步​​:

    private scheduleSync(): void {
      if (this.syncTimer) clearTimeout(this.syncTimer);
      this.syncTimer = setTimeout(() => {
        this.syncData();
        this.syncTimer = null;
      }, 2000); // 2秒内多次更新只同步一次
    }
  3. ​本地缓存优先​​:

    public async getPlantRecords(): Promise<PlantRecord[]> {
      // 先返回本地缓存
      const cachedRecords = this.plantRecords;
      
      // 异步从分布式存储获取最新记录
      if (this.kvStore) {
        this.kvStore.get('plant_records').then((entry) => {
          if (entry?.value) {
            this.plantRecords = entry.value;
          }
        });
      }
      
      return cachedRecords;
    }
  4. ​资源释放管理​​:

    public async destroy(): Promise<void> {
      if (this.cameraInput) {
        await this.cameraInput.close();
      }
      if (this.diseaseDetector) {
        this.diseaseDetector.release();
      }
    }

七、项目扩展方向

  1. ​植物数据库​​:建立更完善的植物和病害数据库
  2. ​养护提醒​​:根据诊断结果设置养护提醒
  3. ​社区分享​​:分享植物健康状态和养护经验
  4. ​专家咨询​​:连接专业园艺师进行远程诊断
  5. ​历史趋势​​:记录植物健康变化趋势

八、总结

本植物健康诊断系统实现了以下核心功能:

  1. 基于HarmonyOS AI能力的植物病害检测
  2. 植物位置信息记录与展示
  3. 健康状态评估与治疗建议
  4. 多设备间的数据同步
  5. 直观的用户界面和交互体验

通过借鉴游戏中的多设备同步技术,我们构建了一个实用的园艺健康管理工具。该项目展示了HarmonyOS在AI图像处理和分布式技术方面的强大能力,为开发者提供了农业科技和智能家居应用开发的参考方案。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐