flutter中引入c++ sdk

Home / C++ MrLee 6月前 837

使用flutter开发移动端项目时,接入跨平台的c++ sdk是很常见的。
一般我们会在项目里新建一个插件来封装引入的sdk,里面定义对应的数据结构、调用逻辑,对外暴露出dart接口供项目使用。

1、创建原生插件

flutter create --template=plugin hello

2、引入sdk

虽然sdk代码是c++写的,但是不同平台的硬件资源、底层的api基本上是不同的,所以一般是android一个so文件,ios一个framework,需要分别操作。

2.1、android平台引入

以opencv为例,从opencv官网下载解压后,放到hello/android/src/main/jniLibs/(需要用的arm架构 如:armeabi-v7a)/libopencv_java4.so。

添加CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(hello)
#头文件路径
include_directories(../include)
#引入多个so文件
add_library(lib_opencv SHARED IMPORTED)
add_library(lib_xxx SHARED IMPORTED)

set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java4.so)
set_target_properties(lib_xxx PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libXxx.so)

# find_library(log-lib log)
#里面封装调用sdk的cpp文件
add_library(hell SHARED ../ios/Classes/hello.cpp)
target_link_libraries(
    hello 
    lib_opencv 
    lib_xxx

    GLESv2
    EGL
    log
)

修改build.gradle

android {  ...
    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
        main.jniLibs.srcDirs = ["libs"]    }

    defaultConfig {
        minSdkVersion 21

        externalNativeBuild {
            cmake {
                cppFlags '-frtti -fexceptions -std=c++11'
                arguments "-DANDROID_STL=c++_shared"            }        }

        ndk {
            abiFilters 'armeabi-v7a'        }    }

    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"        }    }  ...}
2.2、ios平台引入

ios平台其实是以模块私有化的形式来处理,把sdk放到hello/ios/(opencv2.framework 、xxx.framework)。

修改hello.podspec

  # telling CocoaPods not to remove framework
  s.preserve_paths = 'opencv2.framework', 'xxx.framework'  # telling linker to include opencv2 framework
  s.xcconfig = { 
    'OTHER_LDFLAGS' => '-framework opencv2 -framework xxx',  }  # including OpenCV framework
  s.vendored_frameworks = 'opencv2.framework', 'xxx.framework'  # s.vendored_libraries = 'path/name.a'

  s.frameworks = 'AVFoundation'
  s.library = 'c++'

新建hello.cpp

#include <opencv2/opencv.hpp>#include <chrono>#include <iostream>#if defined(__ANDROID__)#include <android/log.h>#include "xxx.h"#endif#if defined(TARGET_OS_IPHONE)#include <xxx/xxx.h>#endif#if defined(__GNUC__)    // Attributes to prevent 'unused' function from being removed and to make it visible    #define FUNCTION_ATTRIBUTE __attribute__((visibility("default"))) __attribute__((used))#elif defined(_MSC_VER)    // Marking a function for export    #define FUNCTION_ATTRIBUTE __declspec(dllexport)#endifusing namespace std;using namespace cv;long long int get_now() {    return chrono::duration_cast<std::chrono::milliseconds>(
            chrono::system_clock::now().time_since_epoch()    ).count();}void platform_log(const char *fmt, ...) {
    va_list args;    va_start(args, fmt);#ifdef __ANDROID__    __android_log_vprint(ANDROID_LOG_VERBOSE, "ndk", fmt, args);#else    vprintf(fmt, args);#endif    va_end(args);}//对应sdk里的数据结构typedef struct MyPoint {    float x;    float y;}MyPoint;//对应sdk里的数据结构typedef struct MySize {    float width;    float height;}MySize;extern "C" {
    FUNCTION_ATTRIBUTE    const char* version() {
        std::cout << "version func" << std::endl;        return CV_VERSION;    }

    FUNCTION_ATTRIBUTE    void test(int *a) {
        std::cout << a << std::endl;    }

    FUNCTION_ATTRIBUTE    void process_image(char* inputImagePath, char* outputImagePath) {        long long start = get_now();
        Mat input = imread(inputImagePath, IMREAD_GRAYSCALE);
        Mat threshed, withContours;
        
        vector<vector<Point> > contours;
        vector<Vec4i> hierarchy;        
        adaptiveThreshold(input, threshed, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 77, 6);        findContours(threshed, contours, hierarchy, RETR_TREE, CHAIN_APPROX_TC89_L1);        
        cvtColor(threshed, withContours, COLOR_GRAY2BGR);        drawContours(withContours, contours, -1, Scalar(0, 255, 0), 4);        
        imwrite(outputImagePath, withContours);        
        int evalInMillis = static_cast<int>(get_now() - start);
        std::cout << "Processing done in" << evalInMillis << std::endl;        
    }


    FUNCTION_ATTRIBUTE    void bytes2Mat(uint8_t* imgData, int h, int w, int channels, cv::Mat *mat){       
       if (channels == 1)       {           *mat = cv::Mat(h, w, CV_8UC1, imgData);       }       else if (channels == 3)       {           *mat = cv::Mat(h, w, CV_8UC3, imgData);       }       else if (channels == 4)       {           *mat = cv::Mat(h, w, CV_8UC4, imgData);       }    }........}

修改hello/lib/hello.dart

import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'dart:ffi';import 'dart:io';import 'package:ffi/ffi.dart';//-----对应的结构体class MyPoint_dart extends Struct {  @Float()  external double x;  @Float()  external double y;}class MySize_dart extends Struct {  @Float()  external double width;  @Float()  external double height;} //如果用指针,对应的c内存就要自己管理//-----------final DynamicLibrary helloLib = Platform.isAndroid    ? DynamicLibrary.open("libHello.so")    : DynamicLibrary.process();//-----//c 方法签名typedef _CVersionFunc = Pointer<Utf8> Function();typedef _CProcessImageFunc = Void Function(Pointer<Utf8>, Pointer<Utf8>);typedef _CTestFunc = Void Function(Pointer<Int32>);//dart 方法签名typedef _VersionFunc = Pointer<Utf8> Function();typedef _ProcessImageFunc = void Function(Pointer<Utf8>, Pointer<Utf8>);typedef _TestFunc = void Function(Pointer<Int32>);//找对应的方法final _VersionFunc _version = helloLib    .lookup<NativeFunction<_CVersionFunc>>("version").asFunction();  final _ProcessImageFunc _processImage = helloLib    .lookup<NativeFunction<_CProcessImageFunc>>("process_image").asFunction();final _TestFunc test = helloLib    .lookup<NativeFunction<_CTestFunc>>("test").asFunction();//-------class ImageArguments {  final String inPath;  final String outPath;  ImageArguments(this.inPath, this.outPath);}void ProcessImage(ImageArguments args) {  _processImage(args.inPath.toNativeUtf8(), args.outPath.toNativeUtf8());}......

项目中引入对应的插件

#pubspec.yaml
dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  ...
  hello: 
    path: ./hello
  ...

到这里,基本的配置已经完成,接下来就是引入插件里的dart文件常规使用。

2.3、引入类似sdk的配置json等资源

在ios里最简单的就是直接放到项目里,编入到ipa文件里,然后根据mainbundle路径去加载。
针对android,踩的坑比较多,比较合理的办法是先把资源文件放到'项目'/android/app/src/main/assets/xxx.json,然后在项目的pubspec.yaml中引入这些资源。

#pubspec.yaml
...
assets:
    - android/app/src/main/assets/
  ...

在使用的时候还是先从’android/app/src/main/assets/***‘加载对应的资源数据,写到外部存储目录后再使用。
c++ sdk接入过程对我来说并不流畅,特此记录下,希望能帮助有需要的人。

本文链接:https://it72.com/12772.htm

推荐阅读
最新回复 (0)
返回