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);
目录
相关文章
|
2月前
|
Java API Spring
打造未来电商新引擎:揭秘Java可扩展API设计,让支付与物流灵活如丝,引领电商时代潮流!
【8月更文挑战第30天】本文通过电商平台案例,探讨了如何设计可扩展的Java API。首先定义支付和物流服务的接口与抽象类,然后实现具体服务,接着引入工厂模式或依赖注入管理服务实例,最后通过配置实现灵活扩展。这种设计确保了应用架构的灵活性和长期稳定性。
49 3
|
3月前
|
JSON Java API
如何设计可扩展的RESTful API?
如何设计可扩展的RESTful API?
|
5月前
|
缓存 前端开发 API
构建可扩展的API:REST vs GraphQL
【5月更文挑战第14天】在API设计中,REST和GraphQL是两种主要的架构风格。REST基于HTTP协议,强调资源的无状态性和客户端-服务器模型,适合简单CRUD操作。而GraphQL提供更灵活的数据查询,允许客户端精确指定需求,减少HTTP请求和数据冗余,适合复杂场景和高性能需求。选择时要考虑项目需求、技术栈、性能和团队经验。
|
16天前
|
监控 安全 API
Docker + .NET API:简化部署和扩展
Docker + .NET API:简化部署和扩展
29 0
|
17天前
|
监控 安全 API
最完美的扩展Docker + .NET API:简化部署和扩展
最完美的扩展Docker + .NET API:简化部署和扩展
42 0
|
5月前
|
设计模式 Java API
Java 可扩展 API 设计:打造灵活的应用架构
【4月更文挑战第27天】设计可扩展的 API 是构建灵活、易于维护的应用程序架构的关键。Java 提供了丰富的工具和技术来实现这一目标,使开发者能够构建具有高度可扩展性的应用程序。
111 4
|
5月前
|
存储 缓存 安全
API在Visual Basic中的应用:连接外部服务与扩展功能
【4月更文挑战第27天】本文探讨了在Visual Basic中使用API连接外部服务和扩展功能的方法,涵盖了API的基本概念、种类及如何使用本地和Web API。通过DllImport调用本地API,利用HttpClient和WebClient与Web API交互,同时强调了第三方API的使用和SOA架构中的API角色。安全性、性能优化和错误处理是实践中的关键点。案例研究和最佳实践有助于开发者更有效地利用API,提升Visual Basic应用程序的功能和灵活性。随着API技术的发展,Visual Basic将持续支持开发者创造更强大的应用。
55 0
|
2月前
|
XML JSON API
RESTful API设计最佳实践:构建高效、可扩展的接口
【8月更文挑战第17天】RESTful API设计是一个涉及多方面因素的复杂过程。通过遵循上述最佳实践,开发者可以构建出更加高效、可扩展、易于维护的API。然而,值得注意的是,最佳实践并非一成不变,随着技术的发展和业务需求的变化,可能需要不断调整和优化API设计。因此,保持对新技术和最佳实践的关注,是成为一名优秀API设计师的关键。
|
2月前
|
Java 数据库连接 缓存
Hibernate性能调优:五大秘籍,让应用效能飙升,告别慢如蜗牛的加载,体验丝滑般流畅!
【8月更文挑战第31天】本文深入探讨了提升Hibernate应用性能的五大技巧,包括选择合适的缓存策略、优化查询语句、合理使用Eager与Lazy加载、批量操作与事务管理以及利用索引和数据库优化。通过正确配置多级缓存、分页查询、延迟加载、批量处理及合理创建索引,能够显著提高应用响应速度与吞吐量,改善用户体验。这些技巧需根据具体应用场景灵活调整,以实现最佳性能优化效果。
105 0
|
2月前
|
存储 缓存 运维
平稳扩展:可支持RevenueCat每日12亿次API请求的缓存
平稳扩展:可支持RevenueCat每日12亿次API请求的缓存
41 1