React Native鸿蒙开发实战(六):原生能力调用与设备API

React Native鸿蒙开发实战(六):原生能力调用与设备API

一、引言

在前五篇系列文章中,我们已经完成了React Native鸿蒙开发的基础环境搭建、组件布局、状态管理、路由导航和网络请求等核心内容的学习。掌握了这些知识后,我们已经能够构建功能完整的跨平台应用。然而,真正让应用具备"原生感"的关键,在于如何充分利用设备硬件能力和系统服务。

从第5篇的网络请求过渡到本篇,我们可以思考:网络请求获取的数据如何与设备硬件结合?比如,获取用户位置信息后展示在地图上,或者调用相机拍摄照片后上传到服务器。这正是原生能力调用的核心价值所在。

二、TurboModule与鸿蒙原生模块通信机制

2.1 TurboModule架构原理

TurboModule是React Native新架构中用于JavaScript与原生代码交互的核心机制。在鸿蒙平台上,TurboModule分为两种类型:

cxxTurboModule:主要用于不需要系统参与的能力,如数据计算、动画等,直接在C++侧实现以提高性能。

ArkTSTurboModule:提供调用ArkTS原生API的方法,依赖NAPI进行原生代码与C++侧的通信,支持同步和异步两种调用方式。

2.2 原生模块创建与注册

在鸿蒙DevEco Studio中创建原生模块:

复制代码
// SystemTurboModule.ets
import { TurboModule } from '@rnoh/react-native-openharmony'

export class SystemTurboModule extends TurboModule {
  // 获取设备信息
  getDeviceInfo(): Promise<object> {
    return Promise.resolve({
      brand: 'HUAWEI',
      model: 'Mate 60 Pro',
      systemVersion: 'HarmonyOS 5.0'
    })
  }

  // 检查权限
  checkPermission(permission: string): Promise<boolean> {
    return Promise.resolve(true)
  }
}

oh-package.json5中注册模块:

复制代码
{
  "name": "@rnoh/system-turbo-module",
  "main": "SystemTurboModule.ets",
  "types": "./index.d.ts",
  "dependencies": {
    "@rnoh/react-native-openharmony": "^0.72.86"
  }
}

2.3 JS端调用原生模块

在React Native组件中调用:

复制代码
import { NativeModules } from 'react-native'

const { SystemTurboModule } = NativeModules

// 获取设备信息
const getDeviceInfo = async () => {
  try {
    const info = await SystemTurboModule.getDeviceInfo()
    console.log('设备信息:', info)
  } catch (error) {
    console.error('获取设备信息失败:', error)
  }
}

// 检查相机权限
const checkCameraPermission = async () => {
  const hasPermission = await SystemTurboModule.checkPermission('CAMERA')
  return hasPermission
}

三、设备硬件API调用

3.1 相机与相册

相机调用示例

复制代码
import { NativeModules, Platform } from 'react-native'

const { CameraModule } = NativeModules

// 拍照
const takePhoto = async () => {
  try {
    const photo = await CameraModule.takePhoto({
      quality: 0.8,
      saveToPhotos: true
    })
    return photo
  } catch (error) {
    console.error('拍照失败:', error)
  }
}

// 选择相册图片
const pickImage = async () => {
  try {
    const image = await CameraModule.pickImage({
      allowsEditing: true,
      aspect: [4, 3]
    })
    return image
  } catch (error) {
    console.error('选择图片失败:', error)
  }
}

3.2 定位服务

获取当前位置

复制代码
import { NativeModules } from 'react-native'

const { LocationModule } = NativeModules

// 获取当前位置
const getCurrentLocation = async () => {
  try {
    const location = await LocationModule.getCurrentLocation({
      enableHighAccuracy: true,
      timeout: 10000,
      maximumAge: 60000
    })
    return location
  } catch (error) {
    console.error('获取位置失败:', error)
  }
}

// 监听位置变化
const watchLocation = (callback) => {
  LocationModule.watchPosition((location) => {
    callback(location)
  })
}

// 停止监听
const clearWatch = () => {
  LocationModule.clearWatch()
}

3.3 传感器数据

加速度计监听

复制代码
import { NativeModules } from 'react-native'

const { SensorManager } = NativeModules

// 启动加速度计监听
SensorManager.startAccelerometer((data) => {
  console.log('加速度数据:', data.x, data.y, data.z)
})

// 停止监听
SensorManager.stopAccelerometer()

陀螺仪监听

复制代码
// 启动陀螺仪监听
SensorManager.startGyroscope((data) => {
  console.log('陀螺仪数据:', data.x, data.y, data.z)
})

// 停止监听
SensorManager.stopGyroscope()

3.4 蓝牙功能

蓝牙设备扫描与连接

复制代码
import { NativeModules, DeviceEventEmitter } from 'react-native'

const { BluetoothModule } = NativeModules

// 启动蓝牙扫描
const startScan = () => {
  BluetoothModule.startScan()
}

// 监听设备发现
DeviceEventEmitter.addListener('BluetoothDeviceFound', (device) => {
  console.log('发现设备:', device)
})

// 连接设备
const connectDevice = async (deviceId) => {
  try {
    await BluetoothModule.connectDevice(deviceId)
    console.log('设备连接成功')
  } catch (error) {
    console.error('设备连接失败:', error)
  }
}

// 发送数据
const sendData = async (data) => {
  try {
    await BluetoothModule.sendData(data)
    console.log('数据发送成功')
  } catch (error) {
    console.error('数据发送失败:', error)
  }
}

3.5 NFC功能

NFC读写操作

复制代码
import { NativeModules } from 'react-native'

const { NFCModule } = NativeModules

// 监听NFC标签
const startNFCScan = () => {
  NFCModule.startScan()
}

// 停止监听
const stopNFCScan = () => {
  NFCModule.stopScan()
}

// 写入NFC标签
const writeToNFCTag = async (data) => {
  try {
    await NFCModule.writeToTag(data)
    console.log('写入成功')
  } catch (error) {
    console.error('写入失败:', error)
  }
}

四、鸿蒙文件系统与分布式数据管理

4.1 本地文件操作

文件读写示例

复制代码
import { NativeModules } from 'react-native'

const { FileSystemModule } = NativeModules

// 写入文件
const writeFile = async (path, content) => {
  try {
    await FileSystemModule.writeFile(path, content)
    console.log('文件写入成功')
  } catch (error) {
    console.error('文件写入失败:', error)
  }
}

// 读取文件
const readFile = async (path) => {
  try {
    const content = await FileSystemModule.readFile(path)
    return content
  } catch (error) {
    console.error('文件读取失败:', error)
  }
}

// 删除文件
const deleteFile = async (path) => {
  try {
    await FileSystemModule.deleteFile(path)
    console.log('文件删除成功')
  } catch (error) {
    console.error('文件删除失败:', error)
  }
}

4.2 分布式数据管理

鸿蒙的分布式数据管理能力是平台的核心特色,支持多设备间的数据同步和共享。

分布式文件同步

复制代码
import { NativeModules } from 'react-native'

const { DistributedDataModule } = NativeModules

// 写入分布式数据
const writeDistributedData = async (key, value) => {
  try {
    await DistributedDataModule.put(key, value)
    console.log('数据写入成功')
  } catch (error) {
    console.error('数据写入失败:', error)
  }
}

// 读取分布式数据
const readDistributedData = async (key) => {
  try {
    const value = await DistributedDataModule.get(key)
    return value
  } catch (error) {
    console.error('数据读取失败:', error)
  }
}

// 监听数据变化
DistributedDataModule.addDataChangeListener(({ key, value }) => {
  console.log(`数据变化: ${key} = ${value}`)
})

跨设备文件访问

复制代码
// 获取分布式目录
const getDistributedDir = async () => {
  try {
    const dir = await DistributedDataModule.getDistributedDir()
    return dir
  } catch (error) {
    console.error('获取分布式目录失败:', error)
  }
}

// 跨设备文件读写
const readCrossDeviceFile = async (deviceId, filePath) => {
  try {
    const content = await DistributedDataModule.readCrossDeviceFile(deviceId, filePath)
    return content
  } catch (error) {
    console.error('跨设备文件读取失败:', error)
  }
}

五、权限配置与安全注意事项

5.1 权限声明配置

module.json5中声明所需权限:

复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "需要相机权限以拍摄照片"
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "需要位置权限以获取用户位置"
      },
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "需要读取媒体文件权限以访问相册"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "需要分布式数据同步权限"
      }
    ]
  }
}

5.2 动态权限申请

权限检查与申请

复制代码
import { NativeModules, Alert } from 'react-native'

const { PermissionModule } = NativeModules

// 检查权限
const checkPermission = async (permission) => {
  try {
    const hasPermission = await PermissionModule.checkPermission(permission)
    return hasPermission
  } catch (error) {
    console.error('检查权限失败:', error)
    return false
  }
}

// 申请权限
const requestPermission = async (permission) => {
  try {
    const result = await PermissionModule.requestPermission(permission)
    return result
  } catch (error) {
    console.error('申请权限失败:', error)
    return false
  }
}

// 检查并申请权限(推荐使用)
const checkAndRequestPermission = async (permission) => {
  const hasPermission = await checkPermission(permission)
  if (!hasPermission) {
    const granted = await requestPermission(permission)
    if (!granted) {
      Alert.alert('权限申请', '需要授予权限才能使用该功能', [
        { text: '取消', style: 'cancel' },
        { text: '去设置', onPress: () => openSettings() }
      ])
      return false
    }
  }
  return true
}

5.3 安全最佳实践

  1. 最小权限原则:只申请应用真正需要的权限
  2. 按需申请:在需要使用功能时才申请对应权限
  3. 权限说明:在权限申请弹窗中清晰说明权限用途
  4. 权限拒绝处理:优雅处理用户拒绝权限的情况
  5. 数据加密:敏感数据使用鸿蒙分布式密钥管理进行加密存储

六、性能优化与错误处理

6.1 性能优化策略

图片懒加载优化

复制代码
import { Image } from 'react-native'

const LazyImage = ({ uri, style }) => {
  const [isLoading, setIsLoading] = useState(true)
  const [hasError, setHasError] = useState(false)

  return (
    <View style={style}>
      {isLoading && <ActivityIndicator size="small" />}
      {hasError && <Text>图片加载失败</Text>}
      <Image
        source={{ uri }}
        style={[style, { display: isLoading || hasError ? 'none' : 'flex' }]}
        onLoad={() => setIsLoading(false)}
        onError={() => {
          setIsLoading(false)
          setHasError(true)
        }}
      />
    </View>
  )
}

列表性能优化

复制代码
import { FlatList } from 'react-native'

const OptimizedList = ({ data }) => {
  const renderItem = useCallback(({ item }) => (
    <ListItem item={item} />
  ), [])

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      initialNumToRender={10}
      maxToRenderPerBatch={10}
      windowSize={5}
      removeClippedSubviews={true}
    />
  )
}

6.2 错误处理机制

全局错误边界

复制代码
import React from 'react'

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = { hasError: false }
  }

  static getDerivedStateFromError(error) {
    return { hasError: true }
  }

  componentDidCatch(error, errorInfo) {
    console.error('组件错误:', error, errorInfo)
    // 上报错误到监控平台
  }

  render() {
    if (this.state.hasError) {
      return (
        <View style={styles.errorContainer}>
          <Text>抱歉,应用出现异常</Text>
          <Button title="重试" onPress={() => this.setState({ hasError: false })} />
        </View>
      )
    }

    return this.props.children
  }
}

网络请求错误处理

复制代码
const fetchWithRetry = async (url, options = {}, maxRetries = 3) => {
  let retries = 0
  
  while (retries < maxRetries) {
    try {
      const response = await fetch(url, options)
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      return await response.json()
    } catch (error) {
      retries++
      if (retries === maxRetries) {
        throw error
      }
      await new Promise(resolve => setTimeout(resolve, 1000 * retries))
    }
  }
}

七、实战案例:设备能力综合应用

7.1 场景描述

构建一个智能家居控制应用,实现以下功能:

  • 获取设备位置信息
  • 调用相机拍摄家庭照片
  • 使用蓝牙连接智能设备
  • 通过NFC快速配对
  • 分布式数据同步到其他设备

7.2 核心代码实现

主应用组件

复制代码
import React, { useState, useEffect } from 'react'
import { View, Text, Button, StyleSheet, Alert } from 'react-native'
import { NativeModules, DeviceEventEmitter } from 'react-native'

const { LocationModule, CameraModule, BluetoothModule, NFCModule, DistributedDataModule } = NativeModules

const SmartHomeApp = () => {
  const [location, setLocation] = useState(null)
  const [photo, setPhoto] = useState(null)
  const [devices, setDevices] = useState([])
  const [connectedDevice, setConnectedDevice] = useState(null)

  useEffect(() => {
    // 监听设备发现
    DeviceEventEmitter.addListener('BluetoothDeviceFound', (device) => {
      setDevices(prev => [...prev, device])
    })

    // 监听NFC标签
    DeviceEventEmitter.addListener('NFCTagDetected', (tag) => {
      handleNFCTag(tag)
    })

    return () => {
      DeviceEventEmitter.removeAllListeners('BluetoothDeviceFound')
      DeviceEventEmitter.removeAllListeners('NFCTagDetected')
    }
  }, [])

  // 获取当前位置
  const getLocation = async () => {
    try {
      const currentLocation = await LocationModule.getCurrentLocation()
      setLocation(currentLocation)
      
      // 同步到分布式数据
      await DistributedDataModule.put('last_location', currentLocation)
    } catch (error) {
      Alert.alert('错误', '获取位置失败')
    }
  }

  // 拍照
  const takePhoto = async () => {
    try {
      const photoData = await CameraModule.takePhoto()
      setPhoto(photoData)
      
      // 同步照片到其他设备
      await DistributedDataModule.put('last_photo', photoData)
    } catch (error) {
      Alert.alert('错误', '拍照失败')
    }
  }

  // 扫描蓝牙设备
  const scanDevices = () => {
    BluetoothModule.startScan()
    setTimeout(() => {
      BluetoothModule.stopScan()
    }, 10000)
  }

  // 连接设备
  const connectDevice = async (deviceId) => {
    try {
      await BluetoothModule.connectDevice(deviceId)
      setConnectedDevice(deviceId)
    } catch (error) {
      Alert.alert('错误', '设备连接失败')
    }
  }

  // 处理NFC标签
  const handleNFCTag = (tag) => {
    // 根据NFC标签内容执行不同操作
    if (tag.type === 'device_pairing') {
      connectDevice(tag.deviceId)
    }
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>智能家居控制</Text>
      
      <Button title="获取位置" onPress={getLocation} />
      {location && (
        <Text>位置: {location.latitude}, {location.longitude}</Text>
      )}
      
      <Button title="拍照" onPress={takePhoto} />
      {photo && (
        <Image source={{ uri: photo.uri }} style={styles.photo} />
      )}
      
      <Button title="扫描设备" onPress={scanDevices} />
      {devices.map(device => (
        <View key={device.id} style={styles.deviceItem}>
          <Text>{device.name}</Text>
          <Button title="连接" onPress={() => connectDevice(device.id)} />
        </View>
      ))}
      
      {connectedDevice && (
        <Text>已连接设备: {connectedDevice}</Text>
      )}
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20
  },
  photo: {
    width: 200,
    height: 200,
    marginTop: 10
  },
  deviceItem: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: 10,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc'
  }
})

export default SmartHomeApp

7.3 权限配置

module.json5中配置所有需要的权限:

复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "需要相机权限以拍摄家庭照片"
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "需要位置权限以获取家庭位置"
      },
      {
        "name": "ohos.permission.BLUETOOTH",
        "reason": "需要蓝牙权限以连接智能设备"
      },
      {
        "name": "ohos.permission.NFC",
        "reason": "需要NFC权限以快速配对设备"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "需要分布式数据同步权限以同步数据到其他设备"
      }
    ]
  }
}

八、总结

8.1 核心要点回顾

通过本篇的学习,我们掌握了React Native在鸿蒙平台上调用原生能力的完整流程:

  1. TurboModule机制:理解了cxxTurboModule和ArkTSTurboModule的区别与适用场景
  2. 设备硬件API:学会了调用相机、定位、传感器、蓝牙、NFC等设备能力
  3. 文件系统:掌握了本地文件操作和分布式数据管理
  4. 权限管理:了解了权限声明、动态申请和安全最佳实践
  5. 性能优化:学习了图片懒加载、列表优化等性能提升技巧
  6. 错误处理:掌握了全局错误边界和网络请求重试机制

8.2 常见问题解决方案

问题1:权限申请被拒绝

  • 解决方案:在申请权限时提供清晰的用途说明,并引导用户到设置页面开启权限

问题2:设备API调用失败

  • 解决方案:检查设备是否支持该功能,添加错误处理机制,提供友好的用户提示

问题3:分布式数据同步失败

  • 解决方案:检查设备是否登录同一账号,网络连接是否正常,权限是否已授予

问题4:性能问题

  • 解决方案:使用懒加载、虚拟化列表、图片压缩等技术优化性能

8.3 下篇预告

在下一篇《React Native鸿蒙开发实战(七):性能优化与调试技巧》中,我们将深入探讨:

  • 性能监控工具:使用DevEco Profiler监控应用性能
  • 内存优化:内存泄漏检测与修复
  • 启动优化:冷启动、热启动优化策略
  • 渲染优化:减少重渲染、列表优化技巧
  • 调试技巧:真机调试、日志分析、崩溃追踪

掌握这些性能优化技巧,将帮助您构建更加流畅、稳定的鸿蒙应用,为用户提供更好的使用体验。

相关推荐
L、21819 小时前
统一日志与埋点系统:在 Flutter + OpenHarmony 混合架构中实现全链路可观测性
javascript·华为·智能手机·electron·harmonyos
WindStormrage19 小时前
umi3 → umi4 升级:踩坑与解决方案
前端·react.js·cursor
学习非暴力沟通的程序员20 小时前
从全局主题控制拆解 React 核心 Hook:useContext、useState、useMemo
react.js
leonwgc21 小时前
🎯 AI 写代码?我用 MCP 让 Copilot 秒变全栈工程师!
react.js·ai编程
markyankee10121 小时前
🌟 React useState 深入理解与最佳实践
react.js
冰冷的bin21 小时前
【React Native】渐变骨架屏组件SkeletonElement
react native
hefengbao1 天前
『京墨文库』鸿蒙版上线!
harmonyos·arkts·arkui·arkdata
赵浩生1 天前
鸿蒙技术干货6:鸿蒙权限管理与后台任务开发指南(下)
harmonyos
赵浩生1 天前
鸿蒙技术干货5:鸿蒙权限管理与后台任务开发指南(上)
harmonyos
wordbaby1 天前
React Native 安全指南:如何打造“防弹”应用
前端·react native