• Home
Name Date Size #Lines LOC

..--

gpioled-javascript/12-May-2024-426373

README.mdD12-May-202422 KiB642487

README.md

1### 一、**样例介绍**
2
3> 基于OpenHarmony NAPI框架实现北向应用端控制南向设备端扩展板指定GPIO口对应LED灯的亮灭
4
5### 二、**开发环境**
6
71. 搭载OpenHarmony-3.2-Beat2版本的Unionpi Tiger开发板
82. DevEco Studio 3.0.0.991
93. 40PIN测试小板
104. Ubuntu20.04虚拟机
115. USB_Burning_Tool烧录工具
12
13**环境搭建就不详细介绍了,社区也可以搜得到,这里给出笔者参考的几篇资料**
14
15**\[1]**[OpenHarmony在Amlogic A311D芯片平台的快速开发上手指南-开源基础软件社区-51CTO.COM](https://ost.51cto.com/posts/11117)
16
17**\[2]**[docs/UnionpiTiger_helloworld · OpenHarmony-SIG/knowledge_demo_temp - 码云 - 开源中国 (gitee.com)](https://gitee.com/openharmony-sig/knowledge_demo_temp/tree/master/docs/UnionpiTiger_helloworld)
18
19**[3]**[unionpi_tiger/README_zh.md · OpenHarmony/device_board_unionman - Gitee.com](https://gitee.com/openharmony/device_board_unionman/blob/master/unionpi_tiger/README_zh.md#搭建开发环境)
20
21### 三、**OpenHarmony系统技术架构**
22
23OpenHarmony整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“**系统 > 子系统 > 组件**”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或组件。子系统是一个逻辑概念,它具体由对应的组件构成。
24
25![img](https://gitee.com/openharmony/docs/raw/master/zh-cn/figures/1.png)
26
27### 四、**NAPI框架简介**
28
29NAPI(Native API)是 OpenHarmony 标准系统的一种JS API实现机制,适合封装IO、CPU密集型、OS底层等能力并对外暴露JS接口,实现**JS与C/C++代码互相访问**。
30
31![NAPI](../figures/gpioled/NAPI.png)
32
33### 五、**实现步骤**
34
35### **1、创建NAPI扩展库**
36
37**新增子系统napisubsys**
38
39在OpenHarmony源码目录下任意位置创建一个目录`napisubsys`作为子系统的目录(子系统可创建在OpenHarmony源码目录任意位置)。子系统目录下创建`ohos.build`文件,构建时会先读取此文件。把新增的子系统配置到`build/subsystem_config.json`。
40
41```json
42"napisubsys": {
43  "path": "vendor/unionman/unionpi_tiger/sample/napi/napisubsys",
44  "name": "napisubsys"
45},
46```
47
48**新增gpioled_part组件**
49
50在子系统目录下创建一个子组件目录`gpioled_part`。
51
52打开`unionpi_tiger/sample/napi/napisubsys/ohos.build`文件,在`"parts":`中添加下列语句
53
54```
55"gpioled_part": {
56    "variants": [
57        "phone"
58    ],
59    "module_list": [
60        "//vendor/unionman/unionpi_tiger/sample/napi/napisubsys/gpioled_part/gpioled_demo:gpioled"
61    ]
62}
63```
64
65**新增扩展动态库**
66
67在组件目录下创建一个子目录`gpioled_demo`,作为 NAPI扩展库的代码目录。
68
69在`gpioled_demo`目录下创建代码文件`gpioled.cpp`。
70
71在`gpioled_demo`目录下创建`BUILD.gn`文件,编写构建配置。
72
73```
74import("//build/ohos.gni")
75
76ohos_shared_library("gpioled") {
77  include_dirs = [ "//foundation/ace/napi/interfaces/kits" ]
78
79  sources = [
80    "gpioled.cpp",
81    "um_gpio.c",
82  ]
83
84  deps = [
85    "//foundation/ace/napi:ace_napi",
86    "//utils/native/base:utils",
87  ]
88
89  external_deps = [ "hilog:libhilog" ]
90
91  relative_install_dir = "module"
92  subsystem_name = "napisubsys"
93  part_name = "gpioled_part"
94}
95```
96
97**将组件添加到产品定义中**
98
99打开`vendor/unionman/unionpi_tiger/config.json`文件,在`"subsystems":`中添加下列语句
100
101```json
102{
103    "subsystem": "napisubsys",
104    "components": [
105        {
106            "component": "gpioled_part",
107            "features": []
108        }
109    ]
110},
111```
112
113### **2、NAPI接口开发**
114
115**模块注册**
116
1171)添加NAPI框架头文件,引入框架提供的方法。
118
119```c++
120#include "napi/native_api.h"
121#include "napi/native_node_api.h"
122```
123
1242)定义模块。
125
1263)注册模块,加载动态库时自动调用注册。
127
128```c++
129/*
130 * 模块定义
131 */
132static napi_module gpioled_demoModule = {
133        .nm_version = 1,
134        .nm_flags = 0,
135        .nm_filename = nullptr,
136        .nm_register_func = registerGpioLed_DemoApis,
137        .nm_modname = "gpioled_demo",  //模块名
138        .nm_priv = ((void *) 0),
139        .reserved = {0},
140};
141
142/*
143 * 注册模块
144 */
145extern "C" __attribute__((constructor)) void RegisterGpioLed_DemoModule(void) {
146    napi_module_register(&gpioled_demoModule);  //接口注册函数
147}
148```
149
150使用**DECLARE_NAPI_FUNCTION**("js函数名", c++实现函数名)定义接口函数、**DECLARE_NAPI_PROPERTY**、 **DECLARE_NAPI_STATIC_PROPERTY**等定义属性,再通过**napi_define_properties**赋给exports对象,最后返回exports对象。
151
152```c++
153/*
154 * 注册接口
155 */
156static napi_value registerGpioLed_DemoApis(napi_env env, napi_value exports) {
157    napi_value gpioValHigh = gpioValHigh;
158    napi_value gpioValLow = nullptr;
159    napi_create_int32(env, UM_GPIO_HIGH_LEVE, &gpioValHigh);
160    napi_create_int32(env, UM_GPIO_LOW_LEVE, &gpioValLow);
161    napi_property_descriptor desc[] = {
162            DECLARE_NAPI_FUNCTION("setLedStatusWithCallback", setLedStatusWithCallback),
163            DECLARE_NAPI_FUNCTION("getLedStatusWithCallback", getLedStatusWithCallback),
164            DECLARE_NAPI_FUNCTION("setLedStatusWithPromise", setLedStatusWithPromise),
165            DECLARE_NAPI_FUNCTION("getLedStatusWithPromise", getLedStatusWithPromise),
166            DECLARE_NAPI_FUNCTION("setLedStatus", setLedStatus),
167            DECLARE_NAPI_FUNCTION("getLedStatus", getLedStatus),
168            DECLARE_NAPI_STATIC_PROPERTY("LED_ON", gpioValHigh),
169            DECLARE_NAPI_STATIC_PROPERTY("LED_OFF", gpioValLow),
170    };
171    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
172    return exports;
173}
174```
175
176#### **实现原理**
177
178**NAPI支持Callback、 Promise 二种异步模型**
179
180> • Callback:任务结果以参数的形式提供给用户注册的回调函数;代码逻辑复杂,可读性差,回调地狱。
181>
182> • Promise:ES6提供的一种异步编程解决方案,比传统的解决方案——回调函数更加优雅。可使得异步执行可以 按照同步的流表示出来,避免了层层嵌套的回调函数,代码更易读。
183>
184
185**NAPI框架中,异步接口实现基于napi_create_async_work() 函数创建的异步工作项**
186
187> napi_status napi_create_async_work(napi_env env,
188>
189> ​																  napi_value async_resource,
190>
191> ​																  napi_value async_resource_name,
192>
193> ​																  napi_async_execute_callback execute,
194>
195> ​																  napi_async_complete_callback complete,
196>
197> ​																  void* data, napi_async_work* result);
198
199参数说明:
200
201> [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供。
202>
203> [in] async_resource: 可选项,关联async_hooks。
204>
205> [in] async_resource_name: 异步资源标识符,主要用于async_hooks API暴露断言诊断信息。
206>
207> [in] execute: 异步工作项的处理函数,当工作项被调度到后在worker线程中执行,用于处理业务逻辑,并得到结果。
208>
209> [in] complete: execute参数指定的函数执行完成或取消后,触发执行该函数,将结果返回给JS。此函数在EventLoop中执行。
210>
211> [in] data: 异步工作项上下文数据(Addon),用于在主线程接口实现方法、execute、complete之间传递数据。
212>
213> [out] result: napi_async_work*指针,返回创建的异步工作项对象。
214>
215> 返回值:返回napi_ok表示转换成功,其他值失败。
216
217**异步工作项工作时序图**
218
219![2](../figures/gpioled/2.png)
220
221**定义异步工作项上下文数据**
222
223根据实际场景需要定义异步工作项上下文数据结构,用于在主线程方法、execute、complete之间传递数据,一般包含异步工作项对象、napi_deferred对象、回调函数、参数数组、返回结果等。
224
225```c
226struct LedAddOnData {
227    napi_async_work asyncWork = nullptr; //异步工作项
228    napi_deferred deferred = nullptr;    //用于Promise的resolve、reject处理
229    napi_ref callback = nullptr;         //回调函数
230    int args[2] = {0};                   //2个输入参数
231    int result = 0;                      //业务逻辑处理结果(返回值)
232};
233```
234
235#### **异步接口--Callback实现**
236
237主线程方法接收参数,初始化异步工作项上下文数据,创建异步工作项,并将其加到调度队列。最后方法返回空值。
238
239```c++
240static napi_value setLedStatusWithCallback(napi_env env, napi_callback_info info) {
241    //获取3个参数,值的类型是js类型(napi_value)
242    size_t argc = 3;
243    napi_value args[3];
244    napi_value thisArg = nullptr;
245    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
246
247    //异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
248    auto addonData = new LedAddOnData{
249            .asyncWork = nullptr,
250    };
251
252    //将接收到的参数传入用户自定义的上下文数据
253    NAPI_CALL(env, napi_get_value_int32(env, args[0], &addonData->args[0]));
254    NAPI_CALL(env, napi_get_value_int32(env, args[1], &addonData->args[1]));
255    NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));
256
257    //创建async work,创建成功后通过最后一个参数接受async work的handle
258    napi_value resourceName = nullptr;
259    napi_create_string_utf8(env, "setLedStatusWithCallback", NAPI_AUTO_LENGTH, &resourceName);
260    napi_create_async_work(env, nullptr, resourceName, setLedStatusExecuteCB, completeCBForCallback, (void *) addonData,
261                           &addonData->asyncWork);
262
263    //将刚创建的async work加到队列,由底层去调度执行
264    napi_queue_async_work(env, addonData->asyncWork);
265
266    //原生方法返回空对象
267    napi_value result = 0;
268    NAPI_CALL(env, napi_get_null(env, &result));
269    return result;
270}
271```
272
273worker线程执行业务逻辑计算,将结果存入上下文数据
274
275```c++
276//业务逻辑处理函数,由worker线程池调度执行。
277static void setLedStatusExecuteCB(napi_env env, void *data) {
278    LedAddOnData *addOnData = (LedAddOnData *) data;
279    int gpioNum = addOnData->args[0];
280    int bExport = UM_GPIO_EXPORTED;
281    int direction = UM_GPIO_DIRECTION_OUT;
282    int getValue = -1;
283
284    UM_GPIO_IsExport(gpioNum, &getValue);
285    if (getValue == UM_GPIO_NOT_EXPORT) {
286        UM_GPIO_Export(gpioNum, bExport);
287    }
288
289    UM_GPIO_SetDirection(gpioNum, direction);
290    addOnData->result = UM_GPIO_SetValue(gpioNum, addOnData->args[1]);
291}
292```
293
294执行Complete函数,从上下文数据中获取结果,将结果转换为JS类型,调用回调函数返回结果给调用方。
295
296```c++
297//业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
298static void completeCBForCallback(napi_env env, napi_status status, void *data) {
299    LedAddOnData *addOnData = (LedAddOnData *) data;
300    napi_value callback = nullptr;
301    napi_get_reference_value(env, addOnData->callback, &callback);
302    napi_value undefined = nullptr;
303    napi_get_undefined(env, &undefined);
304    napi_value result = nullptr;
305    napi_create_int32(env, addOnData->result, &result);
306
307    //执行回调函数
308    napi_value returnVal = nullptr;
309    napi_call_function(env, undefined, callback, 1, &result, &returnVal);
310
311    //删除napi_ref对象
312    if (addOnData->callback != nullptr) {
313        napi_delete_reference(env, addOnData->callback);
314    }
315
316    //删除异步工作项
317    napi_delete_async_work(env, addOnData->asyncWork);
318    delete addOnData;
319}
320```
321
322#### **异步接口-- Promise实现**
323
324NAPI框架提供napi_create_promise()函数用于创建Promise,返回promise、deferred 二个对象, promise用于主线程方法返回, deferred对象用于resolve、reject处理。
325
326> napi_status napi_create_promise(napi_env env,
327>
328> ​															napi_deferred* deferred,
329>
330> ​															napi_value* promise);
331
332**参数说明:**
333
334> [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
335>
336> [out] deferred: 返回接收刚创建的deferred对象,关联Promise对象,后面用于napi_resolve_deferred() 或 napi_reject_deferred() 更新状态,返回数据。
337>
338> [out] promise: 关联上面deferred对象的JS Promise对象,用于主线程方法返回。
339>
340> 返回值:返回napi_ok表示转换成功,其他值失败。
341
342主线程方法接收参数,创建Promise,初始化异步工作项上下文数据,创建异步工作项,并将其加到调度队列。最后返回Promise对象。
343
344```c++
345static napi_value setLedStatusWithPromise(napi_env env, napi_callback_info info) {
346    //获取2个参数,值的类型是js类型(napi_value)
347    size_t argc = 2;
348    napi_value args[2];
349    napi_value thisArg = nullptr;
350    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
351
352    //创建promise
353    napi_value promise = nullptr;
354    napi_deferred deferred = nullptr;
355    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
356
357    //异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据
358    auto addonData = new LedAddOnData{
359            .asyncWork = nullptr,
360            .deferred = deferred,
361    };
362
363    //将被收到的参数传入
364    NAPI_CALL(env, napi_get_value_int32(env, args[0], &addonData->args[0]));
365    NAPI_CALL(env, napi_get_value_int32(env, args[1], &addonData->args[1]));
366
367    //创建async work,创建成功后通过最后一个参数(addonData->asyncWork)返回asyncwork的handle
368    napi_value resourceName = nullptr;
369    napi_create_string_utf8(env, "setLedStatusWithPromise", NAPI_AUTO_LENGTH, &resourceName);
370    napi_create_async_work(env, nullptr, resourceName, setLedStatusExecuteCB, completeCBForPromise, (void *) addonData,
371                           &addonData->asyncWork);
372
373    //将刚创建的async work加到队列,由底层去调度执行
374    napi_queue_async_work(env, addonData->asyncWork);
375
376    //返回promise
377    return promise;
378}
379```
380
381worker线程执行业务逻辑计算,将结果存入上下文数据。
382
383```c++
384//业务逻辑处理函数,由worker线程池调度执行。
385static void setLedStatusExecuteCB(napi_env env, void *data) {
386    LedAddOnData *addOnData = (LedAddOnData *) data;
387    int gpioNum = addOnData->args[0];
388    int bExport = UM_GPIO_EXPORTED;
389    int direction = UM_GPIO_DIRECTION_OUT;
390    int getValue = -1;
391
392    UM_GPIO_IsExport(gpioNum, &getValue);
393    if (getValue == UM_GPIO_NOT_EXPORT) {
394        UM_GPIO_Export(gpioNum, bExport);
395    }
396
397    UM_GPIO_SetDirection(gpioNum, direction);
398    addOnData->result = UM_GPIO_SetValue(gpioNum, addOnData->args[1]);
399}
400```
401
402执行Complete函数,从上下文数据中获取结果,将结果转换为JS类型,调用 napi_resolve_deferred、 napi_reject_deferred更新状态、返回结果给调用方。
403
404```c++
405static void completeCBForPromise(napi_env env, napi_status status, void *data) {
406    LedAddOnData *addOnData = (LedAddOnData *) data;
407    napi_value result = nullptr;
408    napi_create_int32(env, addOnData->result, &result);
409    napi_resolve_deferred(env, addOnData->deferred, result);
410
411    //删除napi_ref对象
412    if (addOnData->callback != nullptr) {
413        napi_delete_reference(env, addOnData->callback);
414    }
415
416    //删除异步工作项
417    napi_delete_async_work(env, addOnData->asyncWork);
418    delete addOnData;
419}
420```
421
422修改`device\board\unionman\unionpi_tiger\config\init\arm\init.A311D.cfg `文件,在cmds中添加相关命令
423
424```cfg
425"write /sys/class/gpio/export 380",
426"write /sys/class/gpio/export 381",
427"write /sys/class/gpio/export 382",
428"write /sys/class/gpio/export 383",
429"write /sys/class/gpio/export 384",
430"write /sys/class/gpio/export 385",
431"write /sys/class/gpio/export 386",
432"write /sys/class/gpio/export 387",
433"write /sys/class/gpio/export 388",
434"write /sys/class/gpio/export 389",
435"chmod 666 /sys/class/gpio/gpio380/direction",
436"chmod 666 /sys/class/gpio/gpio381/direction",
437"chmod 666 /sys/class/gpio/gpio382/direction",
438"chmod 666 /sys/class/gpio/gpio383/direction",
439"chmod 666 /sys/class/gpio/gpio384/direction",
440"chmod 666 /sys/class/gpio/gpio385/direction",
441"chmod 666 /sys/class/gpio/gpio386/direction",
442"chmod 666 /sys/class/gpio/gpio387/direction",
443"chmod 666 /sys/class/gpio/gpio388/direction",
444"chmod 666 /sys/class/gpio/gpio389/direction",
445"chmod 666 /sys/class/gpio/gpio380/value",
446"chmod 666 /sys/class/gpio/gpio381/value",
447"chmod 666 /sys/class/gpio/gpio382/value",
448"chmod 666 /sys/class/gpio/gpio383/value",
449"chmod 666 /sys/class/gpio/gpio384/value",
450"chmod 666 /sys/class/gpio/gpio385/value",
451"chmod 666 /sys/class/gpio/gpio386/value",
452"chmod 666 /sys/class/gpio/gpio387/value",
453"chmod 666 /sys/class/gpio/gpio388/value",
454"chmod 666 /sys/class/gpio/gpio389/value"
455```
456
457### **3.NAPI接口应用**
458
459**编写定义.d.ts文件**
460
461编写接口定义@ohos.**nameX**.d.ts文件,放到OpenHarmony SDK目录ets\\${ets_version}\\api目录下。使用SDK 8则${ets_version}为3.1.7.5,SDK 7则为3.0.0.0。
462
463注意@ohos.**nameX**必须和NAPI模块的BUILD.gn文件中ohos_shared_library("**nameX**")指定的动态库名一致。
464
465![3](../figures/gpioled/3.png)
466
467使用DevEco Studio创建标准应用App,并引入模块:
468
469![4](../figures/gpioled/4.png)
470
471![5](../figures/gpioled/5.png)
472
473```js
474import gpioled from '@ohos.gpioled';
475```
476
477页面初始化,同步按钮开关状态与选择的GPIO口对应的值保持一致
478
479```js
480onInit() {
481    this.syncButtonStatus()
482},
483```
484
485```js
486//同步按钮开关状态
487syncButtonStatus() {
488    gpioled.getLedStatus(this.pin).then((result) => {
489        this.status = Boolean(result)
490    })
491}
492```
493
494使用select组件实现选择指定GPIO口的功能
495
496```html
497<select @change="changeGpio" style="font-size : 30fp; weights : 400;">
498    <option value="380">
499        UM_GPIO_01
500    </option>
501    ...
502    <option value="389">
503        UM_GPIO_10
504    </option>
505</select>
506```
507
508当下拉选择新值时,触发change事件并调用changeGpio函数
509
510```js
511changeGpio(msg) {
512    this.pin = Number(msg.newValue)
513    this.syncButtonStatus()
514}
515```
516
517使用switch组件,当开关状态切换时触发事件调用switchChange方法
518
519```html
520<switch @change="switchChange" checked="{{status}}">
521</switch>
522```
523
524```js
525switchChange(e) {
526    if (e.checked) {
527        this.addLedEffect()
528    } else {
529        this.removeLedEffect()
530    }
531}
532```
533
534新增两个方法,封装开启、关闭指定的LED灯
535
536```js
537//开灯
538addLedEffect() {
539    gpioled.setLedStatus(this.pin, gpioled.LED_ON).then((result) => {
540        if (result === 0) {
541            prompt.showToast({
542                message: "开灯"
543            })
544        } else {
545            prompt.showToast({
546                message: "开灯失败"
547            })
548        }
549    })
550},
551
552//关灯
553removeLedEffect() {
554    gpioled.setLedStatus(this.pin, gpioled.LED_OFF).then((result) => {
555        if (result === 0) {
556            prompt.showToast({
557                message: "关灯"
558            })
559        } else {
560            prompt.showToast({
561                message: "关灯失败"
562            })
563        }
564    })
565}
566```
567
568点击File->Project Structure
569
570![6](../figures/gpioled/6.png)
571
572选择自动签名
573
574![7](../figures/gpioled/7.png)
575
576使用Micro USB数据线连接PC与开发板OTG口并接通电源后点击Run即可
577
578![8](../figures/gpioled/8.png)
579
580### **六、构建与烧录**
581
582进入源码根目录,执行如下命令进行版本编译
583
584```
585./build.sh --product-name unionpi_tiger –ccache
586```
587
588编译完成后,效果如图所示:
589
590![9](../figures/gpioled/9.png)
591
592编译完成后需要,进行对镜像进行打包,然后进行烧写。
593执行以下命令固件打包
594
595```
596./device/board/unionman/unionpi_tiger/common/tools/packer-unionpi.sh
597```
598
599固件打包完成,生成路径为编译根目录下的out/unionpi_tiger/packages/phone/images/OpenHarmony.img600
601![10](../figures/gpioled/10.png)
602
603打开烧录工具,使用Micro USB数据线连接PC与开发板OTG口并接通电源,导入烧录包后开始烧录即可(可关闭校验IMG)
604
605![11](../figures/gpioled/11.png)
606
607![12](../figures/gpioled/12.png)
608
609![13](../figures/gpioled/13.png)
610
611系统烧录后,如系统版本未变,可直接使用hdc_std工具将新构建的out/unionpi_tiger/packages/phone/system/lib/module/libgpioled.z.so文件复制替换开发板系统中的/system/lib/module/libgpioled.z.so文件,提升验证效率。参考命令如下:
612
613```
614hdc_std shell mount -o remount,rw /          //重新挂载为已经挂载了的文件系统(以读写权限挂载)
615hdc_std file send libgpioled.z.so /system/lib/module/
616```
617
618*使用hdc工具还有另一个好处就是调试过程中不需要将Micro USB数据线在开发板OTG口和DEBUG口来回切换,不管是烧录,串口调试还是应用安装都是连接OTG口就行*
619
620**hdc_std工具获取方式:**
621
622通过OpenHarmony sdk获取,hdc_std在sdk的toolchains目录下,例如笔者的hdc工具存放路径为:C:\Users\\haoyuan.chen\AppData\Local\OpenHarmony\Sdk\toolchains\3.1.7.5
623
624![14](../figures/gpioled/14.png)
625
626将其添加到环境变量,显示以下结果即可
627
628![15](../figures/gpioled/15.png)
629
630更多资料请参考:[OpenAtom OpenHarmony](https://docs.openharmony.cn/pages/v3.1/zh-cn/device-dev/subsystems/subsys-toolchain-hdc-guide.md/)
631
632### **七、演示效果**
633
634![img](https://alliance-communityfile-drcn.dbankcdn.com/FileServer/getFile/cmtybbs/251/810/102/0260086000251810102.20220831114740.49121476565256150730640116299211:50530830041038:2800:B3855CF1044A2E105F3C688C549567EBCCBC6E74816C308BEBA9D7DB607CFD6E.gif)
635
636### **八、参考资料**
637
638[标准设备应用开发—Native Api-开源基础软件社区-51CTO.COM](https://ost.51cto.com/activity/59)
639
640
641
642