当前位置: 首页 > 科技观察

OpenHarmony源码分析JavaScriptAPI框架(NAPI)

时间:2023-03-15 09:50:11 科技观察

更多内容请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com1.NAPI概念1.1JSAPI概念JSAPI:JavaScriptApplicationProgramming接口,JavaScript应用程序编程接口。1.2JSAPI的实现方式OpenHarmony上有三种JSAPI的实现方式,分别是:JSI机制、Channel机制、NAPI机制。JSI机制:L0~L1设备支持。Channel机制:L3设备支持。NAPI机制:目前只有L2设备支持,未来会扩展到L3~L5设备。1.3NAPI概念一句话,NAPI就是JSAPI在L2设备上的实现方式。2.NAPI机制介绍2.1实现原则优先封装异步方法!当需要社区反馈时,可以添加同步方法。如果引擎支持Promise特性,异步方法必须同时支持Callback方法和Promise方法。使用哪种方法由应用开发者决定,以是否传递Callback来区分。是不传递Callback的Promise方法,方法执行结果是Promise实例对象。L0到L1设备受限于硬件级别,只能在Callback方式下实现异步方法;在L2到L5设备上,必须实现同时支持Callback和Promise方法的异步方法。2.2异步编程模型2.2.1Promise异步模型Promise异步模型是OHOS标准异步模型之一。Promise对象:ES6原生提供了Promise对象,Promise是异步编程的解决方案,可以替代传统的解决方案——回调函数和事件。promise对象是异步操作的结果。它提供了一些API,让异步执行可以按照同步流程来表达,避免回调函数层层嵌套,保证回调是异步调用的。当用户调用这些接口时,接口实现会异步执行任务,并返回一个代表异步操作结果的Promise对象。当返回多个结果时,它作为对象属性返回。Promise特性作为一个对象,Promise有两个特性:(1)对象的状态不受外界影响;(2)状态一旦改变,就不会再改变,也就是说Promise在任何时候都只有一个状态。2.2.2回调异步模型回调异步模型是OHOS标准异步模型之一。当用户调用这些接口时,接口实现将异步执行任务。任务执行结果以参数的形式提供给用户注册的回调函数。这些参数中的第一个是Error或undefined类型,分别表示错误和正常执行。2.2实现步骤2.2.1模块注册API集按照业务功能划分模块。开发者在使用前必须导入相应的模块。姓名:@ohos。模块名称注意:模块名称必须唯一,由ACE团队维护。将模块添加到子系统时,必须向ACE团队提出申请。模块名称最好是单个名词。实在不行,也可以由多个名词组成,但必须遵循小驼峰命名规则。一个模块,一个声明文件(*.d.ts)。声明文件命名遵循@ohos.modulename.d.ts,文件名全部小写,单词之间没有分隔。N-API通过注册函数对模块进行注册,注册函数接受一个全局变量参数,在全局变量结构体中定义了模块名称和模块初始化函数。在模块的初始化中,我们可以定义模块需要暴露的方法和属性。示例:staticnapi_valueStorageExport(napi_envenv,napi_valueexports){constchar*storageClassName="Storage";napi_valuestorageClass=nullptr;/*定义模块需要对外暴露的方法*/staticnapi_property_descriptorstorageDesc[]={DECLARE_NAPI_FUNCTION("get",JSStorageGet),DECLARE_NAPI_FUNC",JSStorageGetSync),};/*定义C++类对应的JavaScript类,包括JS类名和JS构造函数*/napi_define_class(env,storageClassName,strlen(storageClassName),JSStorageConstructor,nullptr,sizeof(storageDesc)/sizeof(storageDesc[0]),storageDesc,&storageClass);/*定义模块需要对外暴露的属性*/staticnapi_property_descriptordesc[]={DECLARE_NAPI_PROPERTY("Storage",storageClass),};/*设置导出对象的属性*/napi_define_properties(env,exports,sizeof(desc)/sizeof(desc[0]),desc);returnexports;}模块定义staticnapi_modulestorageModule={.nm_version=1,.nm_flags=0,.nm_文件名=nullptr,.nm_register_func=StorageExport,.nm_modname="存储",.nm_priv=((void*)0),.reserved={0},};模块注册extern"C"__attribute__((constructor))voidStorageRegister(){napi_module_register(&storageModule);}2.2.2NAPI声明声明文件模板@ohos.modulename.d.ts文件:/***moduledescription*@sinceAPI版本号,ITRelease3对应4,所以类比*@sysCap系统能力*@devices支持设备*@importimportmodule*@permission权限列表*/declarenamespace模块名{//这里定义函数方法}exportdefault模块名;示例:声明文件@ohos.storage.d.ts/***Storage*@since3*@sysCapACEEngine*@devicesphone,tablet,tv,wearable,liteWearable,smartVision*@importimportstoragefrom'@ohos.storage';*@permissionN/A*/declarenamespacestorage{//这里定义函数方法}exportdefaultstorage;2.2.2NAPI实现JSAPI调用流程如下图所示:接口定义/**input**napi_env:表示一个上下文变量;napi_callback_info:传递给回调函数的封装数据类型,可用于获取调用时的上下文信息,也可用于设置回调函数的返回值;**返回值**napi_value:所有js基础值的密封包,表示一个基础值;*/staticnapi_valueGet(napi_envenv,napi_callback_infoinfo);staticnapi_valueGetSync(napi_envenv,napi_callback_infoinfo);2.2.2.1同步回调同步方法调用后,JS线程会一直阻塞,直到获取到返回值命名:Verb+SyncorVerb+Noun+Sync格式:无参数:方法名()带参数:方法名Sync(必参[,可选参数])返回值声明文件模板declarenamespace模块名{/***方法描述*@note特别说明*@since(optional,表示方法支持版本与模块不一致时)*@sysCapsystemcapability*@devicessupporteddevices(optional,表示支持的设备类型与模块不一致时)*@paramparameter参数说明(可选,无参数或当参数被接口包含时无需说明)*@return返回值说明(可选,无返回值或当返回值被接口包含时无需说明)*///NoneParameterfunctionmethodnameSync():returnvaluetype;//parameterfunctionmethodnameSync(requiredparameter:parametertype,options?:optionalparametertype):返回值类型;接口可选参数类型{参数名称:参数类型;}}exportdefault模块名称;示例声明declarenamespacestorage{/***getSync方法说明*@since6*@sysCapACEEnginge*@paramkeykey值说明*@return返回值说明*/functiongetSync(key:string,options?:GetStorageOptions):string;interfaceGetStorageOptions{/***默认参数说明*@since6*@sysCapACEEnginge*/default:string;}}exportdefaultstorage;implementstaticnapi_valueGetSync(napi_envenv,napi_callback_infoinfo){size_trequireArgc=1;size_targc=2;//参数个数napi_valueargv[2]={0};//参数定义napi_valuethisVar=nullptr;//JS对象this参数void*data=nullptr;//回调数据指针/*根据环境变量获取参数*/napi_get_cb_info(env,info,&argc,argv,&thisVar,&data);NAPI_ASSERT(env,argc>=requireArgc,"requires1parameter");charkey[KEY_BUFFER_SIZE]={0};size_tkeyLen=0;charvalue[VALUE_BUFFER_SIZE]={0};size_tvalueLen=0;for(size_ti=0;isecond.c_str(),itr->second.length(),&result);}elseif(valueLen>0){napi_create_string_utf8(env,value,valueLen,&result);}else{objectInfo->Emit(nullptr,"error");NAPI_ASSERT(env,false,"keydoesnotexist");}returnresult;//返回JS对象}2.2.2.2异步回调异步方法调用全程不妨碍调用者工作命名:动词或动词+名词格式:无参数:方法名([回调函数])有参数:方法名(必填参数[,可选参数][,回调函数])返回值如果回调函数不为空,则返回void如果回调函数为空,则返回Promise实例对象声明文件模板declarenamespace模块名{/***方法描述*@notespecial描述*@since(可选,如果方法支持版本与模块不一致,需要待标注)*@sysCap系统能力*@devices支持的设备(可选,当支持的设备类型与模块不一致时需要标注)*@paramparameter参数说明(可选,无参数或当模块不一致时无需标注)参数包含在接口中)*///无参数函数方法名(callback:AsyncCallback):void;functionmethodname():Promise;//functionmethodnamewithparameters(必需的参数eter:参数类型,callback:AsyncCallback):void;function方法名(必填参数:参数类型,选项:可选参数类型,回调:AsyncCallback<结果数据类型>):void;function方法名(必填参数:参数类型,options?:可选参数类型):Promise;接口可选参数类型{参数名称:参数类型;}}exportdefault模块名称;示例:声明import{AsyncCallback}from'./basic';declarenamespacestorage{/***获取方法描述*@noteN/A*@since5*@sysCapACEEngine*@devicesphone,tablet,tv,wearable*@paramkeykey值描述*/functionget(key:string,callback:AsyncCallback):void;functionget(key:string,options:GetStorageOptions,callback:AsyncCallback):void;functionget(key:st环,选项?:GetStorageOptions):Promise;interfaceGetStorageOptions{default:string;}}exportdefaultstorage;异步回调流程如下图所示:staticnapi_valueGet(napi_envenv,napi_callback_infoinfo){size_trequireArgc=1;size_targc=3;//参数个数napi_valueargv[3]={0};//参数定义napi_valuethisVar=nullptr;//JSobjectthisparametervoid*data=nullptr;//回调数据指针/*根据环境变量获取参数*/napi_get_cb_info(env,info,&argc,argv,&thisVar,&data);NAPI_ASSERT(env,argc>=requireArgc,"requires1parameter");/*异步接口上下文,用于接收JS接口的环境变量、参数、回调函数、接口返回值等*/autoasyncContext=newStorageAsyncContext();asyncContext->env=env;for(size_ti=0;ikey,KEY_BUFFER_SIZE,&asyncContext->keyLen);}elseif(valueType==napi_string){napi_get_value_string_utf8(env,argv[i],asyncContext->value,VALUE_BUFFER_SIZE,&asyncContext->valueLen);}elseif(valueType==napi_function){/*根据JS对象参数argv[i]*/napi_create_reference(env,argv[i],1,&asyncContext->回调参考);break;}else{NAPI_ASSERT(env,false,"typemismatch");}}napi_valueresult=nullptr;if(asyncContext->callbackRef==nullptr){/*Promise方法异步调用,创建delay对象,JSPromise对象,使两者都Make一个关联*/napi_create_promise(env,&asyncContext->deferred,&result);}else{/*回调方法异步调用,不需要返回Promise对象,返回一个JS未定义值*/napi_get_undefined(env,&result);}/*根据JS对象获取绑定的native对象实例*/napi_unwrap(env,thisVar,(void**)&asyncContext->objectInfo);napi_valueresource=nullptr;napi_create_string_utf8(env,"JSStorageGet",NAPI_AUTO_LENGTH,&resource);//获取JS异步资源名/*创建异步工作*/napi_create_async_work(env,nullptr,resource,/*执行异步逻辑的原生函数*/[](napi_envenv,void*data){StorageAsyncContext*asyncContext=(StorageAsyncContext*)data;autoitr=g_keyValueStorage.find(asyncContext->key);if(itr!=g_keyValueStorage.end()){if(strncpy_s(asyncContext->value,VALUE_BUFFER_SIZE,itr->second.c_str(),itr->second.length())==-1){asyncContext->status=1;//失败}else{asyncContext->status=0;//success}}else{asyncContext->status=1;//failure}},/*异步函数执行完成或取消后,后处理函数需要执行*/[](napi_envenv,napi_statusstatus,void*data){StorageAsyncContext*asyncContext=(StorageAsyncContext*)data;napi_valueresult[2]={0};if(!asyncContext->status){napi_get_undefined(env,&result[0]);napi_create_string_utf8(env,asyncContext->value,strlen(asyncContext->value),&result[1]);}else{napi_valuemessage=nullptr;napi_create_string_utf8(env,"keydoesnotexist",NAPI_AUTO_LENGTH,&message);napi_create_error(env,nullptr,message,&result[0]);napi_get_undefined(env,&result[1]);asyncContext->objectInfo->Emit(nullptr,"error");}if(asyncContext->deferred){if(!asyncContext->status){/*异步函数执行成功后,后处理ing函数*/napi_resolve_deferred(env,asyncContext->deferred,result[1]);}else{/*异步函数执行失败后,后处理函数*/napi_reject_deferred(env,asyncContext->deferred,result[0]);}}else{napi_valuecallback=nullptr;napi_get_reference_value(env,asyncContext->callbackRef,&callback);napi_call_function(env,nullptr,callback,sizeof(result)/sizeof(result[0]),result,nullptr);napi_delete_reference(env,asyncContext->callbackRef);}/*之后异步回调完成资源释放*/napi_delete_async_work(env,asyncContext->work);deleteasyncContext;},/*用户数据上下文,这个数据传给异步执行函数和后处理函数*/(void*)asyncContext,/*generatedasynchronouswork*/&asyncContext->work);napi_queue_async_work(env,asyncContext->work);//将异步work入队并执行returnresult;}3.应用代码示例JS应用引用NAPI接口时,它必须首先引用接口定义的相应模块。接口可以调用importstoragefrom'@ohos.storage'exportdefault{testGetSync(){//同步接口varname=storage.getSync('name');console.log('nameis'+name);},testGet(){//异步接口storage.get('name').then(date=>console.log('nameis'+data)).catch(error=>console.log('error:'+error));}}更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区