FFI — Dart에서 C C++ 라이브러리 직접 호출
FFI — Dart에서 C/C++ 라이브러리 직접 호출
FFI(Foreign Function Interface)를 사용하면 MethodChannel 없이 C/C++ 함수를 직접 호출할 수 있습니다. MethodChannel보다 오버헤드가 적어 성능이 중요한 경우에 유용합니다.
FFI vs MethodChannel
| 비교 | FFI | MethodChannel |
|---|---|---|
| 호출 대상 | C/C++ 함수 | 플랫폼 코드 (Kotlin/Swift) |
| 오버헤드 | 매우 적음 | 직렬화/역직렬화 필요 |
| 동기/비동기 | 동기 가능 | 비동기만 |
| 설정 복잡도 | 중간 | 낮음 |
| 플랫폼 API | 직접 불가 | 가능 |
기본 예제
C 코드 작성
// native/math_utils.c
#include <math.h>
// 두 수의 합
int add(int a, int b) {
return a + b;
}
// 제곱근
double sqrt_value(double x) {
return sqrt(x);
}
// 피보나치 (재귀)
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Dart에서 호출
import 'dart:ffi';
import 'dart:io' show Platform;
// C 함수 시그니처 (native 측)
typedef AddNative = Int32 Function(Int32 a, Int32 b);
typedef SqrtNative = Double Function(Double x);
typedef FibonacciNative = Int32 Function(Int32 n);
// Dart 함수 시그니처
typedef AddDart = int Function(int a, int b);
typedef SqrtDart = double Function(double x);
typedef FibonacciDart = int Function(int n);
class MathUtils {
late final DynamicLibrary _lib;
late final AddDart add;
late final SqrtDart sqrtValue;
late final FibonacciDart fibonacci;
MathUtils() {
// 플랫폼별 라이브러리 로드
_lib = _loadLibrary();
// 함수 바인딩
add = _lib
.lookupFunction<AddNative, AddDart>('add');
sqrtValue = _lib
.lookupFunction<SqrtNative, SqrtDart>('sqrt_value');
fibonacci = _lib
.lookupFunction<FibonacciNative, FibonacciDart>('fibonacci');
}
DynamicLibrary _loadLibrary() {
if (Platform.isAndroid) {
return DynamicLibrary.open('libmath_utils.so');
} else if (Platform.isIOS) {
return DynamicLibrary.process();
} else if (Platform.isMacOS) {
return DynamicLibrary.open('libmath_utils.dylib');
} else if (Platform.isWindows) {
return DynamicLibrary.open('math_utils.dll');
} else if (Platform.isLinux) {
return DynamicLibrary.open('libmath_utils.so');
}
throw UnsupportedError('지원하지 않는 플랫폼');
}
}
// 사용
void main() {
final math = MathUtils();
print(math.add(3, 5)); // 8
print(math.sqrtValue(16.0)); // 4.0
print(math.fibonacci(10)); // 55
}
Flutter 프로젝트에 통합
CMakeLists.txt (Android)
# android/app/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(math_utils)
add_library(math_utils SHARED
../../native/math_utils.c
)
build.gradle 수정
// android/app/build.gradle
android {
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
문자열 처리
C에서 문자열은 포인터로 전달됩니다.
// C 코드
#include <string.h>
#include <stdlib.h>
const char* greet(const char* name) {
static char buffer[256];
snprintf(buffer, sizeof(buffer), "안녕하세요, %s님!", name);
return buffer;
}
// C 함수 시그니처
typedef GreetNative = Pointer<Utf8> Function(Pointer<Utf8> name);
typedef GreetDart = Pointer<Utf8> Function(Pointer<Utf8> name);
// 사용
final greet = lib.lookupFunction<GreetNative, GreetDart>('greet');
// 문자열 변환
final namePtr = '심정훈'.toNativeUtf8();
final resultPtr = greet(namePtr);
final result = resultPtr.toDartString();
print(result); // 안녕하세요, 심정훈님!
// 메모리 해제
calloc.free(namePtr);
구조체 사용
// C 구조체
typedef struct {
double x;
double y;
} Point;
double distance(Point* a, Point* b) {
double dx = a->x - b->x;
double dy = a->y - b->y;
return sqrt(dx * dx + dy * dy);
}
// Dart에서 구조체 정의
final class Point extends Struct {
@Double()
external double x;
@Double()
external double y;
}
typedef DistanceNative = Double Function(Pointer<Point> a, Pointer<Point> b);
typedef DistanceDart = double Function(Pointer<Point> a, Pointer<Point> b);
ffigen — 바인딩 자동 생성
수동으로 바인딩을 작성하는 것은 번거롭습니다. ffigen이 C 헤더 파일에서 Dart 바인딩을 자동 생성합니다.
# pubspec.yaml
dev_dependencies:
ffigen: ^11.0.0
# ffigen.yaml
name: MathBindings
description: Math utils bindings
output: lib/generated/math_bindings.dart
headers:
entry-points:
- native/math_utils.h
dart run ffigen
비동기 FFI (Isolate 활용)
FFI 호출은 기본적으로 동기입니다. 무거운 계산은 Isolate에서 실행하세요.
Future<int> computeFibonacci(int n) async {
return await Isolate.run(() {
final math = MathUtils();
return math.fibonacci(n);
});
}
정리
- FFI는 C/C++ 함수를 직접 호출하여 MethodChannel보다 오버헤드가 적습니다
DynamicLibrary로 네이티브 라이브러리를 로드하고lookupFunction으로 함수를 바인딩합니다- 문자열은
Pointer<Utf8>로 변환하고, 사용 후 메모리를 해제해야 합니다 ffigen으로 C 헤더에서 Dart 바인딩을 자동 생성할 수 있습니다- 무거운 FFI 작업은 Isolate에서 실행하여 UI 블로킹을 방지하세요
댓글 로딩 중...