Harmony開發之設備發現與連接——分佈式操作的起點

引入:自動發現附近可用設備

想象一下這樣的場景:當你走進家門,手機自動發現並連接上家裏的智能音響,開始播放你喜歡的音樂;當你在會議室做演示時,平板自動發現投影儀並建立連接,無需繁瑣的線纜連接和設置。這種"開機即連"的無縫體驗,正是HarmonyOS設備發現與連接技術帶來的革命性變化。作為分佈式操作的起點,設備發現與連接為多設備協同奠定了堅實基礎。

一、設備發現與連接的核心價值

傳統設備連接的痛點

在傳統的IoT設備連接中,用户需要手動進入配對模式、搜索可用設備、手動選擇目標並輸入密碼,這個過程不僅繁瑣,還容易因操作失誤導致連接失敗。

HarmonyOS的解決方案

HarmonyOS通過分佈式軟總線技術,實現了設備的自動發現和透明連接。設備開機後自動廣播自身信息,系統掃描到符合條件的設備自動發起連接,分佈式能力完成跨設備數據傳輸和協作。

核心優勢對比:

  • 自動發現:無需手動搜索,設備自動相互識別
  • 智能過濾:基於設備能力、賬户關係等條件精準匹配
  • 安全認證:多重認證機制確保連接安全性
  • 多協議支持:統一抽象層支持藍牙、Wi-Fi等多種通信方式

二、技術架構與核心組件

2.1 分佈式軟總線基礎

分佈式軟總線(DSoftBus)是HarmonyOS分佈式系統的神經網絡,承載着設備發現、連接建立、數據傳輸等關鍵功能。它採用分層架構設計,位於HarmonyOS系統服務層,為上層應用提供統一的分佈式通信能力。

核心模塊組成:

  • 發現模塊:負責檢測附近的HarmonyOS設備,使用CoAP等協議進行輕量級傳輸
  • 連接管理模塊:處理設備間連接的建立和維護
  • 傳輸模塊:提供多種數據類型的傳輸能力
  • 安全模塊:內置安全認證和數據加密機制

2.2 設備管理核心接口

HarmonyOS提供了@ohos.distributedHardware.deviceManager模塊來完成設備管理功能,主要包括:

// 設備管理核心接口
interface DeviceManager {
  createDeviceManager(bundleName: string, callback: AsyncCallback<DeviceManager>): void;
  startDeviceDiscovery(subscribeId: number, options: DiscoverOptions): void;
  authenticateDevice(deviceId: string, authParam: AuthParam, callback: AsyncCallback<void>): void;
  getAvailableDeviceListSync(): DeviceInfo[];
}

三、設備發現實戰開發

3.1 權限配置與初始化

在開始設備發現前,需要配置必要的權限並初始化設備管理實例。

權限配置(module.json5):

{
  "reqPermissions": [
    { 
      "name": "ohos.permission.DISTRIBUTED_DATASYNC",
      "reason": "用於設備發現和數據同步" 
    },
    { 
      "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE",
      "reason": "監聽設備狀態變化" 
    },
    { 
      "name": "ohos.permission.DISCOVER_BLUETOOTH",
      "reason": "藍牙設備發現" 
    },
    { 
      "name": "ohos.permission.LOCATION",
      "reason": "位置權限用於設備發現" 
    }
  ]
}

設備管理初始化:

import deviceManager from '@ohos.distributedHardware.deviceManager';
import { BusinessError } from '@ohos.BasicServicesKit';

let dmInstance: deviceManager.DeviceManager | null = null;

// 創建設備管理實例
function initDeviceManager(): void {
  try {
    deviceManager.createDeviceManager('com.example.myapp', (err: BusinessError, data: deviceManager.DeviceManager) => {
      if (err) {
        console.error('DeviceManager create failed: ' + JSON.stringify(err));
        return;
      }
      dmInstance = data;
      console.info('DeviceManager created successfully');
      setupDeviceDiscovery();
    });
  } catch (error) {
    console.error('Init DeviceManager error: ' + JSON.stringify(error));
  }
}

3.2 啓動設備發現

配置發現參數並啓動設備掃描過程。

function setupDeviceDiscovery(): void {
  if (!dmInstance) {
    return;
  }

  // 配置發現參數
  const discoverOptions: deviceManager.DiscoverOptions = {
    subscribeId: 1001, // 訂閲ID
    mode: deviceManager.DiscoverMode.DISCOVER_MODE_ACTIVE, // 主動發現模式
    medium: deviceManager.ExchangeMedium.COAP, // 使用CoAP協議
    freq: deviceManager.ExchangeFreq.HIGH, // 高頻發現
    isSameAccount: true, // 同一賬户設備
    isWakeRemote: true, // 喚醒遠程設備
    capability: deviceManager.Capability.OSD // 設備能力過濾
  };

  // 啓動設備發現
  dmInstance.startDeviceDiscovery(discoverOptions);
  
  // 註冊設備發現回調
  registerDeviceCallbacks();
}

function registerDeviceCallbacks(): void {
  if (!dmInstance) {
    return;
  }

  // 監聽設備發現事件
  dmInstance.on('deviceFound', (data: deviceManager.DeviceInfo) => {
    console.info('Device found: ' + JSON.stringify(data));
    handleFoundDevice(data);
  });

  // 監聽設備狀態變化
  dmInstance.on('deviceStateChange', (data: deviceManager.DeviceStateInfo) => {
    console.info('Device state changed: ' + JSON.stringify(data));
    handleDeviceStateChange(data);
  });

  // 監聽發現失敗事件
  dmInstance.on('discoverFail', (reason: string) => {
    console.error('Discovery failed: ' + reason);
  });
}

3.3 設備過濾與識別

發現設備後需要進行過濾和識別,確保只連接目標設備。

function handleFoundDevice(deviceInfo: deviceManager.DeviceInfo): void {
  // 基礎設備信息驗證
  if (!deviceInfo.deviceId || !deviceInfo.deviceName) {
    console.warn('Invalid device info');
    return;
  }

  // 設備類型過濾(只連接手機、平板、智慧屏等)
  const allowedTypes = [
    deviceManager.DeviceType.PHONE,
    deviceManager.DeviceType.TABLET,
    deviceManager.DeviceType.TV
  ];
  
  if (!allowedTypes.includes(deviceInfo.deviceType)) {
    console.info('Skip device type: ' + deviceInfo.deviceType);
    return;
  }

  // 設備能力驗證
  if (!hasRequiredCapability(deviceInfo)) {
    console.info('Device does not have required capability');
    return;
  }

  // 設備可信狀態檢查
  if (isTrustedDevice(deviceInfo.deviceId)) {
    console.info('Found trusted device: ' + deviceInfo.deviceName);
    onTrustedDeviceFound(deviceInfo);
  } else {
    console.info('Found untrusted device: ' + deviceInfo.deviceName);
    onUntrustedDeviceFound(deviceInfo);
  }
}

// 檢查設備是否具備所需能力
function hasRequiredCapability(deviceInfo: deviceManager.DeviceInfo): boolean {
  // 這裏可以根據業務需求檢查設備能力
  return deviceInfo.capabilityBitmap.includes(deviceManager.Capability.OSD);
}

// 檢查設備是否已認證
function isTrustedDevice(deviceId: string): boolean {
  // 從本地存儲或系統服務中檢查設備可信狀態
  const trustedDevices = getTrustedDevicesFromStorage();
  return trustedDevices.includes(deviceId);
}

四、設備認證與連接建立

4.1 設備認證流程

發現目標設備後,需要建立安全連接,認證是確保設備可信的關鍵步驟。

// 設備認證參數配置
interface AuthConfig {
  authType: deviceManager.AuthType;
  appName: string;
  extraInfo?: Record<string, Object>;
}

function authenticateDevice(deviceInfo: deviceManager.DeviceInfo): void {
  if (!dmInstance) {
    return;
  }

  const authConfig: AuthConfig = {
    authType: deviceManager.AuthType.PIN_CODE, // PIN碼認證
    appName: 'MyDistributedApp',
    extraInfo: {
      'authTimeout': 30000, // 30秒超時
      'maxRetryCount': 3    // 最大重試次數
    }
  };

  const authParam: deviceManager.AuthParam = {
    authType: authConfig.authType,
    appName: authConfig.appName,
    extraInfo: authConfig.extraInfo
  };

  dmInstance.authenticateDevice(deviceInfo.deviceId, authParam, (err: BusinessError) => {
    if (err) {
      console.error('Authentication failed: ' + JSON.stringify(err));
      handleAuthFailure(deviceInfo.deviceId, err);
      return;
    }
    
    console.info('Authentication successful for device: ' + deviceInfo.deviceName);
    handleAuthSuccess(deviceInfo);
  });
}

// 認證成功處理
function handleAuthSuccess(deviceInfo: deviceManager.DeviceInfo): void {
  // 保存設備可信狀態
  saveTrustedDevice(deviceInfo.deviceId);
  
  // 建立業務連接
  establishBusinessConnection(deviceInfo);
  
  // 更新UI狀態
  updateDeviceConnectionStatus(deviceInfo.deviceId, 'connected');
}

// 認證失敗處理
function handleAuthFailure(deviceId: string, error: BusinessError): void {
  console.error('Device authentication failed: ', error);
  
  // 根據錯誤碼進行相應處理
  switch (error.code) {
    case 201: // 權限錯誤
      requestMissingPermissions();
      break;
    case 202: // 設備不支持
      showDeviceNotSupportedToast();
      break;
    case 203: // 認證超時
      retryAuthentication(deviceId);
      break;
    default:
      showAuthenticationErrorToast();
  }
}

4.2 連接管理與狀態維護

建立連接後需要維護連接狀態和處理異常情況。

class DeviceConnectionManager {
  private connectedDevices: Map<string, deviceManager.DeviceInfo> = new Map();
  private connectionListeners: Set<ConnectionListener> = new Set();
  
  // 添加設備到連接池
  addConnectedDevice(deviceInfo: deviceManager.DeviceInfo): void {
    this.connectedDevices.set(deviceInfo.deviceId, deviceInfo);
    this.notifyConnectionChange('connected', deviceInfo);
  }
  
  // 移除設備連接
  removeConnectedDevice(deviceId: string): void {
    const deviceInfo = this.connectedDevices.get(deviceId);
    if (deviceInfo) {
      this.connectedDevices.delete(deviceId);
      this.notifyConnectionChange('disconnected', deviceInfo);
    }
  }
  
  // 獲取所有已連接設備
  getConnectedDevices(): deviceManager.DeviceInfo[] {
    return Array.from(this.connectedDevices.values());
  }
  
  // 註冊連接狀態監聽
  addConnectionListener(listener: ConnectionListener): void {
    this.connectionListeners.add(listener);
  }
  
  // 通知連接狀態變化
  private notifyConnectionChange(status: 'connected' | 'disconnected', deviceInfo: deviceManager.DeviceInfo): void {
    this.connectionListeners.forEach(listener => {
      try {
        listener.onDeviceConnectionChanged(status, deviceInfo);
      } catch (error) {
        console.error('Connection listener error: ', error);
      }
    });
  }
  
  // 清理資源
  cleanup(): void {
    this.connectedDevices.clear();
    this.connectionListeners.clear();
  }
}

// 使用示例
const connectionManager = new DeviceConnectionManager();

// 添加連接狀態監聽
connectionManager.addConnectionListener({
  onDeviceConnectionChanged: (status, deviceInfo) => {
    console.info(`Device ${deviceInfo.deviceName} ${status}`);
    updateDeviceListUI();
  }
});

五、完整實戰案例:多設備音樂共享

5.1 場景描述

實現一個多設備音樂共享應用,手機作為控制中心,可以自動發現附近的音響設備並建立連接,實現音樂的無縫切換和播放。

5.2 核心實現

// 音樂設備管理器
class MusicDeviceManager {
  private deviceManager: deviceManager.DeviceManager | null = null;
  private musicDevices: Map<string, MusicDevice> = new Map();
  
  // 初始化音樂設備管理
  async initialize(): Promise<void> {
    try {
      this.deviceManager = await this.createDeviceManager();
      this.setupDeviceDiscovery();
      this.setupEventListeners();
    } catch (error) {
      console.error('MusicDeviceManager initialization failed: ', error);
    }
  }
  
  // 創建設備管理器
  private createDeviceManager(): Promise<deviceManager.DeviceManager> {
    return new Promise((resolve, reject) => {
      deviceManager.createDeviceManager('com.example.musicapp', (err, manager) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(manager);
      });
    });
  }
  
  // 設置設備發現
  private setupDeviceDiscovery(): void {
    if (!this.deviceManager) {
      return;
    }
    
    const options: deviceManager.DiscoverOptions = {
      subscribeId: 2001,
      mode: deviceManager.DiscoverMode.DISCOVER_MODE_ACTIVE,
      medium: deviceManager.ExchangeMedium.COAP,
      freq: deviceManager.ExchangeFreq.NORMAL,
      isSameAccount: false, // 允許不同賬户設備
      capability: deviceManager.Capability.AUDIO_PLAYBACK
    };
    
    this.deviceManager.startDeviceDiscovery(options);
  }
  
  // 處理髮現的音樂設備
  private handleMusicDeviceFound(deviceInfo: deviceManager.DeviceInfo): void {
    // 檢查是否支持音樂播放能力
    if (this.isMusicPlaybackDevice(deviceInfo)) {
      const musicDevice: MusicDevice = {
        deviceId: deviceInfo.deviceId,
        deviceName: deviceInfo.deviceName,
        deviceType: deviceInfo.deviceType,
        capabilities: this.getDeviceCapabilities(deviceInfo),
        connectionStatus: 'discovered'
      };
      
      this.musicDevices.set(deviceInfo.deviceId, musicDevice);
      this.onMusicDeviceDiscovered(musicDevice);
    }
  }
  
  // 連接到音樂設備
  async connectToMusicDevice(deviceId: string): Promise<void> {
    const device = this.musicDevices.get(deviceId);
    if (!device) {
      throw new Error('Device not found');
    }
    
    try {
      // 先進行設備認證
      await this.authenticateDevice(deviceId);
      
      // 建立音樂傳輸通道
      await this.establishMusicChannel(deviceId);
      
      device.connectionStatus = 'connected';
      this.onMusicDeviceConnected(device);
      
    } catch (error) {
      device.connectionStatus = 'error';
      this.onMusicDeviceConnectionFailed(device, error);
      throw error;
    }
  }
  
  // 播放音樂到指定設備
  async playMusicToDevice(deviceId: string, musicData: MusicData): Promise<void> {
    const device = this.musicDevices.get(deviceId);
    if (!device || device.connectionStatus !== 'connected') {
      throw new Error('Device not connected');
    }
    
    try {
      // 傳輸音樂數據
      await this.transferMusicData(deviceId, musicData);
      
      // 發送播放命令
      await this.sendPlayCommand(deviceId);
      
      console.info(`Music playing on ${device.deviceName}`);
      
    } catch (error) {
      console.error('Music playback failed: ', error);
      throw error;
    }
  }
}

// 使用示例
const musicManager = new MusicDeviceManager();

// 初始化並開始發現設備
musicManager.initialize().then(() => {
  console.info('Music device manager ready');
});

六、性能優化與最佳實踐

6.1 發現性能優化

設備發現過程需要平衡功耗和發現效率。

// 智能發現策略
class SmartDiscoveryStrategy {
  private discoveryIntervals: Map<deviceManager.ExchangeFreq, number> = new Map([
    [deviceManager.ExchangeFreq.HIGH, 5000],    // 高頻:5秒
    [deviceManager.ExchangeFreq.NORMAL, 15000],  // 正常:15秒
    [deviceManager.ExchangeFreq.LOW, 30000]      // 低頻:30秒
  ]);
  
  // 根據應用狀態調整發現頻率
  adjustDiscoveryFrequency(appState: 'foreground' | 'background'): deviceManager.ExchangeFreq {
    switch (appState) {
      case 'foreground':
        return deviceManager.ExchangeFreq.HIGH;
      case 'background':
        return deviceManager.ExchangeFreq.LOW;
      default:
        return deviceManager.ExchangeFreq.NORMAL;
    }
  }
  
  // 設備發現限流
  throttleDeviceDiscovery(discoveryCallback: () => void): () => void {
    let lastCallTime = 0;
    const throttleDelay = 2000; // 2秒限流
    
    return () => {
      const now = Date.now();
      if (now - lastCallTime > throttleDelay) {
        lastCallTime = now;
        discoveryCallback();
      }
    };
  }
}

6.2 連接池管理

對於需要維護多個設備連接的應用,連接池管理至關重要。

// 設備連接池管理
class DeviceConnectionPool {
  private connections: Map<string, DeviceConnection> = new Map();
  private maxConnections = 5; // 最大連接數
  
  // 添加連接
  addConnection(deviceId: string, connection: DeviceConnection): boolean {
    if (this.connections.size >= this.maxConnections) {
      // 連接數已達上限,清理最不活躍的連接
      this.cleanupInactiveConnections();
    }
    
    if (this.connections.size < this.maxConnections) {
      this.connections.set(deviceId, connection);
      return true;
    }
    
    return false;
  }
  
  // 清理不活躍連接
  private cleanupInactiveConnections(): void {
    const now = Date.now();
    const inactiveThreshold = 5 * 60 * 1000; // 5分鐘
    
    for (const [deviceId, connection] of this.connections) {
      if (now - connection.lastActiveTime > inactiveThreshold) {
        connection.close();
        this.connections.delete(deviceId);
      }
    }
  }
  
  // 獲取活躍連接數
  getActiveConnectionCount(): number {
    let count = 0;
    for (const connection of this.connections.values()) {
      if (connection.isActive()) {
        count++;
      }
    }
    return count;
  }
}

七、常見問題與解決方案

7.1 設備發現失敗處理

// 設備發現故障排除
class DiscoveryTroubleshooter {
  // 檢查發現失敗原因
  diagnoseDiscoveryFailure(error: BusinessError): string {
    switch (error.code) {
      case 201: // 權限錯誤
        return '請檢查應用是否具有設備發現相關權限';
      case 202: // 設備不支持
        return '當前設備不支持發現功能';
      case 203: // 網絡不可用
        return '網絡連接不可用,請檢查網絡設置';
      case 204: // 參數錯誤
        return '發現參數配置錯誤';
      default:
        return `未知錯誤: ${error.code}`;
    }
  }
  
  // 自動重試機制
  async retryDiscoveryWithBackoff(
    discoveryFn: () => Promise<void>, 
    maxRetries: number = 3
  ): Promise<void> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        await discoveryFn();
        return; // 成功則返回
      } catch (error) {
        if (attempt === maxRetries) {
          throw error; // 最後一次嘗試仍然失敗
        }
        
        // 指數退避延遲
        const delay = Math.pow(2, attempt) * 1000;
        await this.delay(delay);
      }
    }
  }
}

總結

設備發現與連接是HarmonyOS分佈式能力的起點,為多設備協同提供了基礎支撐。通過本文的學習,你應該掌握了:

核心要點回顧:

  1. 設備發現機制:基於分佈式軟總線的自動發現能力,支持多種通信協議
  2. 安全認證流程:多重認證機制確保設備連接的安全性
  3. 連接狀態管理:完整的連接生命週期管理和異常處理
  4. 性能優化策略:智能發現頻率控制和連接池管理

行動建議:

  • 在應用初始化時合理配置設備發現參數,平衡功耗和發現效率
  • 實現完整的錯誤處理和重試機制,提升用户體驗
  • 遵循最小權限原則,只申請必要的設備訪問權限
  • 在適當的時機清理連接資源,避免內存泄漏

設備發現與連接技術的正確運用,將為你的HarmonyOS應用打開分佈式世界的大門,為用户創造真正無縫的多設備協同體驗。