native-integration.mdc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. ---
  2. description: 原生模块集成指南
  3. globs: ["**/cpp/**/*", "**/napi/**/*", "**/ijkplayer/**/*", "**/lib/**/*"]
  4. alwaysApply: false
  5. ---
  6. # 原生模块集成指南
  7. ## ijkplayer播放器集成
  8. ### 基本使用
  9. ```typescript
  10. import { IjkMediaPlayer } from '@ohos/ijkplayer';
  11. // 创建播放器实例
  12. private player: IjkMediaPlayer = new IjkMediaPlayer();
  13. // 设置数据源
  14. async setDataSource(path: string) {
  15. try {
  16. await this.player.setDataSource(path);
  17. await this.player.prepare();
  18. } catch (err: Error) {
  19. Logger.error(`设置数据源失败: ${err.message}`);
  20. }
  21. }
  22. ```
  23. ### 播放控制
  24. ```typescript
  25. // 播放
  26. async play() {
  27. try {
  28. await this.player.start();
  29. this.playStatus = PlayStatus.PLAY;
  30. } catch (err: Error) {
  31. Logger.error(`播放失败: ${err.message}`);
  32. }
  33. }
  34. // 暂停
  35. async pause() {
  36. try {
  37. await this.player.pause();
  38. this.playStatus = PlayStatus.PAUSE;
  39. } catch (err: Error) {
  40. Logger.error(`暂停失败: ${err.message}`);
  41. }
  42. }
  43. // 停止
  44. async stop() {
  45. try {
  46. await this.player.stop();
  47. this.playStatus = PlayStatus.STOP;
  48. } catch (err: Error) {
  49. Logger.error(`停止失败: ${err.message}`);
  50. }
  51. }
  52. ```
  53. ### 播放状态监听
  54. ```typescript
  55. // 设置播放状态监听器
  56. setupPlayerListener() {
  57. this.player.on('stateChange', (state: string) => {
  58. switch (state) {
  59. case 'prepared':
  60. // 准备完成
  61. break;
  62. case 'playing':
  63. this.playStatus = PlayStatus.PLAY;
  64. break;
  65. case 'paused':
  66. this.playStatus = PlayStatus.PAUSE;
  67. break;
  68. case 'completed':
  69. this.playStatus = PlayStatus.DONE;
  70. this.playNext(); // 播放下一首
  71. break;
  72. case 'error':
  73. this.handlePlaybackError(new Error('播放器错误'));
  74. break;
  75. }
  76. });
  77. this.player.on('timeUpdate', (currentTime: number, duration: number) => {
  78. this.currentTime = currentTime;
  79. this.duration = duration;
  80. this.updateProgress();
  81. });
  82. }
  83. ```
  84. ### 音频焦点处理
  85. ```typescript
  86. import { avSession } from '@kit.ArkAVSessionKit';
  87. // 请求音频焦点
  88. async requestAudioFocus() {
  89. try {
  90. const audioSession = await avSession.createAVSession(getContext(this), 'audio', 'music');
  91. await audioSession.activate();
  92. this.audioSession = audioSession;
  93. } catch (err: Error) {
  94. Logger.error(`获取音频焦点失败: ${err.message}`);
  95. }
  96. }
  97. // 释放音频焦点
  98. async releaseAudioFocus() {
  99. if (this.audioSession) {
  100. try {
  101. await this.audioSession.deactivate();
  102. await this.audioSession.destroy();
  103. this.audioSession = null;
  104. } catch (err: Error) {
  105. Logger.error(`释放音频焦点失败: ${err.message}`);
  106. }
  107. }
  108. }
  109. ```
  110. ## 歌词库集成
  111. ### LyricHelper使用
  112. ```typescript
  113. import { LyricHelper } from '@lib/LyricHelper';
  114. // 解析歌词文件
  115. async parseLyricFile(filePath: string): Promise<LyricLine[]> {
  116. try {
  117. const lyrics = await LyricHelper.parseLyricFile(filePath);
  118. return lyrics;
  119. } catch (err: Error) {
  120. Logger.error(`解析歌词文件失败: ${err.message}`);
  121. return [];
  122. }
  123. }
  124. // 解析歌词文本
  125. parseLyricText(lyricText: string): LyricLine[] {
  126. try {
  127. return LyricHelper.parseLyricText(lyricText);
  128. } catch (err: Error) {
  129. Logger.error(`解析歌词文本失败: ${err.message}`);
  130. return [];
  131. }
  132. }
  133. ```
  134. ### 歌词同步
  135. ```typescript
  136. // 获取当前时间对应的歌词行
  137. getCurrentLyric(currentTime: number): LyricLine | null {
  138. if (!this.lyrics || this.lyrics.length === 0) {
  139. return null;
  140. }
  141. for (let i = 0; i < this.lyrics.length; i++) {
  142. if (this.lyrics[i].time > currentTime) {
  143. return i > 0 ? this.lyrics[i - 1] : null;
  144. }
  145. }
  146. return this.lyrics[this.lyrics.length - 1];
  147. }
  148. // 获取下一句歌词
  149. getNextLyric(currentTime: number): LyricLine | null {
  150. if (!this.lyrics || this.lyrics.length === 0) {
  151. return null;
  152. }
  153. for (let i = 0; i < this.lyrics.length; i++) {
  154. if (this.lyrics[i].time > currentTime) {
  155. return this.lyrics[i];
  156. }
  157. }
  158. return null;
  159. }
  160. ```
  161. ## NAPI开发指南
  162. ### 基本NAPI模块结构
  163. ```cpp
  164. // napi_init.cpp
  165. #include "napi/native_api.h"
  166. static napi_value Init(napi_env env, napi_value exports) {
  167. // 导出函数
  168. napi_property_descriptor desc[] = {
  169. {"createPlayer", nullptr, CreatePlayer, nullptr, nullptr, nullptr, napi_default, nullptr},
  170. {"destroyPlayer", nullptr, DestroyPlayer, nullptr, nullptr, nullptr, napi_default, nullptr},
  171. };
  172. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  173. return exports;
  174. }
  175. static napi_module demoModule = {
  176. .nm_version = 1,
  177. .nm_flags = 0,
  178. .nm_filename = nullptr,
  179. .nm_register_func = Init,
  180. .nm_modname = "entry",
  181. .nm_priv = ((void*)0),
  182. .reserved = {0},
  183. };
  184. extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
  185. napi_module_register(&demoModule);
  186. }
  187. ```
  188. ### 异步操作处理
  189. ```cpp
  190. // 异步操作结构体
  191. struct AsyncData {
  192. napi_async_work work;
  193. napi_deferred deferred;
  194. napi_ref callback;
  195. std::string result;
  196. std::string error;
  197. };
  198. // 异步执行函数
  199. static void ExecuteCallback(napi_env env, void* data) {
  200. AsyncData* asyncData = (AsyncData*)data;
  201. try {
  202. // 执行耗时操作
  203. asyncData->result = performOperation();
  204. } catch (const std::exception& e) {
  205. asyncData->error = e.what();
  206. }
  207. }
  208. // 完成回调函数
  209. static void CompleteCallback(napi_env env, napi_status status, void* data) {
  210. AsyncData* asyncData = (AsyncData*)data;
  211. napi_value callback;
  212. napi_get_reference_value(env, asyncData->callback, &callback);
  213. napi_value result;
  214. if (asyncData->error.empty()) {
  215. napi_create_string_utf8(env, asyncData->result.c_str(), NAPI_AUTO_LENGTH, &result);
  216. napi_call_function(env, nullptr, callback, 1, &result, nullptr);
  217. } else {
  218. napi_value error;
  219. napi_create_string_utf8(env, asyncData->error.c_str(), NAPI_AUTO_LENGTH, &error);
  220. napi_call_function(env, nullptr, callback, 1, &error, nullptr);
  221. }
  222. // 清理资源
  223. napi_delete_async_work(env, asyncData->work);
  224. napi_delete_reference(env, asyncData->callback);
  225. delete asyncData;
  226. }
  227. ```
  228. ## 性能优化建议
  229. ### 播放器优化
  230. 1. 使用对象池管理播放器实例,避免频繁创建和销毁
  231. 2. 预加载下一首歌曲,减少切换歌曲时的延迟
  232. 3. 使用硬件解码加速,降低CPU占用
  233. 4. 合理设置缓冲区大小,平衡播放流畅度和内存占用
  234. ### 内存管理
  235. 1. 及时释放不再使用的资源,如播放器实例、音频会话等
  236. 2. 使用弱引用避免循环引用导致的内存泄漏
  237. 3. 监控内存使用情况,及时处理内存警告
  238. ### 线程管理
  239. 1. 将耗时操作放在工作线程中执行,避免阻塞UI线程
  240. 2. 使用线程池管理并发任务,避免创建过多线程
  241. 3. 合理使用同步机制,避免死锁和竞态条件
  242. ## 错误处理
  243. ### 播放器错误处理
  244. ```typescript
  245. // 播放器错误处理
  246. handlePlayerError(error: Error) {
  247. Logger.error(`播放器错误: ${error.message}`);
  248. // 根据错误类型采取不同处理策略
  249. if (error.message.includes('网络')) {
  250. // 网络错误,尝试重试或使用本地缓存
  251. this.retryWithCache();
  252. } else if (error.message.includes('解码')) {
  253. // 解码错误,尝试使用备用解码器
  254. this.switchToBackupDecoder();
  255. } else {
  256. // 其他错误,显示错误提示并停止播放
  257. this.showErrorMessage(error.message);
  258. this.stop();
  259. }
  260. }
  261. ```
  262. ### NAPI错误处理
  263. ```cpp
  264. // NAPI错误处理
  265. static napi_value SomeFunction(napi_env env, napi_callback_info info) {
  266. size_t argc = 1;
  267. napi_value args[1];
  268. napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  269. if (status != napi_ok || argc < 1) {
  270. napi_throw_error(env, nullptr, "Invalid arguments");
  271. return nullptr;
  272. }
  273. // 参数验证
  274. bool isString;
  275. napi_is_string(env, args[0], &isString);
  276. if (!isString) {
  277. napi_throw_type_error(env, nullptr, "Expected string");
  278. return nullptr;
  279. }
  280. // 执行操作...
  281. return result;
  282. }
  283. ```