FFI: asoslar, Foreign Function Interface va C bilan integratsiya

Muallif: IT Sectr Nashr etilgan: 2026-06-05 O'qish vaqti: 10 daq

FFI (Foreign Function Interface) — dart:ffi paketi tomonidan taqdim etilgan Dart tili mexanizmi bo'lib, u Kotlin, Swift yoki Java kabi oraliq qatlamlarsiz to'g'ridan-to'g'ri mahalliy C kutubxonalaridan funksiyalarni chaqirish imkonini beradi. Dasturchi dinamik kutubxonani yuklaydi (.so Androidda, .dylib iOSda, .dll Windowsda), C funksiyalarining imzolarini e'lon qiladi va ularni oddiy Dart funksiyalari kabi chaqiradi. Dart API Reference (2025) ma'lumotlariga ko'ra, FFI tillararo chaqiruvlardagi yuklamani 0.1 μs gacha kamaytiradi, bu Method Channel orqali chaqirishdan o'nlab marta tezroq.

Asosiy ma'lumotlar

  • FFI (Foreign Function Interface) — C funksiyalarini Dartdan to'g'ridan-to'g'ri chaqirish mexanizmi
  • dart:ffi kutubxonalarni yuklash va imzolarni e'lon qilish uchun API taqdim etadi
  • Ish unumdorligi FFI orqali chaqiruvlar Method Channelga qaraganda 50–100 marta yuqori
  • Tiplashtirish FFI ibtidoiy turlarni, strukturalarni va C ko'rsatkichlarini qo'llab-quvvatlaydi
  • Flutter FFI dan mahalliy kutubxonalar bilan integratsiya uchun foydalanadi: OpenCV, SQLite, FFmpeg

FFI nima?

FFI (Foreign Function Interface) — bu dasturlash tiliga boshqa tillarda yozilgan funksiyalarni chaqirish imkonini beruvchi mexanizm. Dart va Flutter kontekstida FFI, Java (Android) yoki Swift/Objective-C (iOS) da platforma kodini yozmasdan to'g'ridan-to'g'ri Dart kodidan C/C++ kutubxonalaridan funksiyalarni chaqirish qobiliyatini anglatadi.

dart:ffi paketi Dart 2.12 (2021) da paydo bo'ldi va shundan beri Flutterni mahalliy kod bilan integratsiyalash uchun asosiy vositaga aylandi. dart:ffi paydo bo'lgunga qadar Dartdan C funksiyasini chaqirishning yagona usuli Method Channel edi — bu Dart va mahalliy tomon o'rtasida JSON seriyalash orqali xabarlarni uzatuvchi asinxron mexanizm. FFI boshqacha ishlaydi: Dart kodi to'g'ridan-to'g'ri C kutubxonasining xotirasiga murojaat qiladi, funksiyalarni mahalliy ABI (Application Binary Interface) orqali seriyalash va kontekst almashmasdan chaqiradi.

FFI, ayniqsa, ish unumdorligi muhim bo'lgan stsenariylarda talab qilinadi: tasvirni qayta ishlash (OpenCV), audio (FFmpeg), kriptografiya (OpenSSL), mashina o'qitish (TensorFlow Lite) va ma'lumotlar bazalari (SQLite). Bularning barchasida Method Channel qabul qilib bo'lmaydigan kechikishlarni keltirib chiqaradi, FFI esa mahalliy C/C++ kodi bilan solishtirish mumkin bo'lgan ish unumdorligini ta'minlaydi. dart:ffi kutubxonasi, shuningdek, xotira bilan ishlashni qo'llab-quvvatlaydi: ajratish, bo'shatish va ko'rsatkichlarni boshqarish.

FFI vs Method Channel: tub farq

Method Channel asinxron ishlaydi: Dart mahalliy kodga xabar yuboradi, mahalliy kod uni qayta ishlaydi va natijani qaytaradi. Har bir chaqiruv argumentlarni Mapga seriyalash, navbat orqali uzatish va deseriyalashni talab qiladi. Bu har bir chaqiruv uchun 0.5–5 ms vaqt oladi. FFI sinxron va seriyalashsiz ishlaydi — C funksiyasining chaqiruvi 0.01–0.1 μs davom etadi. 50–500 marta farq, bu yuqori chastotali operatsiyalar uchun juda muhim.

dart:ffi qanday ishlaydi?

dart:ffi bilan ishlash uch bosqichdan iborat: kutubxonani yuklash, imzolarni e'lon qilish va funksiyalarni chaqirish. Har bir bosqich Dartning qat'iy tiplashtirishidan foydalanadi, bu esa bajarilish vaqtidagi xatolarni kamaytiradi.

Birinchi bosqichda dinamik kutubxona DynamicLibrary sinfi orqali yuklanadi. Kutubxona nomi bo'yicha (libxyz.so, libxyz.dylib, xyz.dll) yoki to'liq yo'l bilan yuklanishi mumkin. Dart avtomatik ravishda kutubxonani tizimning standart yo'llarida qidiradi. DynamicLibrary Dart funksiyasini C funksiyasi bilan simvol nomi bo'yicha bog'laydigan lookupFunction metodini taqdim etadi.

Ikkinchi bosqichda C imzosiga mos keladigan turdagi annotatsiyalar bilan Dart funksiyasi e'lon qilinadi. Buning uchun dart:ffi dan maxsus turlar ishlatiladi: Int32, Float, Double, Pointer, NativeFunction, Handle va boshqalar. lookupFunction annotatsiyasi ikkita generik parametrni qabul qiladi: Dart funksiyasining turi (Dartda qanday ko'rinishi) va mahalliy C funksiyasining turi (Cda qanday e'lon qilingani).

Uchinchi bosqichda yaratilgan Dart funksiyasi oddiy funksiya kabi chaqiriladi. Argumentlar to'g'ridan-to'g'ri uzatiladi, natija darhol qaytariladi. Agar C funksiyasi ko'rsatkichlar orqali xotirani o'zgartirsa, Dart bu o'zgarishlarni Pointer sinfi orqali o'qiy oladi. Xotirani boshqarish C tomonida dasturchining mas'uliyatida qoladi — dart:ffi Cda malloc orqali ajratilgan xotirani boshqarmaydi.

Asosiy misol: Dartdan C funksiyasini chaqirish

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

// C funksiyasining deklaratsiyasi: int add(int a, int b)
typedef AddNative = Int32 Function(Int32, Int32)
typedef AddDart = int Function(int, int)

void main() {
    final lib = DynamicLibrary.open('libcalculator.so')
    final AddDart add = lib
        .lookupFunction<AddNative, AddDart>('add')

    print(add(5, 3)) // 8
}

Ushbu misolda add — ikkita int qabul qiladigan va int qaytaradigan C funksiyasidir. typedef AddNative dart:ffi turlari bilan C imzosini tasvirlaydi, AddDart esa bu funksiyaning Dartda qanday ko'rinishini belgilaydi. lookupFunction ularni bog'laydi va oddiy kabi chaqiriladigan Dart funksiyasini qaytaradi.

FFIda ma'lumot turlari

dart:ffi C turlariga mos keladigan turlar to'plamini taqdim etadi. Har bir tur qat'iy o'lchamga va Dart bilan C o'rtasida konvertatsiya qoidalariga ega. Turlarning mosligini tushunish FFIning to'g'ri ishlashi uchun juda muhim — tur o'lchamidagi yoki belgisidagi xato ilovaning ishdan chiqishiga olib kelishi mumkin.

C turidart:ffi turiDart turiO'lcham (bayt)
intInt32int4
longInt64int8
floatFloatdouble4
doubleDoubledouble8
char*Pointer<Int8>Pointer8 (ko'rsatkich)
void*Pointer<Void>Pointer8 (ko'rsatkich)
structPointer<T> (Struct)Pointermaydonlarga bog'liq

C satrlari (char*) bilan ishlash uchun dart:ffi Pointer<Int8> dan foydalanadi. Dart Stringdan C char* ga va aksincha konvertatsiya toNativeUtf8 (ffi paketidan) va fromUtf8 metodlari orqali amalga oshiriladi. Xotira oqishining oldini olish uchun C satrlarini ishlatishdan keyin calloc.free orqali bo'shatish muhimdir.

Strukturalar (Struct)

dart:ffi C strukturalarini Structdan meros oladigan Dart sinflari sifatida e'lon qilishni qo'llab-quvvatlaydi. Struktura maydonlari @Int32(), @Float(), @Array() va boshqa annotatsiyalar bilan e'lon qilinadi. Maydonlarning o'lchami va siljishi platformaning ABI siga muvofiq avtomatik hisoblanadi. Pointer<Point> C funksiyasidan strukturaga ko'rsatkichni qaytarish orqali olinishi yoki Dartda calloc orqali ajratilishi mumkin.

dart
// C strukturasi: typedef struct { int x; int y; } Point;
final class Point extends Struct {
    @Int32()
    external int x

    @Int32()
    external int y
}

// Point* qaytaruvchi C funksiyasini chaqirish
typedef CreatePointNative = Pointer<Point> Function(Int32, Int32)
typedef CreatePointDart = Pointer<Point> Function(int, int)

final Pointer<Point> p = createPoint(10, 20)
print('x: ${p.ref.x}, y: ${p.ref.y}')
calloc.free(p) // xotirani bo'shatish

Point sinfi Structdan meros oladi va x va y maydonlarini @Int32() annotatsiyalari bilan e'lon qiladi. Yaratilgan C kodi xotirada aynan bir xil maydon joylashuviga ega bo'ladi. Pointer.ref struktura maydonlariga getter va setterlar orqali kirishni ta'minlaydi.

FFI amaliy misollari

Keling, murakkabroq misolni ko'rib chiqaylik — SHA256 xeshini hisoblash uchun C kutubxonasi bilan integratsiya. Bu FFI Method Channelga nisbatan sezilarli ish unumdorligi ustunligini beradigan odatiy vazifadir.

OpenSSL bilan FFI orqali integratsiya

OpenSSL kutubxonasi satrning xeshini hisoblaydigan SHA256 funksiyasini taqdim etadi. dart:ffi orqali biz uni Java yoki Swift wrapperlarini yozmasdan to'g'ridan-to'g'ri chaqira olamiz. Bu FFI Flutterda mavjud C kutubxonalarini qayta ishlatishga qanday imkon berishining namunasidir.

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

// Imzo: unsigned char* SHA256(
//   const unsigned char *d, size_t n, unsigned char *md)
typedef Sha256Native = Pointer<Uint8> Function(
    Pointer<Uint8>, Size, Pointer<Uint8>)
typedef Sha256Dart = Pointer<Uint8> Function(
    Pointer<Uint8>, int, Pointer<Uint8>)

String sha256(String input) {
    final lib = DynamicLibrary.open('libcrypto.so')
    final Sha256Dart sha256Fn = lib
        .lookupFunction<Sha256Native, Sha256Dart>('SHA256')

    final inputPtr = input.toNativeUtf8()
    final outputPtr = calloc(Uint8)(32) // SHA256 = 32 bayt

    sha256Fn(inputPtr, input.length, outputPtr)

    final digest = outputPtr.asTypedList(32)
    final hex = digest.map((b) => b.toRadixString(16)
        .padLeft(2, '0')).join()

    calloc.free(inputPtr)
    calloc.free(outputPtr)

    return hex
}

Ushbu misolda sha256 funksiyasi libcrypto.so kutubxonasini yuklaydi, SHA256 simvolini topadi va uni kirish-chiqish ma'lumotlar ko'rsatkichlari bilan chaqiradi. toNativeUtf8 Dart Stringni C satriga aylantiradi (xotira ajratadi), asTypedList esa natija bayt massivini o'qish imkonini beradi. Xotira ishlatishdan keyin bo'shatiladi — bu oqishning oldini olish uchun majburiy qadamdir.

Xotirani ajratish va bo'shatish

ffi paketi C bilan mos keladigan xotirani ajratish uchun calloc funksiyasini taqdim etadi. Ajratilgan xotira calloc.free orqali bo'shatilishi kerak, aks holda oqish yuz beradi. Xotirani avtomatik boshqarish uchun ffi paketidan Arena sinfidan foydalanish mumkin, u arena.release() chaqirilganda unda ajratilgan barcha xotirani bo'shatadi. Bu, ayniqsa, ko'p sonli vaqtinchalik ajratishlar mavjud bo'lganda qulaydir.

Cheklovlar va eng yaxshi amaliyotlar

FFIning kuchiga qaramay, Flutter ilovasining arxitekturasini loyihalashda e'tiborga olinishi kerak bo'lgan cheklovlar mavjud. Asosiy cheklovlar tur xavfsizligi, xotira boshqaruvi va platforma mosligi bilan bog'liq.

Xavfsizlik

FFI bajarilish vaqtida turlarni tekshirmaydi. Agar C funksiyasi ko'rsatkich kutayotgan bo'lsa va uning o'rniga raqam uzatilsa, ilova segmentation fault bilan ishdan chiqadi. C sarlavhalari (.h fayllari) asosida tur xavfsiz Dart wrapperlarini yaratadigan FFIgen vositasidan foydalanish tavsiya etiladi. FFIgen C funksiyalarining deklaratsiyalarini tahlil qiladi va to'g'ri turlar bilan Dart kodi yaratadi, bu esa kod yozish bosqichidagi xatolarni bartaraf qiladi.

Platforma mosligi

Dinamik kutubxonalarning nomlari va yo'llari turli platformalarda farqlanadi: libxyz.so Android/Linuxda, libxyz.dylib iOS/macOSda, xyz.dll Windowsda. Ko'p platformali kutubxonalar uchun dart:io (Platform.isAndroid, Platform.isIOS) orqali shartli kompilyatsiya yoki package:ffi kabi abstraksiyalar ishlatiladi. Joriy platforma uchun to'g'ri kutubxonani qaytaradigan fabrika metodi yaratish tavsiya etiladi.

Xotirani boshqarish

FFI C tomonidagi xotirani boshqarmaydi. Agar C funksiyasi malloc orqali xotira ajratsa, u free orqali bo'shatilishi kerak, aks holda oqish yuz beradi. Dartda C xotirasi uchun axlat yig'uvchi mavjud emas. Tavsiya: xotirani har doim ajratilgan usulning o'zida bo'shating yoki guruhli bo'shatish uchuna Arena dan foydalaning.

Ish unumdorligi va oqimlar

FFI chaqiruvlari Dart kodi bilan bir xil oqimda bajariladi. Uzoq sinxron operatsiyalar (10 ms dan ortiq) UI oqimini bloklaydi va kadrlarning tushib qolishiga sabab bo'ladi. Uzoq operatsiyalar uchun C funksiyasini izolyatda (Isolate) chaqirish yoki C funksiyasi fon oqimida ishni boshlab, Dartni Port yoki callback orqali xabardor qilishiga ishonch hosil qilish kerak.

dart
// Uzoq operatsiyalar uchun izolyatda FFI
import 'dart:isolate'

Future<String> computeHash(String input) async {
    final port = ReceivePort()
    await Isolate.spawn((SendPort sendPort) {
        final result = sha256(input) // FFI chaqiruvi
        sendPort.send(result)
    }, port.sendPort)

    return await port.first as String
}

FFI chaqiruvini izolyatga o'tkazish UI oqimining bloklanmasligini kafolatlaydi. Biroq, izolyatlar o'rtasida katta hajmdagi ma'lumotlarni uzatish xotirani nusxalashni talab qiladi. Katta buferlar (>10 MB) uchun ShareMemory yoki xotira bilan bog'langan fayllardan foydalanish afzalroqdir.

Tez-tez beriladigan savollar

FFI Method Channeldan qanday farq qiladi?

FFI C funksiyalarini to'g'ridan-to'g'ri, sinxron va seriyalashsiz chaqiradi — kechikish 0.01–0.1 μs. Method Channel asinxron ishlaydi, JSON seriyalash orqali kechikish 0.5–5 ms. FFI yuqori unumdorlikdagi operatsiyalar uchun mos, Method Channel esa oddiy platforma API chaqiruvlari uchun.

C++ funksiyalarini FFI orqali chaqirish mumkinmi?

To'g'ridan-to'g'ri — yo'q, dart:ffi faqat C funksiyalarini qo'llab-quvvatlaydi. C++ chaqirish uchun extern "C" bilan C wrapper yaratish kerak (C simvollari sifatida eksport qilinadigan kirish nuqtalari). C++ sinflari metod chaqiruvlarini C funksiyalariga aylantiruvchi qo'shimcha qatlamni talab qiladi.

C funksiyalaridagi xatolarni qanday boshqarish mumkin?

FFI istisnolarni qo'llab-quvvatlamaydi — agar C funksiyasi xato kodini qaytarsa, uni qo'lda tekshirish kerak. FFI chaqiruvlarini Dartda try-catch bilan o'rash va C funksiyalari qaytargan kodlarni tekshirish tavsiya etiladi. Kritik xatolarni (segfault) ushlab bo'lmaydi.

Qanday kutubxonalarni FFI orqali ishlatib bo'lmaydi?

FFI murakkab Java (JNI) yoki Objective-C (Message Dispatch) initsializatsiyasini talab qiladigan kutubxonalar bilan ishlamaydi. Masalan, UIKit va Android Views FFI orqali mavjud emas. Cheklov FFIning C ABI darajasida ishlashi bilan bog'liq, bu API lar esa maxsus ishga tushirish muhitlarini talab qiladi.

C kutubxonalarini har bir platforma uchun alohida kompilyatsiya qilish kerakmi?

Ha, C kutubxonalari har bir maqsadli platforma uchun alohida kompilyatsiya qilinadi. Android uchun .so turli ABI lar uchun (armeabi-v7a, arm64-v8a, x86_64) yig'iladi. iOS uchun — universal .dylib (arm64). Windows uchun — .dll. Flutter avtomatik ravishda kutubxonaning to'g'ri versiyasini kompilyatsiya paytida paketlaydi.

Xulosa

  • FFI (Foreign Function Interface) — dart:ffi orqali Dartdan C funksiyalarini to'g'ridan-to'g'ri chaqirish mexanizmi
  • Ish unumdorligi FFI chaqiruvlari Method Channeldan 50–500 marta yuqori
  • Arxitektura kutubxonani yuklash, imzolarni e'lon qilish va funksiyalarni chaqirishni o'z ichiga oladi
  • Ma'lumot turlari dart:ffi Int32, Float, Double, Pointer, Struct va boshqa C turlarini qo'llab-quvvatlaydi
  • Xotira C tomonida calloc/free yoki Arena orqali qo'lda boshqariladi
  • Cheklovlar FFI: runtime da tur tekshiruvi yo'q, to'g'ridan-to'g'ri C++ qo'llab-quvvatlanmaydi, UI oqimini bloklaydi
  • FFI dan Flutterda mahalliy kutubxonalar bilan yuqori unumdorlikdagi integratsiya uchun foydalaning

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing