Попытка высокопроизводительного кросс-энда Flutter + Rust

Flutter
Попытка высокопроизводительного кросс-энда Flutter + Rust

С небольшой настройкой один и тот же код охватывает Android и IOS, что более эффективно, чем решение React Native. Кроме того, благодаря кроссплатформенности Rust, некоторый код Rust можно повторно использовать в различных случаях.

Этот пост предназначен для документирования попытки автора объединить Rust и Flutter и является лишь предварительной попыткой. Это не будет включать такие вещи, как:

  • Как создать среду разработки Flutter и как использовать язык Dart
  • Как создать среду разработки на Rust и как выучить язык Rust

Environment

  • Flutter: инструменты Android, IOS настроены правильно
    -w672
  • Rust: Stable — это нормально
    -w513

Rust Part

Prepare cross-platform toolchains & deps

IOS

# Download targets for IOS ( 64 bit targets (real device & simulator) )
rustup target add aarch64-apple-ios x86_64-apple-ios 

# Install cargo-lipo to generate the iOS universal library
cargo install cargo-lipo

Android

здесьЕсть несколько хорошо зарекомендовавших себя вспомогательных скриптов для более быстрой настройки инструментов кросс-компиляции.

  1. Получить Android NDK

    sdkmanager --verbose ndk-bundle
    

    Если Android NDK готов, установите переменную среды$ANDROID_NDK_HOME

    # example:
    export ANDROID_NDK_HOME=/Users/yinsiwei/Downloads/android-ndk-r20b
    
  2. Create the standalone NDK

    # $(pwd) == ~/Downloads
    git clone https://github.com/kennytm/rust-ios-android.git
    cd rust-ios-android
    ./create-ndk-standalone.sh
    
  3. Настройте инструменты кросс-компиляции Android в конфигурации Cargo по умолчанию VS

    cat cargo-config.toml >> ~/.cargo/config
    

    После выполнения вышеуказанной команды связанные кроссплатформенные цели Android (цели,aarch64-linux-android, armv7-linux-androideabi, i686-linux-android) информация об инструменте, указывающая на только что созданныйstandalone NDK.

    [target.aarch64-linux-android]
    ar = ...
    linker = ..
    
    [target.armv7-linux-androideabi]
    ...
    
    [target.i686-linux-android]
    ..
    
  4. Загрузите зависимости Rust для поддержки кросс-компиляции Android

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android ```

Start a simple rust library

  1. Создайте проект Rust

cargo init my-app-base --lib ```

  1. редактироватьCargo.tomlИсправлятьcrate-type

[библиотека] имя = "my_app_base" crate-type = ["staticlib", "cdylib"] ```` Бинарная библиотека, созданная Rust, статически связана с окончательной программой в IOS, и ее необходимо собрать.staticlibподдержка; на Android он заносится в пространство выполнения программы во время выполнения через динамическую линковку, и необходимо строитьcdylibслужба поддержки.

  1. Напишите несколько функций, которые соответствуют C ABIsrc/lib.rs

    use std::os::raw::c_char;
    use std::ffi::CString;
    
    #[no_mangle]
    pub unsafe extern fn hello() -> *const c_char {
        let s = CString::new("world").unwrap();
        s.into_raw()
    }
    

    В приведенном выше коде каждый раз, когда внешний вызовhelloфункция, строка (CString) и передать право собственности (право на освобождение пространства кучи, занятого строкой)абонент.

Build libraries

# IOS
cargo lipo --release

# Android
cargo build --target aarch64-linux-android --release
cargo build --target armv7-linux-androideabi --release
cargo build --target i686-linux-android --release

затем вtargetПо каталогу вы получите следующие полезные материалы.

target
    ├── aarch64-linux-android
    │   └── release
    │       ├── libmy_app_base.a
    │       └── libmy_app_base.so
    ├── armv7-linux-androideabi
    │   └── release
    │       ├── libmy_app_base.a
    │       └── libmy_app_base.so
    ├── i686-linux-android
    │   └── release
    │       ├── libmy_app_base.a
    │       └── libmy_app_base.so
    ├── universal
    │   └── release
    │       └── libmy_app_base.a

Слишком далеко,RustЧасть его заканчивается абзацами.

Flutter Part

Copy build artifacts to flutter project

from: target/universal/release/libmy_app_base.a 
to: ios/

from: target/aarch64-linux-android/release/libmy_app_base.so 
to: android/app/src/main/jniLibs/arm64-v8a/

from: target/armv7-linux-androideabi/release/libmy_app_base.so 
to: android/app/src/main/jniLibs/armeabi-v7a/

from: target/i686-linux-android/release/libmy_app_base.so 
to: android/app/src/main/jniLibs/x86/

Call FFI function in Dart

  1. добавить зависимости

    pubspec.yaml -> dev_dependencies: += ffi: ^0.1.3

  2. добавить код

    (Измените непосредственно в сгенерированном проекте, не принимая во внимание проблему дизайна кода, просто сначала запустите проект)

    import 'dart:ffi';
    import 'package:ffi/ffi.dart';
    
    // ...
    final dylib = Platform.isAndroid ? DynamicLibrary.open('libmy_app_base.so') :DynamicLibrary.process();
    var hello = dylib.lookupFunction<Pointer<Utf8> Function(),Pointer<Utf8> Function()>('hello');
    
    // ...
    hello(); 
    // -> world
    

Build Android Project

flutter run # 如果连接着 Android 设备就直接运行了起来

Build IOS Project

(гораздо сложнее)

  1. следитьFlutter официальная документация, настроитьXCodeпроект.
  2. существуетBuild PhasesсерединаLink Binary With LibrariesДобавить кlibmy_app_base.aдокумент (По стрелке на карте...)
    -w1140
  3. существуетBuild SettingsсерединаOther Linker Flagsдобавлено вforce_loadпараметр.
    -w855

Это связано с тем, что в Dart связанные функции библиотеки вызываются динамически, но при статическом анализе при компиляции это бесполезные функции, которые ни разу не вызывались и обрезаются. пройтиforce_loadспособ решить эту проблему.

Result

2020-02-15 12.39.59-w300

ezgif-6-785f61b1b53b

Troubleshooting

XCode & IOS

Error getting attached iOS device: ideviceinfo could not find device

sudo xattr -d com.apple.quarantine ~/flutter/bin/cache/artifacts/libimobiledevice/ideviceinfo

Замените конечный путь на свой

dyld: Library not loaded

dyld: Library not loaded: /b/s/w/ir/k/homebrew/Cellar/libimobiledevice-flutter/HEAD-398c120_3/lib/libimobiledevice.6.dylib
  Referenced from: /Users/hey/flutter/bin/cache/artifacts/libimobiledevice/idevice_id
  Reason: image not found

удалить и заново скачать

rm -rf /Users/hey/flutter/bin/cache && flutter doctor -v

Реальная машина не может запустить программу Flutter

видетьGitHub.com/flutter/Appendix…Не обновляйтесь до системы IOS 13.3.1

What's next

  • Как эффективно реализовать взаимодействие частей Rust и Dart

    Мы знаем, что Flutter похож на большинство графических библиотек, относится к однопоточной модели в сочетании с системой событий, поэтому код, использующий FFI для вызова Rust-части в основном потоке, не может блокировать поток. Язык Dart предоставляет функции синтаксиса async/await для обработки блокирующих задач, таких как сетевые запросы во Flutter. Кроме того, в последних версиях Rust поддерживает синтаксис async/await, но как элегантно объединить эти две части — проблема.

  • Поддержка рабочего стола MacOS Windows Linux

    Flutter уже имеет экспериментальную поддержку для настольных компьютеров, и вы можете изучить, как объединить их для совместного использования кода на 6 терминалах.

References


Адрес блога:Ограничение идентификатора 0.Dev/2020/02/15/…