Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integrating Rust + Flutter + Kotlin for Mobile Applications

As next week will have importat launch for Rust 2018 and Flutter 1.0, I thought to build an app using Rust for the business logic and Flutter for the user interface, that can run at both Android and iOS, I built one and tested it at Android and it is working fine.

I just wonder how to measure the performance and compare it with native Android/iOS app.

The app flow is:

  1. Main is in Flutter, that is calling native function through platform_channel
  2. The native function is calling rust library through JNI (JNI wrapper is required to be call the rust library)

The structure is as below:

enter image description here

The code used is:

main.dart:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const platform = const MethodChannel('samples.flutter.io/battery');

  String _batteryLevel = 'Unknown battery level.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final String hello = await platform.invokeMethod('getText');
      final int result = await platform.invokeMethod('getBatteryLevel');

      batteryLevel = '$hello Battery level at $result %.';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            RaisedButton(
              child: Text('Get Battery Level'),
              onPressed: _getBatteryLevel,
            ),
            Text(_batteryLevel),
          ],
        ),
      ),
    );
  }
}

JNI wrapper - RustGreetings.kt

package com.mozilla.greetings

class RustGreetings {
    companion object {
        init {
            System.loadLibrary("greetings")
        }
    }

    private external fun greeting(pattern: String): String

    fun sayHello(to: String): String = greeting(to)
}

And the Main Android activity is:

package com.example.batterylevel

import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel

import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES

import lib.Library
import com.mozilla.greetings.RustGreetings

class MainActivity: FlutterActivity() {
  private val CHANNEL = "samples.flutter.io/battery"

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)

    MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
      if (call.method == "getText") {
        result.success(getText())
      } else if (call.method == "getBatteryLevel") {
       // result.success(getText())
        val batteryLevel = getBatteryLevel()

        if (batteryLevel != -1) {
          result.success(batteryLevel)
        } else {
          result.error("UNAVAILABLE", "Battery level not available.", null)
        }
      }
      else {

        result.notImplemented()
      }
    }
  }

  private fun getBatteryLevel(): Int {
    val batteryLevel: Int
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
      batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    } else {
      val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
      batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
    }
    return batteryLevel
  }

  private fun getText(): String {
    val x = Library().someLibraryMethod()
    val g = RustGreetings()
    val r = g.sayHello("My $x Rust")
    return r
  }
}

In the Android gradle.build I just added the below, as I'm interested to check also the impact of adding kotlin JVM library and getting it interacted with the Rust library within the mobile application:

dependencies {
    implementation(files("src/main/libs/lib.jar"))
}

My question is: How can check the performance and impact of each process when it is executed or called by another process

like image 238
Hasan A Yousef Avatar asked Nov 29 '18 06:11

Hasan A Yousef


People also ask

How to build a rust app for Android with flutter?

You can also read this post while taking a look at the associated GitHub repository. Let’s start by creating a folder called rust-for-android-ios-flutter and create four folders in it ( android, ios, flutter & rust ): Once we have it, just cd into the rust folder and create a new Rust library called rustylib:

How to integrate Flutter project with Android app?

Now our native Android and Flutter Project is successfully integrated. Flutter provides FlutterActivity inside the android app that simulates the flutter experience. We need to register flutter activity in AndroidManifest.xml. First, we add Activity. Main.

How to create a static method in flutter with rust?

Open the flutter/ios/Classes/RustyFlutterLibPlugin.swift file and add the following code: Open the flutter/lib/rusty_flutter_lib.dart file and add a new static method in the RustyFlutterLib class: Let’s open the flutter/example/lib/main.dart file with your IDE of choice and let’s consume our recently created Flutter package:

Is rust a good choice for mobile app development?

The biggest challenge is to build a proper bridge between Rust and client frameworks, if you can live with it — it could be a great solution for mobile. My previous investigation: Go + Gomobile for Android and IOS.


Video Answer


1 Answers

With the introduction of ffi in Dart, things became more smoother now, with a better performance as the interction now is Dart/Rust directly, without a need for Dart/Kotlin/Rust or Dart/Swift/Rust cycle, below a simple example:

First src/lib.rs

#[no_mangle]
pub extern fn rust_fn(x: i32) -> i32 {   
    println!("Hello from rust\nI'll return: {}", x.pow(2));
    x.pow(2)
 }

and Cargo.toml

[package]
name = "Double_in_Rost"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"

[lib]
name = "rust_lib"
crate-type = ["dylib"] # could be `staticlib` as well

[dependencies]

Running cargo build --release will generate target\release\rust_lib.dll copy/paste it into Dart application root directory

Write Dart code as below:

import 'dart:ffi';
import 'dart:io' show Platform;

// FFI signature of the hello_world C function
typedef ffi_func = Int32 Function(Int32 x);  //pub extern fn rust_fn(x: i32) -> i32
// Dart type definition for calling the C foreign function
typedef dart_func = int Function(int x);

void main() {
  // Open the dynamic library
  var path = './rust_lib.so';
  if (Platform.isMacOS) path = './rust_lib.dylib';
  if (Platform.isWindows) path = 'rust_lib.dll';
  final dylib = DynamicLibrary.open(path);
  // Look up the Rust/C function
  final my_func =
      dylib.lookup<NativeFunction<ffi_func>>('rust_fn').asFunction<dart_func>();
  print('Double of 3 is ${my_func(3)}');
}

enter image description here

like image 156
Hasan A Yousef Avatar answered Sep 22 '22 06:09

Hasan A Yousef