activity-manager 0.1.0

A UI-agnostic, Android-style activity and routing stack manager for Rust. / 一个无 UI 框架依赖的通用 Android 风格页面与路由堆栈管理框架。
Documentation
  • Coverage
  • 94.12%
    32 out of 34 items documented0 out of 24 items with examples
  • Size
  • Source code size: 33.18 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.46 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 39s Average build duration of successful builds.
  • all releases: 47s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • paulzhang5511/activity-manager
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • paulzhang5511

Activity Manager

Crates.io Documentation License

Activity Manager is a UI-agnostic, Android-style activity and routing stack manager for Rust. 它是一个无 UI 框架依赖的通用 Android 风格页面(Activity)与路由堆栈管理框架。

本框架通过 ActivityHost 抽象了底层的 UI 渲染和事件循环机制,使得核心的路由调度和生命周期管理逻辑可以独立于特定的 UI 框架(如 Iced)运行。

🌟 核心特性 (Core Features)

  • 高度解耦 (UI-Agnostic): 完全剥离具体 UI 框架依赖,通过 ActivityHost Trait 适配任意 UI 库。
  • 生命周期管理 (Lifecycle): 提供 on_create, on_resume, on_pause, on_destroy, on_new_intent 等完整的生命周期流转。
  • 经典启动模式 (Launch Modes): 完美还原 Android 的四种经典启动模式:
    • Standard: 标准新建模式
    • SingleTop: 栈顶复用模式
    • SingleTask: 栈内复用模式 (Clear Top)
    • SingleInstance: 全局单例独占模式
  • 半透明叠加渲染 (Painter's Algorithm): 原生支持 Dialog 风格的半透明页面叠加,智能阻断底层被遮挡页面的无效渲染与无效事件订阅。
  • 依赖注入 (DI): 在 Activity 创建时通过工厂闭包自动注入全局 Context。

📦 安装 (Installation)

在你的 Cargo.toml 中添加:

[dependencies]
activity-manager = "0.1.0"

或者使用命令快速添加:

cargo add activity-manager

📖 完整使用指南 (Usage Guide)

Activity Manager 的核心思想是**“托管”**。你需要将它嵌入到你所使用的具体 UI 框架(如 Iced 等)的主状态机中。以下演示如何在一个典型的应用生命周期中集成并使用本框架。

1. 定义你的环境:Host 与 Route

首先,告诉框架你所使用的 UI 底层类型(Host)以及你的页面路由表(Route)。

use activity_manager::{ActivityHost, LaunchMode, Route};

// 1. 定义 Host (以假设的某个 UI 框架为例)
pub struct AppHost;
impl ActivityHost for AppHost {
    type View = String;            // 你的 UI 框架的视图类型 (如 iced::Element)
    type Effect = ();              // 你的 UI 框架的异步任务类型 (如 iced::Task)
    type Subscription = ();        // 订阅类型 (如 iced::Subscription)
    type Message = AppMessage;     // 全局消息枚举
}

// 2. 定义全局消息
#[derive(Debug, Clone)]
pub enum AppMessage {
    GoToSettings,
    GoBack,
    UserClickedButton,
}

// 3. 定义路由与启动模式
#[derive(Debug, Clone, PartialEq)]
pub enum AppRoute {
    Home,
    Settings,
}
impl Route for AppRoute {
    fn launch_mode(&self) -> LaunchMode {
        match self {
            AppRoute::Home => LaunchMode::SingleTask,
            AppRoute::Settings => LaunchMode::Standard,
        }
    }
    fn is_translucent(&self) -> bool { false }
}

2. 编写你的 Activity (页面)

每个页面都是一个独立的状态机,只需实现 Activity 接口:

use activity_manager::{Activity, Intent};

// 共享的全局状态 (Context)
#[derive(Clone)]
pub struct AppContext {
    pub user_token: String,
}

// 首页 Activity
pub struct HomeActivity;
impl Activity<AppRoute, AppHost, AppContext> for HomeActivity {
    fn route(&self) -> AppRoute { AppRoute::Home }

    // 处理当前页面的逻辑
    fn update(&mut self, message: AppMessage) -> Vec<()> {
        match message {
            AppMessage::UserClickedButton => {
                println!("Button clicked on Home!");
                vec![] // 返回 UI Effect / Task
            }
            _ => vec![]
        }
    }

    // 渲染当前页面的 UI
    fn view(&self) -> String {
        "<h1>Welcome Home</h1>".to_string()
    }
    
    // 可选:实现 on_create, on_resume, on_pause 等生命周期钩子
}

3. 在应用主循环中集成 Manager

这是最关键的一步。你需要将 ActivityManager 存放在你应用的最顶层状态中,并将应用的 updateview 委托给 Manager。

use activity_manager::{ActivityManager, Intent};

// 你的应用根状态
pub struct MyApplication {
    manager: ActivityManager<AppRoute, AppHost, AppContext>,
}

impl MyApplication {
    /// 应用初始化
    pub fn new() -> Self {
        let context = AppContext { user_token: "123".to_string() };
        
        // 定义依赖注入工厂:根据 Route 创建对应的 Activity 实例
        let factory = Box::new(|route: &AppRoute, _ctx: &AppContext| -> Box<dyn Activity<AppRoute, AppHost, AppContext>> {
            match route {
                AppRoute::Home => Box::new(HomeActivity),
                AppRoute::Settings => /* Box::new(SettingsActivity) */ unimplemented!(),
            }
        });

        // 启动初始页面 (Home)
        let (manager, _initial_effects) = ActivityManager::new(AppRoute::Home, context, factory);

        Self { manager }
    }

    /// 接管来自 UI 框架的消息流转
    pub fn update(&mut self, message: AppMessage) -> Vec<()> {
        let mut effects = Vec::new();

        // 1. 处理全局级别的路由跳转
        match message {
            AppMessage::GoToSettings => {
                let intent = Intent::new(AppRoute::Settings);
                effects.extend(self.manager.start_activity(intent));
                return effects; // 发生路由跳转时,拦截消息不再向下传递
            }
            AppMessage::GoBack => {
                effects.extend(self.manager.back());
                return effects;
            }
            _ => {}
        }

        // 2. 将非路由消息派发给当前处于栈顶的活跃 Activity 去处理
        effects.extend(self.manager.update(message));
        
        effects
    }

    /// 接管视图渲染
    pub fn view(&self) -> String {
        // manager.views() 会返回当前需要渲染的页面层级(已处理半透明与遮挡逻辑)。
        // 你需要使用你的 UI 框架的容器(如 Stack / ZStack)将它们从底到顶叠放起来。
        let active_views: Vec<String> = self.manager.views();
        
        // 伪代码:将多个视图层叠渲染
        format!("<Stack>\n{}\n</Stack>", active_views.join("\n"))
    }
}

💡 核心工作流总结

  1. 渲染流:UI 框架请求视图 -> MyApplication::view -> manager.views() -> 获取活动页面的 Activity::view 组合并返回。
  2. 事件流:用户交互 -> UI 框架触发 AppMessage -> MyApplication::update -> 拦截路由消息或透传给 manager.update() -> 栈顶的 Activity::update 接收消息并更新自身状态。

📄 协议 (License)

MIT