精华内容
下载资源
问答
  • Dart FFI的简单分析

    千次阅读 2020-05-15 20:11:56
    本文是阅读dart ffi相关的代码记录。 源码的位置 分成几个部分,dart的代码在sdk/lib/ffi下,有4个文件 annotations.dart dynamic_library.dart : 定义DynamicLibrary类,用于加载so库 ffi.dart 定义Pointer类 ...

    本文是阅读dart ffi相关的代码记录。

    源码的位置

    分成几个部分,dart的代码在sdk/lib/ffi下,有4个文件

    • annotations.dart
    • dynamic_library.dart : 定义DynamicLibrary类,用于加载so库
    • ffi.dart 定义Pointer类
    • native_type.dart 定义NativeType

    Pointer相关的实际实现的代码,
    在runtime/lib/ffi_path.dart, 定义的对应的native实现,关键的如

    • Pointer.asFunction对应 Ffi_asFunction
    • fromFunction 对应Ffi_fromFunction
    • 等等

    runtime/lib/ffi_dynamic_library_patch.dart定义DynamicLibrary的实现

    • DynamicLibrary._open对应 Ffi_dl_open
    • DynamicLibrary.lookup 对应 Ffi_dl_lookup
    • 等等

    对应的c++类
    runtime/lib/ffi_dynamic_library.cc 实现open, loopkup等函数
    runtime/lib/ffi.cc 实现fromFunction, asFunction等函数

    lookup取得函数指针

    dart的用法

    import 'dart:ffi' as ffi;
    import 'dart:io' show Platform;
    
    // FFI signature of the hello_world C function
    typedef hello_world_func = ffi.Void Function();
    // Dart type definition for calling the C foreign function
    typedef HelloWorld = void Function();
    
    main() {
      // Open the dynamic library
      var path = "./hello_library/libhello.so";
      if (Platform.isMacOS) path = './hello_library/libhello.dylib';
      if (Platform.isWindows) path = r'hello_library\Debug\hello.dll';
      final dylib = ffi.DynamicLibrary.open(path);
      // Look up the C function 'hello_world'
      final HelloWorld hello = dylib
          .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
          .asFunction();
      // Call the function
      hello();
    
    DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
      // 取得type_args, 指 lookup的模板参数0:上面的例子ffi.NativeFunction<hello_world_func>>
      GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
      // 对象指针,即this指针:DynamicLibrary实现
      GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
      //函数名
      GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
                                   arguments->NativeArgAt(1));
    
      void* handle = dlib.GetHandle();
      // 调用dlsysm获得函数值
      const intptr_t pointer = reinterpret_cast<intptr_t>(
          ResolveSymbol(handle, argSymbolName.ToCString()));
    
      // TODO(dacoharkes): should this return Object::null() if address is 0?
      // https://github.com/dart-lang/sdk/issues/35756
      // 创建一个native指针,使用int类型保存
      // type_arg确定参数类型,pointer保存地址
      RawPointer* result =
          Pointer::New(type_arg, Integer::Handle(zone, Integer::New(pointer)));
      // result对应dart中的ffi.NativeFunction<hello_world_func>类型
      return result;
    }
    

    asFunction的实现

    DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) {
      //获得 Pointer对象,如上面的例子ffi.NativeFunction<hello_world_func>
      GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
      AbstractType& pointer_type_arg = // 获得类型信息
          AbstractType::Handle(pointer.type_argument());
      ASSERT(IsNativeFunction(pointer_type_arg));
      // 获得asFunction的模板参数
      // asFunction的声明: external R asFunction<@DartRepresentationOf("T") R extends Function>(); 获取其中的R类型,上面的例子 HelloWorld类型
      GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
      ASSERT(DartAndCTypeCorrespond(pointer_type_arg, type_arg));
    
      // dart_signature 获得R的签名,上面例子 HelloWorld类型
      Function& dart_signature = Function::Handle(Type::Cast(type_arg).signature());
      // Pointer类的类型参数,即NativeType的类型
      TypeArguments& nativefunction_type_args =
          TypeArguments::Handle(pointer_type_arg.arguments());
      AbstractType& nativefunction_type_arg =
          AbstractType::Handle(nativefunction_type_args.TypeAt(0));
      Function& c_signature =
          Function::Handle(Type::Cast(nativefunction_type_arg).signature());
      //  生成内部函数接口,TrampolineFunction创建一个跳板函数对象,参数从dart_signature转换为c_signature
      Function& function =
          Function::Handle(TrampolineFunction(dart_signature, c_signature));
    
      // Set the c function pointer in the context of the closure rather than in
      // the function so that we can reuse the function for each c function with
      // the same signature.
      // C函数指针包装在context中
      Context& context = Context::Handle(Context::New(1));
      context.SetAt(0, Integer::Handle(zone, pointer.GetCMemoryAddress()));
    
      // 包装成Function类,在dart中可以直接调用
      RawClosure* raw_closure =
          Closure::New(Object::null_type_arguments(), Object::null_type_arguments(),
                       function, context, Heap::kOld);
    
      return raw_closure;
    }
    

    TrampolineFunction

    指定RawFunction::kFfiTrampoline类型,专门用于ffi调用

    // TODO(dacoharkes): Cache the trampolines.
    // We can possibly address simultaniously with 'precaching' in AOT.
    static RawFunction* TrampolineFunction(const Function& dart_signature,
                                           const Function& c_signature) {
      Thread* thread = Thread::Current();
      Zone* zone = thread->zone();
      String& name =
          String::ZoneHandle(Symbols::New(Thread::Current(), "FfiTrampoline"));
      const Library& lib = Library::Handle(Library::FfiLibrary());
      const Class& owner_class = Class::Handle(lib.toplevel_class());
      Function& function =
          Function::Handle(zone, Function::New(name, RawFunction::kFfiTrampoline,
                                               /*is_static=*/true,
                                               /*is_const=*/false,
                                               /*is_abstract=*/false,
                                               /*is_external=*/false,
                                               /*is_native=*/false, owner_class,
                                               TokenPosition::kMinSource));
      function.set_is_debuggable(false);
      function.set_num_fixed_parameters(dart_signature.num_fixed_parameters());
      function.set_result_type(AbstractType::Handle(dart_signature.result_type()));
      function.set_parameter_types(Array::Handle(dart_signature.parameter_types()));
    
      // The signature function won't have any names for the parameters. We need to
      // assign unique names for scope building and error messages.
      const intptr_t num_params = dart_signature.num_fixed_parameters();
      const Array& parameter_names = Array::Handle(Array::New(num_params));
      for (intptr_t i = 0; i < num_params; ++i) {
        if (i == 0) {
          name = Symbols::ClosureParameter().raw();
        } else {
          name = Symbols::NewFormatted(thread, ":ffiParam%" Pd, i);
        }
        parameter_names.SetAt(i, name);
      }
      function.set_parameter_names(parameter_names);
      function.SetFfiCSignature(c_signature);
    
      return function.raw();
    }
    
    展开全文
  • dart ffiWhat would you do if you wanted your Flutter app to be as much performant as possible, or there was no package in pub that you need? Probably, you would use native Java/Objective-C libraries a...

    dart ffi

    What would you do if you wanted your Flutter app to be as much performant as possible, or there was no package in pub that you need? Probably, you would use native Java/Objective-C libraries and communicate with them using Platform Channels. But what if both platforms share the same business logic? Surely, nobody wants to write the same code twice in two different languages.

    如果您希望Flutter应用程序具有尽可能高的性能,或者在酒吧中没有需要的软件包,该怎么办? 可能您将使用本机Java / Objective-C库,并使用平台通道与它们进行通信。 但是,如果两个平台共享相同的业务逻辑怎么办? 当然,没有人愿意用两种不同的语言编写两次相同的代码。

    The purpose of this article is to help Flutter developers set up their project to use a native C or C++ library and write a single code base that uses it. As an example, the article describes the process of adding the OpenCV library to a Flutter app project.

    本文的目的是帮助Flutter开发人员设置其项目以使用本机C或C ++库,并编写使用该库的单个代码库。 例如,本文介绍了将OpenCV库添加到Flutter应用程序项目的过程。

    这是什么一回事呢? (What is it all about?)

    Dart Foreign Function Interface (FFI) allows us to call native C/C++ functions on both iOS and Android platforms without using Platform Channels or making Java/Objective-C bindings. That leads to having a single code base written in C with zero platform-specific code.

    Dart外部功能接口(FFI)允许我们在iOS和Android平台上调用本机C / C ++函数,而无需使用平台通道或进行Java / Objective-C绑定。 这就导致了用C编写的单一代码库以及零个特定于平台的代码。

    OpenCV is a computer vision library that contains a lot of services used for image processing. It is mainly written in C++.

    OpenCV是一个计算机视觉库,其中包含许多用于图像处理的服务。 它主要是用C ++编写的。

    创建一个插件 (Creating a plugin)

    Even though it’s not required, it’s better to create a Flutter plugin to separate all task-specific stuff from the main application. Run flutter create — template=plugin native_opencv command to create it.Next, we need to update the app’s dependency list in pubspec.yaml with a newly created plugin and ffi package:

    即使不是必需的,也最好创建Flutter插件以将所有特定于任务的内容与主应用程序分开。 运行flutter create — template=plugin native_opencv命令创建它。接下来,我们需要使用新创建的插件和ffi包更新pubspec.yaml中应用程序的依赖项列表:

    dependencies:
    native_opencv:
    path: native_opencv
    ffi: ^0.1.3

    ffi package comes in handy for working with C UTF-8/UTF-16 strings.

    ffi软件包可用于处理C UTF-8 / UTF-16字符串。

    在iOS上设置插件 (Setting up the plugin on iOS)

    OpenCV is distributed as a framework and it has to be included in the plugin along with C++ library. OpenCV also requires AVFoundation framework. Place the framework in plugin’s ios folder (or use a symbolic link) and add lines to .podspec file:

    OpenCV作为框架分发,必须与C ++库一起包含在插件中。 OpenCV还需要AVFoundation框架。 将框架放置在插件的ios文件夹中(或使用符号链接),并在.podspec文件中添加以下行:

     # telling CocoaPods not to remove framework
    s.preserve_paths = ‘opencv2.framework’ # telling linker to include opencv2 framework
    s.xcconfig = { ‘OTHER_LDFLAGS’ => ‘-framework opencv2’ } # including OpenCV framework
    s.vendored_frameworks = ‘opencv2.framework’ # including native framework
    s.frameworks = ‘AVFoundation’ # including C++ library
    s.library = ‘c++’

    If your library doesn’t come as a framework, you need to only include C++ library:

    如果您的库不是作为框架提供的,则只需包含C ++库:

     # including C++ library
    s.library = ‘c++’

    在Android上设置插件 (Setting up the plugin on Android)

    Things are a bit harder for Android platform because it doesn’t support C++ out-of-the-box, but rather with a help of the Android Native Development Kit (NDK).Native library and its headers to be included in the plugin’s android module. Shared libraries should be (but not necessary) placed in jniLibs folder. Headers can be placed anywhere (e.g., in the root folder of the plugin).

    对于Android平台来说事情有点困难,因为它不支持现成的C ++,而是借助Android Native Development Kit(NDK)的帮助。本地库及其标头包含在插件的android中模块。 共享库应该(但不是必须)放在jniLibs文件夹中。 标题可以放在任何地方(例如,在插件的根文件夹中)。

    sdk/native/libs/* -> native_opencv/android/src/main/jniLibs/*
    sdk/native/jni/include -> native_opencv

    Libraries placed in jniLibs folder are automatically included in apk. If you want to use any other directory, you have to specify in build.gradle of the plugin:

    放置在jniLibs文件夹中的库自动包含在apk中。 如果要使用其他目录,则必须在插件的build.gradle中指定:

    sourceSets {
    main {
    jniLibs.srcDirs = ["libs"]
    }
    }

    At this point plugin’s structure looks like this:

    此时,插件的结构如下所示:

    native_opencv
    - android
    - src
    - main
    - jniLibs
    - arm64-v8a
    - libopencv_java4.so
    - armeabi-v7a
    - libopencv_java4.so
    - x86
    - libopencv_java4.so
    - x86_64
    - libopencv_java4.so
    - include
    - opencv2
    - ...

    In order to build and link the library and our code from native_opencv.cpp must be added to cmake (or ndk-build). Create CMakeLists.txt configuration file in android folder of the plugin:

    为了构建和链接库,必须将native_opencv.cpp中的代码添加到cmake(或ndk-build)中。 在插件的android文件夹中创建CMakeLists.txt配置文件:

    cmake_minimum_required(VERSION 3.4.1)include_directories(../include)
    add_library(lib_opencv SHARED IMPORTED)
    set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java4.so)add_library(native_opencv SHARED ../ios/Classes/native_opencv.cpp)
    target_link_libraries(native_opencv lib_opencv)

    And specify this config, as well as some flags and arguments in build.gradle:

    并在build.gradle中指定此配置以及一些标志和参数:

    android {
    defaultConfig {
    externalNativeBuild {
    cmake {
    // Enabling exceptions, RTTI
    // And setting C++ standard version
    cppFlags '-frtti -fexceptions -std=c++11'
    // Shared runtime for shared libraries
    arguments "-DANDROID_STL=c++_shared"
    }
    }
    }
    externalNativeBuild {
    cmake {
    path "CMakeLists.txt"
    }
    }
    }

    If your library doesn’t provide shared libraries, you have to link it statically and use C++ static runtime.

    如果您的库不提供共享库,则必须静态链接它并使用C ++静态运行时。

    Your C code has to be placed inside of plugin’s ios/Classes folder because CocoaPods doesn’t allow including source code from anywhere above .podspec file, though it’s not a problem for Android build system. Files placed in Classes folder are automatically compiled by the Xcode build system; therefore we don’t need to specify them explicitly.

    您的C代码必须放置在插件的ios / Classes文件夹中,因为CocoaPods不允许在.podspec文件上方的任何位置包含源代码,尽管这对于Android构建系统而言不是问题。 放置在Classes文件夹中的文件由Xcode构建系统自动编译; 因此,我们无需明确指定它们。

    Let’s create a .cpp file with some sample code:

    让我们用一些示例代码创建一个.cpp文件:

    native_opencv.cpp
    native_opencv.cpp

    To make sure OpenCV’s core, imgproc, imgcodecs modules are all linked and working, process_image function will process an image: load it, binarize it, find contours, draw contours on a binarized image and save it. version function will return a pointer to a string that represents OpenCV’s version.

    为了确保OpenCVcoreimgproc和 imgcodecs模块都已链接并正常工作, process_image函数将处理图像:加载图像,对图像进行二值化,找到轮廓,在二值化图像上绘制轮廓并保存。 版本函数将返回一个指向代表OpenCV版本的字符串的指针。

    Afterward, we need to “connect” with Dart code by binding it to native code. Create .dart file in the plugin:

    之后,我们需要通过将Dart代码绑定到本机代码来“连接”它。 在插件中创建.dart文件:

    native_opencv.dart
    native_opencv.dart

    Now it’s time to call these functions and check the result:

    现在是时候调用这些函数并检查结果了:

    Image for post
    The result of calling native code on iOS
    在iOS上调用本机代码的结果
    Image for post
    The result of calling native code on Android
    在Android上调用本机代码的结果

    Yay, we can see OpenCV’s version and processed images now. There were no linking/compilation errors.

    是的,我们现在可以看到OpenCV版本和已处理的图像。 没有链接/编译错误。

    让UI线程休息 (Giving UI thread a rest)

    Heavy processing might take some time (sometimes even dozens of seconds) and block the application’s UI thread, furthermore, an app stops responding to any user actions and rendering new frames. This problem is pretty easy to solve with dart:isolate library. A new isolate can be created to do some massive work (e.g., complex image processing). The user will still be able to interact with an app.

    繁重的处理可能会花费一些时间(有时甚至数十秒)并阻塞应用程序的UI线程,此外,应用程序停止响应任何用户操作并呈现新帧。 使用dart:isolate库很容易解决这个问题。 可以创建一个新的隔离区来完成一些繁重的工作(例如,复杂的图像处理)。 用户仍将能够与应用交互。

    Let’s define Isolate’s entry point and arguments:

    让我们定义隔离的入口点和参数:

    And add a simple flag to show a loader while an isolate is working, spawn it and wait for the signal:

    并添加一个简单的标志以在隔离工作时显示加载程序,生成它并等待信号:

    To simulate actual heavy work image processing will iterate ten more times:

    为了模拟实际的繁重工作,图像处理将重复十次以上:

    Image for post
    The result of running native code in Isolate
    在隔离中运行本机代码的结果

    添加平台特定的代码 (Adding platform-specific code)

    Sometimes there’s still need in adding platform-specific code. For example, I wanted to log code execution time, but Android sends stdout data to /dev/null, i.e., printf call gets ignored. To avoid that, the Android logging library has to be used.

    有时仍然需要添加特定于平台的代码。 例如,我想记录代码执行时间,但是Android将stdout数据发送到/ dev / null ,即printf调用被忽略。 为了避免这种情况,必须使用Android日志库。

    Editing CMakeLists.txt to link a logging library:

    编辑CMakeLists.txt以链接日志记录库:

    find_library(log-lib log)target_link_libraries(native_opencv lib_opencv ${log-lib})

    Android NDK defines __ANDROID__ flag by default, so it can be used in preprocessor directives to detect the platform:

    Android NDK默认情况下定义__ANDROID__标志,因此可以在预处理器指令中使用它来检测平台:

    And now logging works on both platforms.

    现在,日志记录在这两个平台上都可以使用。

    Dart FFI is a powerful tool that gives an opportunity to use one of many C libraries and make your apps really fast while sharing code between different platforms. But keep in mind that dart:ffi is still in beta and missing some wanted features like support of nested structures or inline arrays.

    Dart FFI是一个功能强大的工具,它使您有机会使用许多C库之一,并在不同平台之间共享代码的同时,使您的应用程序真正快速。 但请记住, dart:ffi 仍处于测试阶段,并且缺少一些所需的功能,例如对嵌套结构内联数组的支持

    Thank you for reading and feel free to leave any feedback. The source code for the app is available here:

    感谢您的阅读,并随时留下任何反馈。 该应用程序的源代码位于:

    https://www.twitter.com/FlutterComm

    https://www.twitter.com/FlutterComm

    Image for post

    翻译自: https://medium.com/flutter-community/integrating-c-library-in-a-flutter-app-using-dart-ffi-38a15e16bc14

    dart ffi

    展开全文
  • OpenGL FFI Dart的 4.6 FFI绑定。支持Linux,macOS和Windows。
  • Dart的GSettings
  • 使用dart:ffi实验领域绑定。 该演示使用类似 C 的接口包装数据库(C++),以构建简单的键/值存储,并将其公开给 dart。 建造 使用 C 包装器构建包含 Realm 的共享对象库 git clone ...
  • go_dart_ffi_example-源码

    2021-05-22 08:24:01
    从Go goroutine异步调用Dart的演示。 $ go build -o godart.so -buildmode=c-shared $ dart godart.dart Go: Starting some asynchronous work Go: Returning to Dart GO: 2 seconds passed Received: 1 from Go ...
  • Dart对C语言的调用我个人感觉比Java的jni要简单得多,用不着还在C语言中创建出对应的函数才能调用,Dart都能直接调用C语言原有的那些函数,...在使用ffi的时候,只需要把C语言或者C++语言的方法或者函数与Dart中一一...

    Dart对C语言的调用我个人感觉比Java的jni要简单得多,用不着还在C语言中创建出对应的函数才能调用,Dart都能直接调用C语言原有的那些函数,因为java jni调用C语言的完整流程我也上手写过,Dart还是简易多了,不用打乱已有C语言的结构

    上一篇
    Dart:ffi上手一:So库编译篇

    Dart:ffi思想

    在使用ffi的时候,只需要把C语言或者C++语言的方法或者函数与Dart中一一对应起来

    yaml导入ffi包

    在上手ffi了半天,官方的example弄到Flutter项目报错,提示import ‘package:ffi/ffi.dart’;没有找到,我还以为是Flutter中的Dart没有这个包,因为我想到最初Dart支持ffi的时候Flutter中的Dart Sdk没有,然后自己**了半天,发现这是个Dart Packages
    所以

    pubspec.yaml

      ffi: ^0.1.3
    

    调用C语言方法(void)

    由于是在Flutetr中使用ffi,并不是在纯Dart中,所以C语言的任何Printf都是看不到的,不会反馈到Flutter的控制台

    一个简单的C语言方法

    void hello_word(){
        printf("hello word");
    }
    

    编译好后,重新调试一下App,才会更新新编译的So库
    需要这两个包的导入

    Dart:

    import 'dart:ffi';
    import 'package:ffi/ffi.dart';
    

    参照官方,如果要调用一个C语言函数写上两个typedef,这的确会带来很多方便

    typedef hello_word=Void Function();
    

    这里一定要注意,Void的V是V不是v,这部分声明C语言中的函数,涉及到的所有类型必须全部用ffi中对应的NativeType,而这个原生函数的声明后不是拿来调用的,切记,不是拿来调用的,是要用它跟下面Dart的方法绑定起来
    接下来

    typedef HelloWord=void Function();
    

    这就是Dart中需要调用的方法,也有坑,这部分的原则就是,如果Dart有原生的那种类型就用dart已经有的类型,如果没有,还是得用NativeType

    举个例子
    原生函数

    int add(int a,int b)
    

    这个时候对应的dart函数该怎么写呢,原生的int,dart也有,所以dart韩式也是返回int,形参中也都是int,所以

    typedef Add=int Function(int a,int b);
    

    涉及到其他类型下面说,接下来

    final dylib = DynamicLibrary.open("libterm.so");
    

    这行的作用就是找到对应的so库,记住这儿是相对路径,找的就是打包进apk中的so,与libflutter.so同级
    它的作用跟java中jni的loadlibrary类似
    导入了一个动态库

    找到原生方法的指针

     final helloWordPointer = dylib
            .lookup<NativeFunction<hello_word>>('hello_word');
    

    用了刚才的dylib对象的lookup方法,中间的一堆<<>>都是泛型,NativeFunction的泛型就是第一个typedef,也是第一个typedef唯一用到的地方,最后lookup需要传入一个字符串,这个字符串就语言中的方法或者函数的名字,它返回的是一个Pointer类型,就是指针的意思,dart用了自己对应的类型来表示原生中的一些类型
    然后定义并且初始化第二个typedef的方法

    HelloWord helloWord = helloWordPointer.asFunction<HelloWord>();
    

    通过Ponter对象的asFunction()方法初始化了这个helloWord对象,这个helloWord就是就是一个普通的void
    调用它

    helloword();
    

    这部分的打印是看不到的

    调用C语言带返回值的int函数

    C:

    int add(int a,int b){
        return a+b;
    }
    

    上面也提到了这个

    两个typedef

    typedef add_func=Int32 Function(Int32 a,Int32 b);
    typedef Add=int Function(int a,int b);
    

    执行代码

        var path = 'libterm.so';
        final dylib = DynamicLibrary.open(path);
        final addPinter = dylib.lookup<NativeFunction<add_func>>("add");
        Add add = addPinter.asFunction<Add>();
        print("${add(1,2)}");
        print("${add(3,5)}");
        print("${add(6,9)}");
    

    控制台输出,避免内存泄漏,所有的指针类型用完需要调用

    free(pointer);
    

    传char到C语言,并获取返回的char

    这个相对来说难一点也麻烦一点,不过ffi官方文档有这个例子,挺不错的,我直接拿来用,dart中对应c char的类型是Utf8,
    Utf8.toUtf8("")返回指针,即对应原生的char *,用这玩意注意释放,不要把它当匿名对象使用,把它保存在一个对象里面,不然不好控制内存的释放

    Utf8.fromUtf8(pointer)返回str,切记,这个坑就是它只能转换最后以\0结尾的字符

    补上自己的注释

    char *reverse(char *str, int length)
    {
        //动态申请length+1个长度的字符空间
        char *reversed_str = (char *)malloc((length + 1) * sizeof(char));
        //简单的倒置算法
        for (int i = 0; i < length; i++)
        {
            reversed_str[length - i - 1] = str[i];
        }
        //这行很关键,切记,让它最后一个为\0
        reversed_str[length] = '\0';
        return reversed_str;
    }
    

    dart部分声明

    typedef reverse_func = Pointer<Utf8> Function(Pointer<Utf8> str, Int32 length);
    typedef Reverse = Pointer<Utf8> Function(Pointer<Utf8> str, int length);
    

    调用

    final reversePointer = dylib.lookup<NativeFunction<reverse_func>>('reverse');
    final reverse = reversePointer.asFunction<Reverse>();
    final reversedMessage = Utf8.fromUtf8(reverse(Utf8.toUtf8('backwards'), 9));
    print('$reversedMessage');
    

    注意官方示例的所有指针全部没有释放,Utf8.toUtf8(’’)还有dylib.lookup()方法都会调用allocate函数,这个就会让原生给你动态分配空间,会执行malloc函数,我也看到一些大佬的帖子提到不释放是会内存泄漏的

    OK这篇结束,任何问题欢迎评论区留言,下一篇是传char **和原生遍历这个char **

    展开全文
  • 我是首先去看了官方的前两个example,都上手来跑了,然后对应理解每一部分,dart:ffi的例子是在pc端,所以一系列的编译也是在pc端,可我是安卓端的app,于是就遇见了第一个坑,交叉编译.c,.h到so库 参考链接 ...

    我是首先去看了官方的前两个example,都上手来跑了,然后对应理解每一部分,dart:ffi的例子是在pc端,所以一系列的编译也是在pc端,可我是安卓端的app,于是就遇见了第一个坑,交叉编译.c,.h到so库

    参考链接

    https://www.kikt.top/posts/flutter/ffi/2-first-party/

    Dart:ffi官方文档

    SO库的编译

    大致理清一下思路,.h全是.c文件的函数声明,还有结构体,宏的定义等等,单独的.c语言编译后是可执行程序,打开就直接运行main函数,而so库(Android,Linux),dylib(Macos),dll(windows),是由.c,.h共同编译的,这时候的.c文件一般没有main函数,.h复制提供函数声明但实际不参加编译过程,
    我大致查了so库的编译,都说配置Android.mk比较麻烦我也就没有去了解了,整个编译的坑全靠上面第一个帖子趟过,我这自己走一遍

    首先用tree命令查看目录结构,

    $ tree -L 3 c-project
    tree -L 3 c-project
    c-project
    ├── android
    │   ├── CMakeLists.txt
    │   ├── build_android.sh
    │   └── cmd
    │       └── android.sh
    ├── ios
    │   ├── CMakeLists.txt
    │   ├── build_ios.sh
    │   ├── cmd
    │   │   └── ios_abi_build.sh
    │   └── ios.toolchain.cmake
    └── src
        ├── some.c
        └── some.h
    

    然后比着建立一个一样的目录结构(忽略图中无关的东西)
    QQ20200223-123712.png
    可以观察到有两个sh脚本,主脚本build_android.sh,然后主脚本主要遍历出需要编译的架构,然后设置环境变量,调用副脚本android.sh
    src里面就是.c跟.h,我自己写的时候并没有写.h,也能出so库,所以我就没单独写了,可以观察到我脚本取消了.sh的扩展名,这样就能直接通过shebang直接执行了,不会唤起我的xcode,贴一下改过的两个脚本

    build_android.sh

    #!/bin/bash
    cd /Users/nightmare/Desktop/C/nterm/android
    rm -rf ./build
    a="armeabi-v7a arm64-v8a x86 x86_64"
    for abi in $a;
    do
    export ABI=$abi
    sh cmd/android.sh
    done
    cp -rf ../../android/libs/* /Users/nightmare/Desktop/Flutter_Project/nightmare_tool/android/app/libs/
    

    这串还是比较好懂得
    cd命令切换到了这个C项目的android目录下,export即是更新一个环境变量,以让副脚本使用,我最后再用了cp -rf复制到了我的安卓项目的libs文件夹下,对应自己的路径改了就能用

    android.sh

    #!/bin/bash
    export NDK_HOME=/Users/nightmare/Library/Android/sdk/ndk/21.0.6113669 # or set to your ndk home
    
    export MAKE_PATH=build/make-cache
    
    export TARGET_ABI=$ABI
    
    create_makefile() {
        cmake \
            -DANDROID_ABI=$TARGET_ABI \
            -DANDROID_PLATFORM=android-16 \
            -DCMAKE_BUILD_TYPE=release \
            -DANDROID_NDK=$NDK_HOME \
            -DCMAKE_TOOLCHAIN_FILE=$NDK_HOME/build/cmake/android.toolchain.cmake \
            -DANDROID_TOOLCHAIN=clang -B $MAKE_PATH -S .
    }
    
    create_makefile
    
    cd $MAKE_PATH
    
    make clean
    make
    

    与上面链接的帖子中不一样的只有第一行,他的我不怎么能看懂,不过就是设置NDK所在的路径,NDK需要用Android Studio的SDK Manager下载,在FIle->Project Structure->Sdk Location可以看到(这也是坑到我的一个地方)
    CmakeLists.txt

    cmake_minimum_required (VERSION 2.6) # cmake version
    
    project(term) # project name
    
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../android/libs/$ENV{ABI}) # set output path
    
    aux_source_directory(${PROJECT_SOURCE_DIR}/../src SRC_FILES) # scan source code files
    
    add_library(term SHARED ${SRC_FILES}) # add source code files to library, and set build type to dynamic library.
    

    这些我对应改了自己的项目名称

    运行如下

    aaa.gif
    一般c语言没逻辑问题都能编译成功的,最后生成的文件在你项目同级文件夹下的android目录

    OK这篇就先结束了,都写在一起难理解

    展开全文
  • 今天 Dart 2.12 发布了,该版本具有稳定的空安全声明和Dart FFI版本。 空安全是最新的关键生产力功能,旨在帮助开发者避免空错误,这是一项通常很难被发现的错误。 FFI是一种互操作性机制,可以让开发者调用 C 语言...
  • ffi-源码

    2021-03-16 10:41:04
    使用Dart FFI进行实验 一系列简单的示例演示了如何从Dart调用C库。 请注意,Dart FFI API正在积极开发中,可能会在发布前进行更改。 该代码旨在与Dart版本2.5.0-dev.1.0一起使用。 指示 (当前)已将C make文件编写...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 286
精华内容 114
关键字:

dartffi