目录

How do I use a Worker thread to call NAPIs to rewrite variables in a loop?

Symptom

In specific scenarios, an NAPI loop is required for a message loop, without blocking the UI main thread.

Solution

Refer to the following code:

ArkTS side:

Create a Worker and listen for it.

import { MessageEvents, worker } from '@kit.ArkTS';
import { Prompt } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  @State progress: number = 0;
  @State message: string = '0'
  state: number = 0

  build() {
    Column() {
      Row() {
        Button("start")
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            if (this.state == 1) {
              this.getUIContext().getPromptAction().showToast({ message: "Please do not click again" })
              return
            }
            this.state = 1
            let worker1: worker.ThreadWorker =
              new worker.ThreadWorker('entry/ets/workers/Worker.ets', { name: "worker1" });
            worker1.postMessage('this is a msg to start worker1');
            worker1.onmessage = (e: MessageEvents) => {
              this.progress = e.data.data as number
              this.message = String(this.progress)
              console.log('=====js main, process is:' + this.message)

              if (this.progress == 100) {
                worker1.terminate()
                this.state = 0
              }
            }
          });
      }
      .margin({
        top: 10,
        bottom: 10,
        left: 5,
        right: 5
      })

      Row() {
        Text(this.message)
          .fontSize(50)
      }
      .margin({
        top: 10,
        bottom: 5,
        left: 5,
        right: 5
      })
    }
  }
}

Call the NAPI function in the Worker thread.

import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
import testNapi from 'libentry.so';

const workerPort: ThreadWorkerGlobalScope = worker.workerPort;

workerPort.onmessage = (e: MessageEvents) => {
  testNapi.mainThread((data: ESObject) => {
    console.log("==worker func:data is :" + data);
    workerPort.postMessage({ 'type': 1, "data": data });
  });
}

Native side:

Use the loop to complete the message loop on the native side.

struct CallbackContext {
    napi_env env = nullptr;
    napi_ref callbackRef = nullptr;
    int retData = 0;
};
#include "WorkerCallNapiLoop.h" 
#include <thread> 
#include <uv.h> 
 
void WorkerCallNapiLoop::SubThread(CallbackContext *context) { 
    uv_loop_s *loop = nullptr; 
    napi_get_uv_event_loop(context->env, &loop); 
    // uv_work_t is the structure that associates loop and thread pool callback functions
    uv_work_t *work = new uv_work_t; 
    work->data = (CallbackContext *)context; 
    uv_queue_work( 
        loop, work, [](uv_work_t *work) {}, 
        [](uv_work_t *work, int status) { 
            CallbackContext *context = (CallbackContext *)work->data; 
            napi_handle_scope scope = nullptr; 
            // Manage the lifecycle of napi_value to prevent memory leaks
            napi_open_handle_scope(context->env, &scope); 
            if (scope == nullptr) { 
                return; 
            } 
            napi_value callback = nullptr; 
            napi_get_reference_value(context->env, context->callbackRef, &callback); 
            while (context->retData < 100) { 
                context->retData += 1; 
                napi_value retArg; 
                napi_create_int32(context->env, context->retData, &retArg); 
                napi_value ret; 
                napi_call_function(context->env, nullptr, callback, 1, &retArg, &ret); 
                std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
            } 
            napi_close_handle_scope(context->env, scope); 
            if (context->retData > 99) { 
                napi_delete_reference(context->env, context->callbackRef); 
                if (work != nullptr) { 
                    delete work; 
                } 
            } 
        }); 
}; 
napi_value WorkerCallNapiLoop::MainThread(napi_env env, napi_callback_info info) { 
        size_t argc = 1; 
    napi_value args[1] = {0}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    auto asyncContext = new CallbackContext(); 
    asyncContext->env = env; 
    napi_create_reference(env, args[0], 1, &asyncContext->callbackRef); 
    auto func = [](void *asyncContext) { 
        delete asyncContext; 
    }; 
    napi_add_env_cleanup_hook(asyncContext->env, func, asyncContext); 
    uv_loop_s *loop = nullptr; 
    napi_get_uv_event_loop(env, &loop); 
    // Start sub thread 
    std::thread testThread(SubThread, asyncContext); 
    testThread.detach(); 
    return nullptr; 
}