Dart API 扩展

简介: Dart VM 里面其实提供了 dart 跟 cpp 之间的相互调用接口,开发者可以基于这个接口自行扩展 dart 的 api, 本文描述了如何方便的扩展,介绍了 Dart 和 Cpp 直接调用的基本使用方法。

Dart API 扩展

dart vm 里面其实提供了 dart 跟 cpp 之间的相互调用接口,开发者可以基于这个接口自行扩展 dart 的 api, 那么就来看看 dart vm 以及 flutter 是如何做到的。

Dart VM

Dart VM中提供了以下接口:

DART_EXPORT Dart_Handle
Dart_SetNativeResolver(Dart_Handle library,
                       Dart_NativeEntryResolver resolver,
                       Dart_NativeEntrySymbol symbol);

通过以上的API就可以注册进去一个扩展的dart库:

Dart_SetNativeResolver(Dart_LookupLibrary(ToDart("dart:test")),
                        CustomNativeEntryResolver,
                        CustomNativeEntrySymbol);

Dart VM会在使用的时候调用注册时候的两个函数来查找函数:

static void customLogFunc(Dart_NativeArguments arguments) {
    // LOG("invoke custom log func");
}

static Dart_NativeFunction CustomNativeEntryResolver(Dart_Handle name,
                                              int num_of_arguments,
                                              bool* auto_setup_scope) {
    return &customLogFunc;
}

static const uint8_t* CustomNativeEntrySymbol(Dart_NativeFunction native_function) {
    return (const uint8_t *)"customLogFunc";
}

Dart_NativeEntryResolver

typedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name,
                                                        int num_of_arguments,
                                                        bool* auto_setup_scope);

resolver是根据name函数名来查找函数地址Dart_NativeFunction的,num_of_arguments是这个cpp的函数参数个数,auto_setup_scope是表示这个符号是否是要成为 Dart API,设置为trueVM就会自动启用,false就需要在调用的时候才启用。

Dart_NativeEntrySymbol

typedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf);

symbol是根据函数地址或者到函数名。


通过以上的方式就可以在Dart中直接调用customLogFunc函数了,但是我们看到在CPP中的customLogFunc实现还是带有Dart_NativeArguments这一Dart VM浓厚色彩的风格,如何才能将CPP的实现参数自动展开然后像customLogFunc(a, b)这样方便的使用呢。

Flutter

Flutter使用tonic给出了一个完美的解决方案,来看一下使用tonic::DartLibraryNatives是如何为Dart扩展接口的,DartLibraryNatives的接口:

  void Register(std::initializer_list<Entry> entries);

  Dart_NativeFunction GetNativeFunction(Dart_Handle name,
                                        int argument_count,
                                        bool* auto_setup_scope);
  const uint8_t* GetSymbol(Dart_NativeFunction native_function);

只需要构建一个DartLibraryNatives对象,然后通过Register接口传入需要注册的信息

natives->Register({{"Canvas_constructor", Canvas_constructor, 6, true})

再把DartLibraryNatives对象的两个解析信息的函数通过Dart_SetNativeResolver提供给Dart VM即可:

Dart_SetNativeResolver(Dart_LookupLibrary(ToDart("dart:Canvas")),
                        native->GetNativeFunction, native->GetSymbol));

但这也只是一个简单的封装,tonic::DartLibraryNatives负责管理了注册的信息而已,函数调用之后的参数展开还并没有完成。

Dart_NativeArguments 参数和返回值的处理

CPP的实现中,一般是类的形式:

void Canvas::saveLayer(double left,
                       double top,
                       double right,
                       double bottom,
                       const Paint& paint,
                       const PaintData& paint_data)

Dart调用的时候参数和返回值又是通过Dart_NativeArguments传递的,所以这里需要一个中间层来屏蔽细节。
所以在tonic中提供了一组封装,解决Dart_NativeArguments调用参数和返回值的问题:

#define DART_NATIVE_CALLBACK(CLASS, METHOD)                 \
  static void CLASS##_##METHOD(Dart_NativeArguments args) { \
    tonic::DartCall(&CLASS::METHOD, args);                  \
  }

#define DART_NATIVE_CALLBACK_STATIC(CLASS, METHOD)          \
  static void CLASS##_##METHOD(Dart_NativeArguments args) { \
    tonic::DartCallStatic(&CLASS::METHOD, args);            \
  }

通过这两个宏,就可以实现函数的封装和Dart_NativeArguments的处理,只需要使用 DART_NATIVE_CALLBACK(Canvas, saveLayer) 就会生成以下方法:

  static void Canvas_saveLayer(Dart_NativeArguments args) {
    tonic::DartCall(&Canvas::saveLayer, args);
  }

tonic::DartCall会处理好参数和返回值,直接调用Canvas::saveLayer(left, top, right, bottom, paint, paint_data),所以注册的时候只需要使用通过DART_NATIVE_CALLBACK宏生成的Canvas_saveLayer函数即可。

注册信息的生成

通过tonic::DartLibraryNatives注册CPP信息的时候是要提供四个信息的,函数名,地址,参数个数以及auto_setup_scope的标记值。

natives->Register({"Canvas_saveLayer", Canvas_saveLayer, 6, true})

这一组信息tonic也提供了一组宏来自动生成:

#define DART_REGISTER_NATIVE(CLASS, METHOD) \
  {#CLASS "_" #METHOD, CLASS##_##METHOD,    \
   tonic::IndicesForSignature<decltype(&CLASS::METHOD)>::count + 1, true},

#define DART_REGISTER_NATIVE_STATIC(CLASS, METHOD)                           \
  {                                                                          \
#CLASS "_" #METHOD, CLASS##_##METHOD,                                    \
        tonic::IndicesForSignature < decltype(&CLASS::METHOD)> ::count, true \
  }

所以使用DART_REGISTER_NATIVE(Canvas, saveLayer)宏就可以生成注册需要的一组信息出来。

Dart 封装

有了tonic提供的一组接口,就可以做到使用简单的几行代码来注册接口

#define FOR_EACH_BINDING(V)         \
  V(Canvas, save)                   \
  V(Canvas, saveLayerWithoutBounds) \
  V(Canvas, saveLayer)
  
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)

natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)});

而我们只需要在CPP中完成代码的实现就可以了。
当然也需要在Dart中声明这个接口,非常类似 Java/Jni 的实现,以下是Dart代码:

  void _saveLayer(double left,
                  double top,
                  double right,
                  double bottom,
                  List<dynamic> paintObjects,
                  ByteData paintData) native 'Canvas_saveLayer';

Dart中直接调用_saveLayer接口就会使用到CPPCanvas::saveLayer方法。

反射

Dart VM也提供了CPP调用Dart的能力,也是类型Java的反射机制,主要是以下三个API,使用起来也比较简单,就不详细介绍了

DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
Dart_Invoke(Dart_Handle target,
            Dart_Handle name,
            int number_of_arguments,
            Dart_Handle* arguments);


DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
Dart_InvokeClosure(Dart_Handle closure,
                   int number_of_arguments,
                   Dart_Handle* arguments);


DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
Dart_InvokeConstructor(Dart_Handle object,
                       Dart_Handle name,
                       int number_of_arguments,
                       Dart_Handle* arguments);
目录
相关文章
|
7月前
|
JavaScript 前端开发 API
Visual Studio Code Active File in StatusBar 扩展以及 Extension API 概述
Visual Studio Code Active File in StatusBar 扩展以及 Extension API 概述
38 0
|
29天前
|
安全 API 开发者
构建高效可扩展的RESTful API服务
在数字化转型的浪潮中,构建一个高效、可扩展且易于维护的后端API服务是企业竞争力的关键。本文将深入探讨如何利用现代后端技术栈实现RESTful API服务的优化,包括代码结构设计、性能调优、安全性强化以及微服务架构的应用。我们将通过实践案例分析,揭示后端开发的最佳实践,帮助开发者提升系统的响应速度和处理能力,同时确保服务的高可用性和安全。
29 3
|
1月前
|
缓存 前端开发 API
构建高效可扩展的RESTful API:后端开发的最佳实践
【2月更文挑战第30天】 在现代Web应用和服务端架构中,RESTful API已成为连接前端与后端、实现服务间通信的重要接口。本文将探讨构建一个高效且可扩展的RESTful API的关键步骤和最佳实践,包括设计原则、性能优化、安全性考虑以及错误处理机制。通过这些实践,开发者可以确保API的健壮性、易用性和未来的可维护性。
|
1月前
|
XML JSON API
通过Flask框架创建灵活的、可扩展的Web Restful API服务
通过Flask框架创建灵活的、可扩展的Web Restful API服务
|
6月前
|
JavaScript 前端开发 API
通过一个实际的 Fiori Elements 扩展开发需求,介绍什么是 SAP Fiori Extension API 试读版
通过一个实际的 Fiori Elements 扩展开发需求,介绍什么是 SAP Fiori Extension API 试读版
30 0
|
7月前
|
人工智能 JSON Serverless
课时10:典型案例3:十分钟搭建弹性可扩展的 Web API(二)
典型案例3:十分钟搭建弹性可扩展的 Web API
1071 0
|
7月前
|
JSON 监控 Serverless
课时10:典型案例3:十分钟搭建弹性可扩展的 Web API
课时10:典型案例3:十分钟搭建弹性可扩展的 Web API
287 0
|
9月前
|
API
使用hutool中的树形结构api时自定义扩展属性
使用hutool的树结构工具时,其内置的TreeNode类只有固定的几个属性,想添加自定义属性怎么办?
|
10月前
|
缓存 安全 API
API技术解析:构建高效、可扩展的应用接口系统
在当前快速发展的互联网时代,构建高效、可扩展的应用接口系统对于企业的成功至关重要。而API技术作为实现应用程序之间数据交互的关键技术,成为了许多企业和开发者的首选。本文将深入探讨API技术在构建高效、可扩展的应用接口系统中的作用和应用,旨在帮助读者更好地理解和应用API技术。
126 0
|
运维 资源调度 监控
典型案例 3:十分钟搭建弹性可扩展的 Web API |学习笔记
快速学习 典型案例 3:十分钟搭建弹性可扩展的 Web API
215 0