河南平台网站建设制作,河南工程项目信息,旅游网络营销论文,网站 开发 外包欢迎加入开源鸿蒙跨平台社区#xff1a;https://openharmonycrossplatform.csdn.net #x1f4dd; 本文详细记录了 flutter_video_info 库适配 HarmonyOS#xff08;鸿蒙#xff09;平台的完整过程#xff0c;包括前置准备、项目导入、模板创建、代码实现、问题解决以及最…欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net 本文详细记录了flutter_video_info库适配 HarmonyOS鸿蒙平台的完整过程包括前置准备、项目导入、模板创建、代码实现、问题解决以及最佳实践。 目录一、前置准备二、项目导入与初始化三、项目结构分析四、适配步骤详解五、示例应用适配六、文档编写七、代码提交八、总结与注意事项一、前置准备1.1 环境要求在开始适配前请确保你的开发环境满足以下条件环境项要求Flutter SDK建议 3.27.及以上版本并配置好环境变量DevEco Studio已安装 OpenHarmony 开发工具并配置 OH SDKOpenHarmony 设备真机或模拟器且开启开发者模式AtomGit已配置代码仓库的访问权限1.2 验证环境# 检查 Flutter 版本flutter --version# 检查连接的设备flutter devices二、项目导入与初始化2.1 将项目托管到 AtomGit方式一Fork 原项目如果原项目在 GitHub 上可以通过 Fork 的方式导入登录 AtomGit 平台点击「导入仓库」或「Fork」输入原项目的 GitHub 地址如https://atomgit.com/oh-flutter/flutter_video_info选择目标组织和仓库名称点击确认方式二创建新仓库推荐使用Fork登录 AtomGit 平台创建新的代码仓库建议命名与插件名一致如flutter_video_info将本地待适配的 Flutter 插件代码推送到该仓库2.2 克隆代码到本地将 AtomGit 上的仓库克隆到本地开发环境# 替换为你的 AtomGit 仓库地址gitclone https://atomgit.com/[你的用户名]/flutter_video_info.git# 进入项目目录cdflutter_video_info# 建议创建 ohos-adapt 分支进行适配开发gitcheckout -b ohos-adapt2.3 初始化 OH 平台插件结构Flutter 插件默认不包含 OH 平台的代码结构需要通过命令初始化# 在插件根目录执行初始化 OH 平台的插件模板flutter create.--templateplugin --platformsohos命令参数说明参数说明.表示在当前目录执行--templateplugin指定创建插件模板--platformsohos仅生成 OH 平台相关的代码目录避免覆盖已有 iOS/Android 代码执行结果flutter_video_info/ ├── ohos/ # 自动生成的鸿蒙平台目录 │ ├── src/main/ets/ │ │ └── components/plugin/ │ │ └── FlutterVideoInfoPlugin.ets │ ├── module.json5 │ ├── oh-package.json5 │ ├── build-profile.json5 │ ├── hvigorfile.ts │ └── index.ets ├── example/ohos/ # 自动生成的鸿蒙示例项目 │ ├── AppScope/ │ ├── entry/ │ ├── oh-package.json5 │ └── ... └── pubspec.yaml # 已更新添加 ohos 平台配置2.4 更新依赖执行依赖更新命令确保 Flutter 侧能识别 OH 平台的配置flutter pub get验证 pubspec.yaml 配置flutter:plugin:platforms:android:package:com.example.flutter_video_infopluginClass:FlutterVideoInfoPluginios:pluginClass:FlutterVideoInfoPluginohos:# 自动添加的鸿蒙配置pluginClass:FlutterVideoInfoPlugin三、项目结构分析3.1 原有目录结构flutter_video_info/ ├── android/ # Android 平台实现 │ └── src/main/java/ │ └── FlutterVideoInfoPlugin.java ├── ios/ # iOS 平台实现 │ └── Classes/ │ └── SwiftFlutterVideoInfoPlugin.swift ├── lib/ # Dart 层代码 │ ├── flutter_video_info.dart │ └── flutter_video_info_platform_interface.dart ├── example/ # 示例应用 └── pubspec.yaml3.2 分析原有实现 Android 实现JavaAndroid 平台使用MediaMetadataRetriever类获取视频元数据// FlutterVideoInfoPlugin.javaMediaMetadataRetrievermediaRetrievernewMediaMetadataRetriever();mediaRetriever.setDataSource(context,Uri.fromFile(file));StringauthorgetData(MediaMetadataRetriever.METADATA_KEY_AUTHOR,mediaRetriever);StringmimeTypegetData(MediaMetadataRetriever.METADATA_KEY_MIMETYPE,mediaRetriever);StringwidthgetData(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH,mediaRetriever);StringheightgetData(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT,mediaRetriever);StringdurationgetData(MediaMetadataRetriever.METADATA_KEY_DURATION,mediaRetriever);// ... 更多字段 iOS 实现SwiftiOS 平台使用AVURLAsset获取视频元数据// SwiftFlutterVideoInfoPlugin.swiftletassetAVURLAsset(url:url)lettracksasset.tracks(withMediaType:.video)letfpstracks.first?.nominalFrameRateletsizetracks.first?.naturalSizeletdurationTimeround(CMTimeGetSeconds(asset.duration)*1000) Dart 层接口// flutter_video_info.dartclassFlutterVideoInfo{staticconstMethodChannel_channelMethodChannel(flutter_video_info);FutureVideoData?getVideoInfo(Stringpath)async{finaljsonStrawait_channel.invokeMethod(getVidInfo,{path:path});finaljsonMapjson.decode(jsonStr);returnVideoData.fromJson(jsonMap);}}关键发现 使用 MethodChannel 进行平台通信 方法名为getVidInfo 参数为path视频文件路径 返回 JSON 字符串格式四、适配步骤详解4.1 实现核心功能 初始实现第一版根据 Android 实现使用鸿蒙的AVMetadataRetrieverimport{AVMetadataRetriever}fromohos.multimedia.media;// ❌ 错误导入方式不正确遇到问题编译报错Module has no exported member AVMetadataRetriever 修正实现第二版查阅鸿蒙 API 文档后发现正确用法import{media}fromkit.MediaKit;import{fileIoasfs}fromkit.CoreFileKit;exportdefaultclassFlutterVideoInfoPluginimplementsFlutterPlugin,MethodCallHandler{privatechannel:MethodChannel|nullnull;onAttachedToEngine(binding:FlutterPluginBinding):void{this.channelnewMethodChannel(binding.getBinaryMessenger(),flutter_video_info);this.channel.setMethodCallHandler(this);}asynconMethodCall(call:MethodCall,result:MethodResult):Promisevoid{if(call.methodgetVidInfo){letpathcall.argument(path)asstring;letjsonStrawaitthis.getVidInfo(path);result.success(jsonStr);}else{result.notImplemented();}}asyncgetVidInfo(path:string):Promisestring{// 实现细节...}}遇到问题AVMetadata属性名与预期不符 最终实现第三版根据鸿蒙 API 文档AVMetadata的正确属性Android 属性鸿蒙 AVMetadata 属性METADATA_KEY_MIMETYPEmimeTypeMETADATA_KEY_AUTHORartistMETADATA_KEY_VIDEO_WIDTHvideoWidthMETADATA_KEY_VIDEO_HEIGHTvideoHeightMETADATA_KEY_DURATIONduration完整实现代码import{FlutterPlugin,FlutterPluginBinding,MethodCall,MethodCallHandler,MethodChannel,MethodResult,}fromohos/flutter_ohos;import{media}fromkit.MediaKit;import{fileIoasfs}fromkit.CoreFileKit;exportdefaultclassFlutterVideoInfoPluginimplementsFlutterPlugin,MethodCallHandler{privatechannel:MethodChannel|nullnull;constructor(){}getUniqueClassName():string{returnFlutterVideoInfoPlugin;}onAttachedToEngine(binding:FlutterPluginBinding):void{this.channelnewMethodChannel(binding.getBinaryMessenger(),flutter_video_info);this.channel.setMethodCallHandler(this);}onDetachedFromEngine(binding:FlutterPluginBinding):void{if(this.channel!null){this.channel.setMethodCallHandler(null);}}asynconMethodCall(call:MethodCall,result:MethodResult):Promisevoid{if(call.methodgetVidInfo){letpathcall.argument(path)asstring;letjsonStrawaitthis.getVidInfo(path);result.success(jsonStr);}else{result.notImplemented();}}asyncgetVidInfo(path:string):Promisestring{letisFileExistfalse;letmimetype;letauthor;letdateStr;letwidth;letheight;letlocation;letframerate;letduration;letfilesize:number0;letorientation;try{// 检查文件是否存在并获取文件大小letstatfs.statSync(path);isFileExisttrue;filesizestat.size;// 创建元数据提取器letavMetadataExtractor:media.AVMetadataExtractorawaitmedia.createAVMetadataExtractor();try{// 打开文件letfilefs.openSync(path,fs.OpenMode.READ_ONLY);// 设置文件描述符avMetadataExtractor.fdSrcfile;// 获取元数据letmetadata:media.AVMetadataawaitavMetadataExtractor.fetchMetadata();if(metadata){mimetypemetadata.mimeType??;authormetadata.artist??;widthmetadata.videoWidth?.toString()??;heightmetadata.videoHeight?.toString()??;durationmetadata.duration?.toString()??;}// 关闭文件fs.closeSync(file);}catch(e){console.error(AVMetadataExtractor error: e);}finally{// 释放资源awaitavMetadataExtractor.release();}}catch(e){console.error(File access error: e);isFileExistfalse;}// 构建 JSON 返回结果letjsonObj:Recordstring,Object{path:path,mimetype:mimetype,author:author,date:dateStr,width:width,height:height,location:location,framerate:framerate,duration:duration,filesize:filesize,orientation:orientation,isfileexist:isFileExist};returnJSON.stringify(jsonObj);}}4.2 配置权限⚠️ 遇到的问题编译时报错Error: The reason and usedScene attributes are mandatory for user_grant permissions.原因分析ohos.permission.READ_MEDIA是用户授权权限必须包含reason和usedScene属性。✅ 解决方案步骤 1在示例应用的module.json5中配置权限// example/ohos/entry/src/main/module.json5 { module: { requestPermissions: [ { name: ohos.permission.INTERNET }, { name: ohos.permission.READ_MEDIA, reason: $string:read_media_reason, usedScene: { abilities: [EntryAbility], when: inuse } } ] } }步骤 2添加权限说明字符串// example/ohos/entry/src/main/resources/base/element/string.json{string:[{name:module_desc,value:module description},{name:EntryAbility_desc,value:description},{name:EntryAbility_label,value:flutter_video_info_example},{name:read_media_reason,value:用于读取视频文件信息}]}注意HAR 模块本身不能声明权限权限需要在宿主应用中声明。4.3 解决编译错误错误 1导入方式错误Error: Module ohos.multimedia.media has no exported member AVMetadataRetriever解决方案使用正确的导入方式// ❌ 错误import{AVMetadataRetriever}fromohos.multimedia.media;// ✅ 正确import{media}fromkit.MediaKit;letextractorawaitmedia.createAVMetadataExtractor();错误 2使用 any 类型Error: Use explicit types instead of any, unknown (arkts-no-any-unknown)解决方案为所有变量指定明确类型// ❌ 错误letmetadataawaitavMetadataExtractor.fetchMetadata();// ✅ 正确letmetadata:media.AVMetadataawaitavMetadataExtractor.fetchMetadata();错误 3属性不存在Error: Property date does not exist on type AVMetadata Error: Property videoFrameRate does not exist on type AVMetadata解决方案使用正确的属性名部分属性鸿蒙暂不支持// ❌ 错误dateStrmetadata.date;frameratemetadata.videoFrameRate;// ✅ 正确 - 部分属性暂不支持返回空字符串dateStr;framerate;五、示例应用适配5.1 配置 OH 示例项目签名为了真机调试需要对example/ohos目录下的示例项目进行签名配置用 DevEco Studio 打开example/ohos项目点击菜单栏「File」→「Project Structure」→「Modules」→「ohos」→「Signing Configs」选择已有的签名证书若无则创建配置签名信息同步项目Sync Now确保签名配置生效5.2 添加网络视频测试功能由于本地视频路径难以测试添加网络视频下载功能// example/lib/main.dartimportdart:io;importpackage:dio/dio.dart;finalListStringsampleVideoUrls[https://www.w3schools.com/html/mov_bbb.mp4,https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4,// ... 更多示例视频];FutureStringdownloadVideo(Stringurl)async{finalfileNametest_video_${DateTime.now().millisecondsSinceEpoch}.mp4;finaltempDirDirectory.systemTemp;// 使用系统临时目录finalfilePath${tempDir.path}/$fileName;finaldioDio();awaitdio.download(url,filePath);returnfilePath;}5.3 遇到的问题MissingPluginException: No implementation found for method getApplicationDocumentsDirectory原因path_provider插件未适配鸿蒙解决方案使用dart:io的Directory.systemTemp替代// ❌ 错误 - path_provider 未适配鸿蒙finalappDirawaitgetApplicationDocumentsDirectory();// ✅ 正确 - 使用 dart:io 内置方法finaltempDirDirectory.systemTemp;5.4 更新 pubspec.yamldependencies:flutter:sdk:flutterpermission_handler:^11.3.1dio:^5.4.0# 用于下载网络视频cupertino_icons:^1.0.65.5 真机调试将 OH 真机连接到电脑执行调试命令# 在插件根目录执行运行 OH 示例项目flutter run -d[你的设备ID]--verbose调试要点观察终端输出排查代码语法错误、API 调用错误等问题在真机上验证核心功能是否与 iOS/Android 端表现一致反复调试修复兼容性问题六、文档编写6.1 创建中英文 README适配完成后需要新增文档说明 OH 平台的使用方式文档说明README.OpenHarmony_CN.md中文使用文档README.OpenHarmony.md英文使用文档七、代码提交确认所有功能正常、文档完整后执行代码提交操作# 添加适配相关的文件gitaddohos/# OH 平台核心代码gitaddexample/ohos/# OH 示例项目gitaddREADME_CN.md README_EN.md ADAPTATION_GUIDE.md# 适配文档gitaddpubspec.yaml# 平台配置文件gitaddlib/# 若 Flutter 侧代码有改动需添加# 提交代码gitcommit -mohoh适配# 推送到 AtomGit 仓库gitpush origin ohos-adapt八、总结与注意事项8.1 关键技术点技术点AndroidiOSHarmonyOS元数据提取类MediaMetadataRetrieverAVURLAssetAVMetadataExtractor导入方式原生 Java原生 Swiftimport { media } from kit.MediaKit文件操作java.io.FileFileManagerimport { fileIo } from kit.CoreFileKit