設備能力檢測:自適應不同硬件環境

引言

在 HarmonyOS 應用開發中,設備能力檢測是構建自適應應用的關鍵技術。隨着 HarmonyOS 生態的不斷擴大,開發者需要確保應用能夠在不同硬件配置的設備上提供一致的用户體驗。本文將深入講解如何在 HarmonyOS Next 中實現設備能力檢測和自適應佈局。

官方參考資料

  • HarmonyOS API 參考
  • 常見 API 問題

基礎概念

什麼是設備能力檢測

設備能力檢測是指應用程序在運行時識別當前設備硬件特性,並據此調整應用行為的技術。在 HarmonyOS 中,這包括:

  • 屏幕尺寸和分辨率檢測
  • 傳感器可用性檢查
  • 硬件性能評估
  • 外設連接狀態監控

核心 API 概覽

HarmonyOS Next 提供了以下關鍵 API 用於設備能力檢測:

// 設備信息管理
import deviceInfo from "@ohos.deviceInfo";

// 顯示系統管理
import display from "@ohos.display";

// 傳感器管理
import sensor from "@ohos.sensor";

// 電源管理
import batteryInfo from "@ohos.batteryInfo";

設備基礎信息檢測

獲取設備基本信息

import deviceInfo from "@ohos.deviceInfo";

// 獲取設備基礎信息
let deviceInfo: deviceInfo.DeviceInfo = deviceInfo.getDeviceInfo();

console.log(`設備型號: ${deviceInfo.model}`);
console.log(`設備名稱: ${deviceInfo.name}`);
console.log(`設備類型: ${deviceInfo.deviceType}`);
console.log(`硬件版本: ${deviceInfo.hardwareVersion}`);
console.log(`軟件版本: ${deviceInfo.softwareVersion}`);

設備類型對照表

設備類型

數值

説明

PHONE

0x00

手機

TABLET

0x01

平板

WEARABLE

0x06

穿戴設備

TV

0x07

智慧屏

屏幕信息檢測

import display from "@ohos.display";

// 獲取默認顯示設備信息
let defaultDisplay: display.Display = display.getDefaultDisplaySync();

console.log(`屏幕寬度: ${defaultDisplay.width}`);
console.log(`屏幕高度: ${defaultDisplay.height}`);
console.log(`像素密度: ${defaultDisplay.densityPixels}`);
console.log(`刷新率: ${defaultDisplay.refreshRate}`);

// 計算實際物理尺寸
let physicalWidth: number = defaultDisplay.width / defaultDisplay.densityPixels;
let physicalHeight: number =
  defaultDisplay.height / defaultDisplay.densityPixels;
console.log(`物理寬度: ${physicalWidth}英寸`);
console.log(`物理高度: ${physicalHeight}英寸`);

傳感器能力檢測

檢測傳感器可用性

import sensor from "@ohos.sensor";

class SensorDetector {
  // 檢查傳感器是否可用
  static checkSensorAvailability(sensorType: sensor.SensorType): boolean {
    try {
      let sensorInstance = sensor.getSensor(sensorType);
      return sensorInstance !== null && sensorInstance !== undefined;
    } catch (error) {
      console.error(`傳感器檢測失敗: ${error.message}`);
      return false;
    }
  }

  // 獲取可用傳感器列表
  static getAvailableSensors(): string[] {
    const sensorTypes = [
      sensor.SensorType.ACCELEROMETER,
      sensor.SensorType.GYROSCOPE,
      sensor.SensorType.AMBIENT_LIGHT,
      sensor.SensorType.PROXIMITY,
      sensor.SensorType.BAROMETER,
    ];

    let availableSensors: string[] = [];

    sensorTypes.forEach((type) => {
      if (this.checkSensorAvailability(type)) {
        availableSensors.push(this.getSensorName(type));
      }
    });

    return availableSensors;
  }

  private static getSensorName(sensorType: sensor.SensorType): string {
    const sensorNames = {
      [sensor.SensorType.ACCELEROMETER]: "加速度傳感器",
      [sensor.SensorType.GYROSCOPE]: "陀螺儀",
      [sensor.SensorType.AMBIENT_LIGHT]: "環境光傳感器",
      [sensor.SensorType.PROXIMITY]: "距離傳感器",
      [sensor.SensorType.BAROMETER]: "氣壓計",
    };

    return sensorNames[sensorType] || "未知傳感器";
  }
}

// 使用示例
let availableSensors = SensorDetector.getAvailableSensors();
console.log("可用傳感器:", availableSensors);

性能能力檢測

內存和存儲檢測

import systemParameter from "@ohos.systemParameter";

class PerformanceDetector {
  // 獲取內存信息
  static async getMemoryInfo(): Promise<Object> {
    try {
      let totalMemory = await systemParameter.getSync(
        "const.physical_memory.size"
      );
      let availableMemory = await systemParameter.getSync(
        "sys.physical_memory.available_size"
      );

      return {
        totalMemory: this.formatBytes(parseInt(totalMemory)),
        availableMemory: this.formatBytes(parseInt(availableMemory)),
        usagePercentage: (
          (1 - parseInt(availableMemory) / parseInt(totalMemory)) *
          100
        ).toFixed(2),
      };
    } catch (error) {
      console.error("獲取內存信息失敗:", error);
      return {};
    }
  }

  // 獲取存儲信息
  static async getStorageInfo(): Promise<Object> {
    try {
      // 注意:實際API可能有所不同,這裏展示概念
      let totalStorage = await systemParameter.getSync(
        "const.storage.total_size"
      );
      let availableStorage = await systemParameter.getSync(
        "sys.storage.available_size"
      );

      return {
        totalStorage: this.formatBytes(parseInt(totalStorage)),
        availableStorage: this.formatBytes(parseInt(availableStorage)),
        usagePercentage: (
          (1 - parseInt(availableStorage) / parseInt(totalStorage)) *
          100
        ).toFixed(2),
      };
    } catch (error) {
      console.error("獲取存儲信息失敗:", error);
      return {};
    }
  }

  private static formatBytes(bytes: number): string {
    const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
    if (bytes === 0) return "0 Bytes";
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i];
  }
}

// 使用示例
PerformanceDetector.getMemoryInfo().then((memoryInfo) => {
  console.log("內存信息:", memoryInfo);
});

PerformanceDetector.getStorageInfo().then((storageInfo) => {
  console.log("存儲信息:", storageInfo);
});

自適應佈局實現

響應式網格佈局

import { GridContainer, GridRow, GridCol } from '@ohos/grid-container';
import { Color, FlexAlign } from '@ohos.base';

@Entry
@Component
struct AdaptiveLayout {
  @State currentDeviceType: string = 'phone';

  aboutToAppear() {
    this.detectDeviceType();
  }

  detectDeviceType() {
    let displayInfo = display.getDefaultDisplaySync();
    let density = displayInfo.densityPixels;
    let width = displayInfo.width / density;

    if (width < 600) {
      this.currentDeviceType = 'phone';
    } else if (width < 840) {
      this.currentDeviceType = 'tablet';
    } else {
      this.currentDeviceType = 'tv';
    }
  }

  build() {
    GridContainer() {
      GridRow() {
        // 手機:單列,平板:雙列,TV:三列
        GridCol({
          span: this.getColumnSpan()
        }) {
          Column() {
            Text('自適應內容區域')
              .fontSize(this.getFontSize())
              .fontColor(Color.White)
          }
          .width('100%')
          .height(100)
          .backgroundColor(0x317AF7)
          .justifyContent(FlexAlign.Center)
        }
      }
      .padding(10)
    }
  }

  getColumnSpan(): number {
    switch (this.currentDeviceType) {
      case 'phone': return 12;
      case 'tablet': return 6;
      case 'tv': return 4;
      default: return 12;
    }
  }

  getFontSize(): number {
    switch (this.currentDeviceType) {
      case 'phone': return 16;
      case 'tablet': return 18;
      case 'tv': return 24;
      default: return 16;
    }
  }
}

斷點系統實現

import { State } from "@ohos/base";
import display from "@ohos.display";

class BreakpointSystem {
  // 定義斷點
  static readonly BREAKPOINTS = {
    PHONE: 0,
    PHONE_LANDSCAPE: 480,
    TABLET: 768,
    DESKTOP: 1024,
    TV: 1440,
  };

  // 當前斷點狀態
  @State currentBreakpoint: string = "phone";

  // 監聽屏幕尺寸變化
  setupBreakpointListener() {
    display.on("change", (displayId: number) => {
      this.updateBreakpoint();
    });
  }

  updateBreakpoint() {
    let displayInfo = display.getDefaultDisplaySync();
    let width = displayInfo.width / displayInfo.densityPixels;

    if (width < BreakpointSystem.BREAKPOINTS.PHONE_LANDSCAPE) {
      this.currentBreakpoint = "phone";
    } else if (width < BreakpointSystem.BREAKPOINTS.TABLET) {
      this.currentBreakpoint = "phone_landscape";
    } else if (width < BreakpointSystem.BREAKPOINTS.DESKTOP) {
      this.currentBreakpoint = "tablet";
    } else if (width < BreakpointSystem.BREAKPOINTS.TV) {
      this.currentBreakpoint = "desktop";
    } else {
      this.currentBreakpoint = "tv";
    }
  }

  // 根據斷點獲取佈局配置
  getLayoutConfig() {
    const configs = {
      phone: { columns: 1, margin: 16, fontSize: 14 },
      phone_landscape: { columns: 2, margin: 20, fontSize: 15 },
      tablet: { columns: 3, margin: 24, fontSize: 16 },
      desktop: { columns: 4, margin: 32, fontSize: 18 },
      tv: { columns: 6, margin: 48, fontSize: 24 },
    };

    return configs[this.currentBreakpoint] || configs.phone;
  }
}

硬件特性適配

攝像頭能力適配

import camera from "@ohos.camera";

class CameraAdapter {
  // 檢測攝像頭能力
  static async checkCameraCapabilities(): Promise<Object> {
    try {
      let cameraManager = camera.getCameraManager();
      let cameras = cameraManager.getSupportedCameras();

      let capabilities = {
        hasBackCamera: false,
        hasFrontCamera: false,
        supportedResolutions: [],
        hasFlash: false,
        hasAutoFocus: false,
      };

      for (let cam of cameras) {
        if (cam.position === camera.CameraPosition.CAMERA_POSITION_BACK) {
          capabilities.hasBackCamera = true;
        } else if (
          cam.position === camera.CameraPosition.CAMERA_POSITION_FRONT
        ) {
          capabilities.hasFrontCamera = true;
        }

        // 獲取攝像頭支持的分辨率
        let outputCapabilities =
          cameraManager.getSupportedOutputCapability(cam);
        capabilities.supportedResolutions =
          outputCapabilities.previewProfiles.map(
            (profile) => `${profile.size.width}x${profile.size.height}`
          );
      }

      return capabilities;
    } catch (error) {
      console.error("攝像頭檢測失敗:", error);
      return {};
    }
  }

  // 根據設備能力選擇合適的攝像頭配置
  static getOptimalCameraConfig(capabilities: Object): Object {
    if (!capabilities.hasBackCamera && capabilities.hasFrontCamera) {
      return {
        cameraPosition: camera.CameraPosition.CAMERA_POSITION_FRONT,
        resolution: "1280x720",
        useFlash: false,
      };
    }

    // 選擇最高支持的分辨率
    let maxResolution = capabilities.supportedResolutions[0];
    for (let res of capabilities.supportedResolutions) {
      let [width, height] = res.split("x").map(Number);
      let [maxWidth, maxHeight] = maxResolution.split("x").map(Number);
      if (width * height > maxWidth * maxHeight) {
        maxResolution = res;
      }
    }

    return {
      cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,
      resolution: maxResolution,
      useFlash: capabilities.hasFlash,
    };
  }
}

網絡連接檢測

import connection from "@ohos.net.connection";

class NetworkDetector {
  // 檢測網絡類型
  static async getNetworkInfo(): Promise<Object> {
    try {
      let netHandle = connection.getDefaultNet();
      let netCapabilities = await netHandle.getNetCapabilities();

      return {
        networkType: this.getNetworkType(netCapabilities),
        hasInternet: netCapabilities.hasCapability(
          connection.NetCap.NET_CAPABILITY_INTERNET
        ),
        bearerTypes: netCapabilities.bearerTypes,
        linkUpBandwidth: netCapabilities.linkUpBandwidthKbps,
        linkDownBandwidth: netCapabilities.linkDownBandwidthKbps,
      };
    } catch (error) {
      console.error("網絡檢測失敗:", error);
      return { networkType: "unknown", hasInternet: false };
    }
  }

  private static getNetworkType(
    netCapabilities: connection.NetCapabilities
  ): string {
    if (
      netCapabilities.bearerTypes.includes(connection.NetBearType.BEARER_WIFI)
    ) {
      return "wifi";
    } else if (
      netCapabilities.bearerTypes.includes(
        connection.NetBearType.BEARER_CELLULAR
      )
    ) {
      return "cellular";
    } else if (
      netCapabilities.bearerTypes.includes(
        connection.NetBearType.BEARER_ETHERNET
      )
    ) {
      return "ethernet";
    } else {
      return "unknown";
    }
  }

  // 監聽網絡變化
  static setupNetworkListener(callback: (networkInfo: Object) => void) {
    connection.on("netAvailable", (data: connection.NetHandle) => {
      this.getNetworkInfo().then((info) => callback(info));
    });

    connection.on("netLost", (data: connection.NetHandle) => {
      callback({ networkType: "disconnected", hasInternet: false });
    });
  }
}

綜合實戰案例

智能媒體播放器適配

import { Component, Entry, State } from '@ohos/base';
import display from '@ohos.display';
import { Column, Row, Text, Image, Button, Radio, RadioGroup } from '@ohos.components';
import { NetworkDetector } from './NetworkDetector';
import { PerformanceDetector } from './PerformanceDetector';

@Entry
@Component
struct SmartMediaPlayer {
  @State deviceCapabilities: Object = {};
  @State currentLayout: string = 'mobile';
  @State videoQuality: string = 'auto';

  aboutToAppear() {
    this.detectDeviceCapabilities();
    this.setupChangeListeners();
  }

  detectDeviceCapabilities() {
    // 綜合檢測設備能力
    Promise.all([
      this.getScreenInfo(),
      this.getNetworkInfo(),
      this.getPerformanceInfo()
    ]).then(([screenInfo, networkInfo, performanceInfo]) => {
      this.deviceCapabilities = {
        screen: screenInfo,
        network: networkInfo,
        performance: performanceInfo
      };
      this.adaptLayoutAndQuality();
    });
  }

  async getScreenInfo() {
    let displayInfo = display.getDefaultDisplaySync();
    return {
      width: displayInfo.width,
      height: displayInfo.height,
      density: displayInfo.densityPixels,
      refreshRate: displayInfo.refreshRate
    };
  }

  async getNetworkInfo() {
    return await NetworkDetector.getNetworkInfo();
  }

  async getPerformanceInfo() {
    return await PerformanceDetector.getMemoryInfo();
  }

  adaptLayoutAndQuality() {
    // 根據設備能力調整佈局和視頻質量
    let screenWidth = this.deviceCapabilities.screen.width / this.deviceCapabilities.screen.density;

    if (screenWidth < 600) {
      this.currentLayout = 'mobile';
      this.videoQuality = this.deviceCapabilities.network.networkType === 'wifi' ? '720p' : '480p';
    } else if (screenWidth < 1200) {
      this.currentLayout = 'tablet';
      this.videoQuality = '1080p';
    } else {
      this.currentLayout = 'tv';
      this.videoQuality = '4k';
    }

    // 根據性能調整
    if (this.deviceCapabilities.performance.usagePercentage > 80) {
      this.videoQuality = this.downgradeQuality(this.videoQuality);
    }
  }

  downgradeQuality(currentQuality: string): string {
    const qualityLevels = ['4k', '1080p', '720p', '480p'];
    let currentIndex = qualityLevels.indexOf(currentQuality);
    return qualityLevels[Math.min(currentIndex + 1, qualityLevels.length - 1)];
  }

  setupChangeListeners() {
    // 監聽屏幕方向變化
    display.on('change', () => {
      this.detectDeviceCapabilities();
    });

    // 監聽網絡變化
    NetworkDetector.setupNetworkListener(() => {
      this.adaptLayoutAndQuality();
    });
  }

  build() {
    Column() {
      Text(`當前設備類型: ${this.currentLayout}`)
        .fontSize(16)
        .padding(10)

      Text(`視頻質量設置: ${this.videoQuality}`)
        .fontSize(16)
        .padding(10)

      // 根據設備類型展示不同的UI佈局
      if (this.currentLayout === 'mobile') {
        this.renderMobileLayout();
      } else if (this.currentLayout === 'tablet') {
        this.renderTabletLayout();
      } else {
        this.renderTVLayout();
      }
    }
    .padding(20)
    .width('100%')
  }

  renderMobileLayout() {
    Column() {
      Image('media/poster.jpg')
        .width('100%')
        .aspectRatio(16/9)

      // 簡單控制欄
      Row() {
        Button('播放')
          .flexGrow(1)
        Button('下載')
          .flexGrow(1)
      }
      .width('100%')
    }
  }

  renderTabletLayout() {
    Row() {
      Image('media/poster.jpg')
        .flexGrow(2)
        .aspectRatio(16/9)

      // 更多控制選項
      Column() {
        Text('視頻詳情')
          .fontSize(18)
        Button('播放')
        Button('收藏')
        Button('分享')
      }
      .flexGrow(1)
    }
    .width('100%')
  }

  renderTVLayout() {
    Column() {
      Image('media/poster.jpg')
        .width('80%')
        .aspectRatio(16/9)

      // 完整控制界面
      Row() {
        Column() {
          Button('播放')
            .fontSize(24)
          Button('暫停')
            .fontSize(24)
        }

        Column() {
          Text('畫質選擇:')
            .fontSize(20)
          RadioGroup() {
            Radio({ value: '4k' }) { Text('4K') }
            Radio({ value: '1080p' }) { Text('1080p') }
            Radio({ value: '720p' }) { Text('720p') }
          }
        }
      }
  }
    .width('100%')
  }
}

總結與最佳實踐

通過本文的學習,我們全面掌握了 HarmonyOS 中設備能力檢測和自適應佈局的核心技術。以下是一些關鍵要點和最佳實踐總結:

核心技術要點回顧

  1. 多維度設備信息檢測:我們學習瞭如何獲取設備基礎信息、屏幕屬性、傳感器可用性、性能狀況和網絡連接狀態等關鍵參數,為自適應應用提供了數據基礎。
  2. 響應式佈局設計:通過斷點系統和響應式網格佈局,實現了在不同屏幕尺寸和設備類型下的優雅適配。
  3. 硬件特性差異化適配:針對攝像頭等硬件特性,我們學習瞭如何檢測其能力並提供相應的功能支持。
  4. 實時狀態監聽與動態調整:通過監聽屏幕變化和網絡狀態,實現了應用行為的動態調整,提升了用户體驗。

最佳實踐建議

  1. 按需檢測,避免過度檢測
  • 僅在需要時檢測設備能力,避免不必要的性能開銷
  • 合理緩存檢測結果,減少重複檢測
  1. 漸進式功能降級
  • 採用功能降級策略,確保在低配置設備上也能提供核心功能
  • 避免因硬件限制導致應用崩潰或無響應
  1. 以用户體驗為中心
  • 確保在不同設備上的交互邏輯一致性
  • 根據屏幕尺寸和輸入方式優化用户界面
  1. 性能優化考量
  • 對於性能敏感的檢測,採用異步方式進行
  • 減少頻繁檢測帶來的系統開銷
  1. 測試覆蓋
  • 在多種設備類型上進行測試,確保適配效果
  • 使用模擬器模擬不同網絡條件和設備配置

結語

設備能力檢測和自適應佈局是構建高質量 HarmonyOS 應用的重要技術手段。通過合理利用這些技術,開發者可以確保應用在各種設備上都能提供出色的用户體驗,從而擴大應用的受眾範圍並提升用户滿意度。

隨着 HarmonyOS 生態的不斷髮展,設備類型將更加多樣化,掌握設備能力檢測和自適應技術將成為每個 HarmonyOS 開發者的必備技能。希望本文的內容能夠為您的開發工作帶來幫助,讓您的應用在多設備環境中表現出色。

需要參加鴻蒙認證的請點擊 鴻蒙認證鏈接