状态管理

本节提供的是使用 Rinf 有效管理应用状态的一般性指南,而非介绍某个特定的 Rinf 功能。

当应用逻辑完全用 Rust 编写、Flutter 仅用作 GUI 时,Rinf 的表现最佳。基于此,希望将应用状态存储在 Rust 中。

Actor 模型

强烈推荐使用 Actor 模型来管理 Rust 中的异步状态。通过将状态和行为封装在 Actor 结构体中,这些结构体拥有了所有权并处理自身的异步任务,Actor 模型提供了一种可扩展且模块化的方式来管理复杂的状态交互。

Rinf 提供的默认 Rust 模板是一个很好的起点。下面是一个关于 Actor 如何工作的简短示例:

native/hub/src/lib.rs
use async_trait::async_trait;
use messages::prelude::{Actor, Address, Context, Notifiable};
use rinf::{dart_shutdown, debug_print, write_interface};
use tokio::spawn;

write_interface!();

/// 表示计算两个数之和的消息。
struct Sum(usize, usize);

/// Actor 定义,在实际应用中会持有状态。
struct MyActor {
  count: i32,
}

impl Actor for MyActor {}

impl MyActor {
  pub fn new() -> Self {
    Self { count: 0 }
  }
}

#[async_trait]
impl Notifiable<Sum> for MyActor {
  async fn notify(&mut self, msg: Sum, _: &Context<Self>) {
    self.count += 1;
    debug_print!("{}: {}", msg.0 + msg.1, self.count);
  }
}

fn create_actors() -> Address<MyActor> {
  let context = Context::new();
  let addr = context.address();
  let actor = MyActor::new();
  spawn(context.run(actor));
  addr
}

/// 启动业务逻辑的主函数。
#[tokio::main]
async fn main() {
  let mut addr = create_actors();
  let _ = addr.notify(Sum(10, 5)).await;
  dart_shutdown().await;
}

详细用法请参考示例应用 ,如果无法打开也可以点击此处查看码云上的镜像仓库。

静态变量

一般来说,建议避免使用静态变量,因为其特性可能导致测试困难、生命周期管理不便等问题。如果必须使用静态变量,可以按如下方式声明,确保它们在整个应用运行期间有效。

Rust
use rinf::debug_print;
use tokio::sync::Mutex;

static VECTOR: Mutex<Vec<bool>> = Mutex::const_new(Vec::new());

pub async fn do_something_with_state() {
  // 通过临时加锁来使用全局变量。
  VECTOR.lock().await.push(true);

  // 通过获取守卫来使用全局变量。
  let guard = VECTOR.lock().await;
  let length = guard.len();
  debug_print!("{}", length);
}

仅在以下特定场景下使用静态变量,如 Rust 文档所述:

  • 需要存储大量数据;

  • 需要静态变量的单地址特性;

  • 需要内部可变性。.

请务必注意,实现了 Drop trait 的静态变量的析构函数在应用关闭时不会被调用。因此,如果需要运行静态变量的析构函数,必须在退出前显式地释放或关闭它们。

还有一些替代方案,请根据需求选择您认为最合适的: