使用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接入过程对我来说并不流畅,特此记录下,希望能帮助有需要的人。
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2300
- 用户1336
- 访客10731099
每日一句
Love is the bridge between you and everything.
爱是你与一切之间的桥梁。
爱是你与一切之间的桥梁。
wordpress采集器——Wp-AutoPost精简无限制版
linux文件夹打包命令
ndk神奇问题之non-numeric second argument to `wordlist' function: '8.7z'
XEON E5无法使用系统评分解决方法
win10利用winsat disk测试磁盘读写速度
C++ 11新语法获取系统盘符
Android Studio使用Opencv2.4.9进行NDK开发
10年后,Android应用程序仍然比iOS应用程序差
Notepad++如何代码格式化——NppAStyle
iMessage for Android是我们需要的但不是我们想要的
二进制加壳原理与实现
仙剑奇侠传3d回合-PC端辅助
adb logcat 命令行用法
新会员