Dart和JavaScript一样,是一种单线程编程语言。如果把耗时的任务都放在Dart线程中,就会阻塞程序。Dart提供了同步和异步两种扩展方式。异步扩展可以把一个C/C++函数执行在一条工作线程上,这样就可以用于执行耗时的任务,不会阻塞程序。这里结合Dynamsoft C/C++ barcode SDK来实现一个Dart的异步扩展。
安装Dart SDK和Dynamsoft Barcode Reader。
Dart的扩展由*.dart和*.dll文件组成。和JNI一样,*.dart文件中定义了一些调用C/C++接口的函数。这些函数会把上层的参数转发到native发送端口。同时创建一个接收端口用于接收native发送回来的数据。sample_asynchronous_extension.dart:
library sample_asynchronous_extension;
import 'dart:async';
import 'dart:isolate';
import 'dart-ext:sample_extension';
class BarcodeReader {
static SendPort _port;
Future readBarcode(String filename) {
var completer = new Completer();
var replyPort = new RawReceivePort();
var args = new List(2);
args[0] = filename;
args[1] = replyPort.sendPort;
_servicePort.send(args);
replyPort.handler = (result) {
replyPort.close();
if (result != null) {
completer.complete(result);
} else {
completer.completeError(new Exception("Reading barcode failed"));
}
};
return completer.future;
}
SendPort get _servicePort {
if (_port == null) {
_port = _newServicePort();
}
return _port;
}
SendPort _newServicePort() native "BarcodeReader_ServicePort";
}
运行Visual Studio 2015创建一个空的Win32工程。应用属性选择DLL。把头文件和库的路径添加到工程属性中:
添加依赖dart.lib,DBRx64.lib:
添加预处理DART_SHARED_LIB:
创建sample_extension_dllmain_win.cc:
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
return true;
}
#endif // defined(_WIN32)
创建sample_extension.cc。
添加头文件:
#include
#include
#include
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#include "If_DBRP.h"
使用函数sample_extension_Init()和ResolveName()来初始化:
DART_EXPORT Dart_Handle sample_extension_Init(Dart_Handle parent_library) {
if (Dart_IsError(parent_library)) {
return parent_library;
}
Dart_Handle result_code =
Dart_SetNativeResolver(parent_library, ResolveName, NULL);
if (Dart_IsError(result_code)) {
return result_code;
}
return Dart_Null();
}
struct FunctionLookup {
const char* name;
Dart_NativeFunction function;
};
FunctionLookup function_list[] = {
{"BarcodeReader_ServicePort", barcodeReaderServicePort},
{NULL, NULL}};
Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool* auto_setup_scope) {
if (!Dart_IsString(name)) {
return NULL;
}
Dart_NativeFunction result = NULL;
if (auto_setup_scope == NULL) {
return NULL;
}
Dart_EnterScope();
const char* cname;
HandleError(Dart_StringToCString(name, &cname));
for (int i=0; function_list[i].name != NULL; ++i) {
if (strcmp(function_list[i].name, cname) == 0) {
*auto_setup_scope = true;
result = function_list[i].function;
break;
}
}
if (result != NULL) {
Dart_ExitScope();
return result;
}
Dart_ExitScope();
return result;
}
使用Dynamsoft C/C++ barcode接口来读取条形码:
char **readBarcode(char *pszFileName, int *out_len) {
char **values = NULL;
__int64 llFormat = (OneD | QR_CODE | PDF417 | DATAMATRIX);
char pszBuffer[512] = { 0 };
int iMaxCount = MAX_BARCODE_AMOUNT;
int iIndex = 0;
ReaderOptions ro = { 0 };
int iRet = -1;
char * pszTemp = NULL;
unsigned __int64 ullTimeBegin = 0;
unsigned __int64 ullTimeEnd = 0;
size_t iLen = 0;
FILE* fp = NULL;
int iExitFlag = 0;
// Set license
CBarcodeReader reader;
reader.InitLicense("38B9B94D8B0E2B41FDE1FB60861C28C0");
// Read barcode
ullTimeBegin = GetTickCount();
ro.llBarcodeFormat = llFormat;
ro.iMaxBarcodesNumPerPage = iMaxCount;
reader.SetReaderOptions(ro);
iRet = reader.DecodeFile(pszFileName);
ullTimeEnd = GetTickCount();
// Output barcode result
pszTemp = (char*)malloc(4096);
if (iRet != DBR_OK && iRet != DBRERR_LICENSE_EXPIRED && iRet != DBRERR_QR_LICENSE_INVALID &&
iRet != DBRERR_1D_LICENSE_INVALID && iRet != DBRERR_PDF417_LICENSE_INVALID && iRet != DBRERR_DATAMATRIX_LICENSE_INVALID)
{
sprintf_s(pszTemp, 4096, "Failed to read barcode: %s\r\n", DBR_GetErrorString(iRet));
printf(pszTemp);
free(pszTemp);
return NULL;
}
pBarcodeResultArray paryResult = NULL;
reader.GetBarcodes(&paryResult);
if (paryResult->iBarcodeCount == 0)
{
sprintf_s(pszTemp, 4096, "No barcode found. Total time spent: %.3f seconds.\r\n", ((float)(ullTimeEnd - ullTimeBegin) / 1000));
printf(pszTemp);
free(pszTemp);
reader.FreeBarcodeResults(&paryResult);
return NULL;
}
sprintf_s(pszTemp, 4096, "Total barcode(s) found: %d. Total time spent: %.3f seconds\r\n\r\n", paryResult->iBarcodeCount, ((float)(ullTimeEnd - ullTimeBegin) / 1000));
//printf(pszTemp);
values = (char **)malloc((paryResult->iBarcodeCount + 1) * sizeof(char *));
for (iIndex = 0; iIndex < paryResult->iBarcodeCount; iIndex++)
{
char* pszValue = (char*)calloc(paryResult->ppBarcodes[iIndex]->iBarcodeDataLength + 1, sizeof(char));
memcpy(pszValue, paryResult->ppBarcodes[iIndex]->pBarcodeData, paryResult->ppBarcodes[iIndex]->iBarcodeDataLength);
values[iIndex] = pszValue;
}
free(pszTemp);
reader.FreeBarcodeResults(&paryResult);
values[iIndex] = NULL;
*out_len = iIndex;
return values;
}
把返回的数据封装到Dart_CObjects中:
void wrappedBarcodeReader(Dart_Port dest_port_id,
Dart_CObject* message) {
Dart_Port reply_port_id = ILLEGAL_PORT;
if (message->type == Dart_CObject_kArray &&
2 == message->value.as_array.length) {
// Use .as_array and .as_int32 to access the data in the Dart_CObject.
Dart_CObject* param0 = message->value.as_array.values[0];
Dart_CObject* param1 = message->value.as_array.values[1];
if (param0->type == Dart_CObject_kString &&
param1->type == Dart_CObject_kSendPort) {
char * pszFileName = param0->value.as_string;
reply_port_id = param1->value.as_send_port.id;
int length = 0;
char **values = readBarcode(pszFileName, &length);
char **pStart = values;
if (values != NULL) {
Dart_CObject* results[MAX_BARCODE_AMOUNT];
Dart_CObject collection[MAX_BARCODE_AMOUNT];
int index = 0;
char * pszValue = NULL;
while ((pszValue = *pStart) != NULL) {
Dart_CObject value;
value.type = Dart_CObject_kString;
value.value.as_string = pszValue;
collection[index] = value;
++pStart;
++index;
}
for (int i = 0; i < length; i++) {
results[i] = &(collection[i]);
}
Dart_CObject message;
message.type = Dart_CObject_kArray;
message.value.as_array.length = length;
message.value.as_array.values = results;
Dart_PostCObject(reply_port_id, &message);
freeString(values);
return;
}
}
}
Dart_CObject result;
result.type = Dart_CObject_kNull;
Dart_PostCObject(reply_port_id, &result);
}
打开dart_native_api.h查看Dart_CObjects 是如何定义的:
typedef struct _Dart_CObject {
Dart_CObject_Type type;
union {
bool as_bool;
int32_t as_int32;
int64_t as_int64;
double as_double;
char* as_string;
struct {
bool neg;
intptr_t used;
struct _Dart_CObject* digits;
} as_bigint;
struct {
Dart_Port id;
Dart_Port origin_id;
} as_send_port;
struct {
int64_t id;
} as_capability;
struct {
intptr_t length;
struct _Dart_CObject** values;
} as_array;
struct {
Dart_TypedData_Type type;
intptr_t length;
uint8_t* values;
} as_typed_data;
struct {
Dart_TypedData_Type type;
intptr_t length;
uint8_t* data;
void* peer;
Dart_WeakPersistentHandleFinalizer callback;
} as_external_typed_data;
} value;
} Dart_CObject;
设置端口:
void barcodeReaderServicePort(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_SetReturnValue(arguments, Dart_Null());
Dart_Port service_port =
Dart_NewNativePort("BarcodeReaderService", wrappedBarcodeReader, true);
if (service_port != ILLEGAL_PORT) {
Dart_Handle send_port = HandleError(Dart_NewSendPort(service_port));
Dart_SetReturnValue(arguments, send_port);
}
Dart_ExitScope();
}
编译工程生成sample_extension.dll。拷贝sample_asynchronous_extension.dart,sample_extension.dll和DynamsoftBarcodeReaderx64.dll到同一个目录:
创建barcode_reader.dart测试Dart异步扩展:
import 'sample_asynchronous_extension.dart';
void main() {
BarcodeReader reader = new BarcodeReader();
reader.readBarcode(r'f:\AllSupportedBarcodeTypes.bmp').then((values) {
if (values != null) {
for (var number in values) {
print(number);
}
}
});
}
https://github.com/yushulx/dart-native-extension