运行 nativescript/javascript 中的非阻塞、高性能 activity

Running a non-blocking, high-performance activity in nativescript/javascript

这个问题是关于 运行在 nativescript 中使用一个非阻塞的、高性能的 activity,它需要通过直接访问从麦克风读取和保存原始音频的简单任务。通过本机硬件 Android API。我相信我已经把nativescript框架发挥到极致了,我需要专家的帮助。

我正在使用 Nativescript Android 构建一个 WAV 录音机。 here(下面的相关代码)描述了本机实现。

简而言之,这可以通过从 android.media.AudioRecord 缓冲区读取音频流,然后在单独的线程中将缓冲区写入文件来完成,如下所述:

本机 Android 实现

startRecording() 由按下按钮触发,并启动一个新线程 运行s writeAudioDataToFile():

private void startRecording() {
    // ... init Recorder

    recorder.startRecording();

    isRecording = true;

    recordingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            writeAudioDataToFile();
        }
     }, "AudioRecorder Thread");

    recordingThread.start();
}

通过将 isRecording 设置为 false 停止录制(stopRecording() 由按下按钮触发):

private void stopRecording() {
   isRecording = false;

   recorder.stop();
   recorder.release();

   recordingThread = null;
}

如果 isRecording = false,则停止读取和保存缓冲区:

private void writeAudioDataToFile() {
    // ... init file and buffer
    ByteArrayOutputStream recData = new ByteArrayOutputStream(); 
    DataOutputStream dos = new DataOutputStream(recData);

    int read = 0;

    while(isRecording) {
        read = recorder.read(data, 0, bufferSize);

        for(int i = 0; i < bufferReadResult; i++) {
            dos.writeShort(buffer[i]);
        }
    }
}

我的 Nativescript javascript 实现:

我写了一个 nativescript 打字稿代码,它的功能与上面的 Android 代码相同。我遇到的问题 #1 是我不能 运行 while(isRecording) 因为 javascript 线程会在 while 循环内忙 运行ning,并且会永远无法捕捉到 运行 stopRecording().

的按钮点击

我尝试通过使用 setInterval 进行异步执行来解决问题 #1,如下所示:

startRecording() 由按钮按下触发,并设置执行 writeAudioDataToFile():

的时间间隔为 10ms
startRecording() {
    this.audioRecord.startRecording();

    this.audioBufferSavingTimer = setInterval(() => this.writeAudioDataToFile(), 10);
}

writeAudioDataToFile() 回调每 10 毫秒排队一次:

writeAudioDataToFile() {
    let bufferReadResult = this.audioRecord.read(
        this.buffer,
        0,
        this.minBufferSize / 4
    );

    for (let i = 0; i < bufferReadResult; i++) {
        dos.writeShort(buffer[i]);
    }

}

通过清除时间间隔停止录制(stopRecording() 由按下按钮触发):

stopRecording() {
    clearInterval(this.audioBufferSavingTimer);

    this.audioRecord.stop();
    this.audioRecord.release();
}

问题 #2:虽然这很有效,但在许多情况下它会使 UI 冻结 1-10 秒(例如在单击按钮停止录制后)。

我试图将执行 writeAudioDataToFile() 的时间间隔从 10 毫秒更改为 0 毫秒,最多 1000 毫秒(同时具有非常大的缓冲区),但是 UI 冻结时间更长,并且我遇到了保存数据丢失(未保存到文件的缓冲数据)。

我尝试使用 nativescript 工作线程将此操作卸载到单独的线程,如 here 所述,其中 startRecording()stopRecording() 由发送到线程的消息调用,例如这个:

global.onmessage = function(msg) {

    if (msg.data === 'startRecording') {
        startRecording();
    } else if (msg.data === 'stopRecording') {
       stopRecording();
    } 

}

这解决了 UI 问题,但产生了问题 #3:记录器 stop 没有按时执行(即在 stop 后记录停止 10 到 50 秒=86=] msg.data 被工作线程接收)。我尝试在工作线程内的 setInterval 中使用不同的时间间隔(0 毫秒到 1000 毫秒),但这并没有解决问题,甚至使 stopRecording() 的执行延迟更大。

有没有人知道如何在 nativescript/javascript 中执行这种非阻塞高性能记录 activity? 有没有更好的方法来解决我上面描述的问题 #1(javascript 异步执行)?

谢谢

我会在实际 Java 中保留完整的 Java 实现,您可以通过在插件文件夹中创建一个 java 文件来做到这一点: platforms/android/java,所以可能是这样的: platforms/android/java/org/nativescript/AudioRecord.java

在那里你可以做所有线程化的事情,所以你不会因为 UI 被阻塞而烦恼。您可以直接从 NativeScript 调用 Java 方法来开始和停止录制。当您构建项目时,Java 文件将自动编译并包含。

您可以通过从您的插件生成的 .aar 文件 ({plugin_name}.aar) 中抓取 classes.jar 来从您的 Java class 生成类型并生成它的类型声明:https://docs.nativescript.org/core-concepts/android-runtime/metadata/generating-typescript-declarations

这样您就可以在编辑器中获得所有 method/class/type 信息。