返回介绍

第 7 章 使用 React Native 播放音频

发布于 2025-04-26 18:09:27 字数 4618 浏览 0 评论 0 收藏 0

在这一章,我们将学习自定义的 React Native 组件。

有时一个应用程序需要访问平台 API, React Native 并没有相应的封装器。也许你想重用现有的一些 Objective-C 或 C++代码,而不想在 JavaScript 上重新实现;或者你可能想写一些高性能、多线程的代码,用于诸如图像处理、网络堆栈、数据库或渲染等高级扩展。

我们是可以编写真正的 native 代码,并且访问整个平台的。这涉及更高级的特性,虽然我们并不期望它成为通常开发过程中的一部分,但是它的存在是至关重要的。如果 React Native 对你需要的特性不是原生支持的,那么你应该能够自己构建它。

这是一个更高级的指南,展示了如何构建一个原生模块。前提是读者需要了解 Objective-C (或 Swift)和核心库(Foundation、UIKit)。

让我们开始使用下面的命令创建我们的自定义组件:

react-native new-library AudioPlayerManager

将生成的 AudioPlayerManager.xcproj 文件拖动到项目目录下的 Libraries 文件夹中来链接库。如果有任何疑问请参考第 1 章相关内容。

AudioPlayerManager.h



#import "RCTBridgeModule.h"
#import "RCTLog.h"
#import <AVFoundation/AVFoundation.h>
@interface AudioPlayerManager : NSObject <RCTBridgeModule,
    AVAudioPlayerDelegate>

@end

AudioPlayerManager.m


//
//  AudioPlayerManager.m
//  AudioPlayerManager
//
//  Created by Joshua Sierles on 15/04/15.
//  Copyright (c) 2015 Joshua Sierles. All rights reserved.
//

#import "AudioPlayerManager.h"
#import "RCTConvert.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import <AVFoundation/AVFoundation.h>

NSString *const AudioPlayerEventProgress = @"playerProgress";
NSString *const AudioPlayerEventFinished = @"playerFinished";

@implementation AudioPlayerManager {

  AVAudioPlayer *_audioPlayer;

  NSTimeInterval _currentTime;
  id _progressUpdateTimer;
  int _progressUpdateInterval;
  NSDate *_prevProgressUpdateTime;
  NSURL *_audioFileURL;
}

@synthesize bridge = _bridge;
RCT_EXPORT_MODULE();

- (void)sendProgressUpdate {
  if (_audioPlayer && _audioPlayer.playing) {
    _currentTime = _audioPlayer.currentTime;
  }

  NSString *time = [NSString stringWithFormat:@"%f", _currentTime];

  if (_prevProgressUpdateTime == nil ||
    (([_prevProgressUpdateTime timeIntervalSinceNow] * -1000.0) >=
      _progressUpdateInterval)) {
      [_bridge.eventDispatcher sendDeviceEventWithName:
          AudioPlayerEventProgress body:@{
      @"currentTime": [NSNumber numberWithFloat:_currentTime]
    }];

    _prevProgressUpdateTime = [NSDate date];
  }
}

- (void)stopProgressTimer {
  [_progressUpdateTimer invalidate];
}

- (void)startProgressTimer {
  _progressUpdateInterval = 250;
  _prevProgressUpdateTime = nil;

  [self stopProgressTimer];

  _progressUpdateTimer = [CADisplayLink displayLinkWithTarget:self
      selector:@selector(sendProgressUpdate)];
  [_progressUpdateTimer addToRunLoop:[NSRunLoop mainRunLoop]
      forMode:NSDefaultRunLoopMode];
}

- (void)AudioPlayerDidFinishPlaying:(AVAudioPlayer *)recorder
    successfully:(BOOL)flag {
  NSLog(flag ? @"FINISHED OK" : @"FINISH ERROR");
  [_bridge.eventDispatcher sendDeviceEventWithName:
      AudioPlayerEventFinished body:@{
      @"finished": @TRUE
    }];
}

RCT_EXPORT_METHOD(play:(NSString *)path)
{
  NSError *error;

  NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
  NSString *audioFilePath = [resourcePath
      stringByAppendingPathComponent:path];

  _audioFileURL = [NSURL fileURLWithPath:audioFilePath];

  NSLog([_audioFileURL absoluteString]);

  _audioPlayer = [[AVAudioPlayer alloc]
    initWithContentsOfURL:_audioFileURL
    error:&error];
  if (error) {
    [self stopProgressTimer];
    NSLog(@"audio playback loading error: %@", [error
        localizedDescription]);
    // TODO: 通过接口分发错误


  } else {
    [self startProgressTimer];
    [_audioPlayer play];
  }
}

RCT_EXPORT_METHOD(playWithUrl:(NSURL *) url)
{
  NSError *error;
  NSData* data = [NSData dataWithContentsOfURL: url];

  _audioPlayer = [[AVAudioPlayer alloc] initWithData:data  error:&
      error];
  if (error) {
    [self stopProgressTimer];
    NSLog(@"audio playback loading error: %@", [error
        localizedDescription]);
    // TODO: 通过接口分发错误


  } else {
    [self startProgressTimer];
    [_audioPlayer play];
  }
}

RCT_EXPORT_METHOD(pause)
{
  if (_audioPlayer.playing) {
    [_audioPlayer pause];
  }
}

RCT_EXPORT_METHOD(stop)
{
  if (_audioPlayer.playing) {
    [_audioPlayer stop];
  }
}

@end
audio_player.js



'use strict';

/**
  * 这个模块是一个轻量的原生模块。它的作用是实现注册回调,更改设置等。


  */

var React = require('react-native');

var AudioPlayerManager = require('NativeModules').
    AudioPlayerManager;

var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');

var AudioPlayer = {
  play: function(path) {
    AudioPlayerManager.play(path);
  },
  pause: function() {
    AudioPlayerManager.pause();
  },
  stop: function() {
    AudioPlayerManager.stop();
    if (this.subscription) {
      this.subscription.remove();
    }
  }
};

module.exports = {AudioPlayer};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。