Skip to main content

activity_manager/
lib.rs

1use std::fmt::Debug;
2
3/// 定义路由的特征。
4///
5/// 路由负责提供页面的启动模式以及是否为半透明页面,这决定了页面的入栈行为和视图渲染逻辑。
6pub trait Route: Clone + Debug + PartialEq {
7    /// 获取该路由的启动模式。
8    fn launch_mode(&self) -> LaunchMode;
9    /// 指示该路由对应的页面是否为半透明。
10    ///
11    /// 如果为 `false`(不透明),渲染器将不会渲染其底部的页面。
12    fn is_translucent(&self) -> bool;
13}
14
15/// Android 风格的页面启动模式。
16///
17/// 决定了新页面启动时,如何与当前的页面堆栈进行交互。
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum LaunchMode {
20    /// 标准模式。
21    ///
22    /// 每次启动该路由,都会创建一个全新的 Activity 实例并压入栈顶。
23    Standard,
24    /// 栈顶复用模式。
25    ///
26    /// 如果目标路由已经位于栈顶,则不会创建新实例,而是调用栈顶实例的 `on_new_intent`;
27    /// 否则,创建新实例并压入栈顶。
28    SingleTop,
29    /// 栈内复用模式。
30    ///
31    /// 检查整个堆栈。如果目标路由已存在于栈内,则将其上方所有的 Activity 弹出并销毁,
32    /// 使其成为新的栈顶,并调用其 `on_new_intent`。如果不存在,则创建新实例压入栈顶。
33    SingleTask,
34    /// 单例模式。
35    ///
36    /// 堆栈中仅允许存在这唯一一个页面。启动此模式的路由时,会清空栈内的所有其他页面。
37    SingleInstance,
38}
39
40/// 宿主环境抽象。
41///
42/// 为框架提供类型映射,使其可以兼容不同的 UI 引擎(如 Iced、Dioxus 等)。
43pub trait ActivityHost: Sized + 'static {
44    /// 框架全局传递的消息类型。
45    type Message: Send + Debug;
46    /// 全局上下文类型,通常用于存储全局状态或共享服务。
47    type Context;
48    /// 业务路由枚举类型。
49    type Route: Route;
50
51    /// 视图类型。
52    ///
53    /// 利用 GATs(通用关联类型)将生命周期 `'a` 绑定到视图上,
54    /// 允许视图直接安全地借用 `Activity` 内部的数据(例如 Iced 的 `Element<'a, Message>`)。
55    type View<'a>
56    where
57        Self: 'a;
58
59    /// 异步任务类型。
60    ///
61    /// 用于封装页面生命周期和交互中产生的副作用(例如 Iced 的 `Task<Message>`)。
62    type Task;
63
64    /// 创建一个不执行任何操作的空任务。
65    ///
66    /// 作为任务系统的“单位元”,用于那些不需要产生副作用的生命周期钩子。
67    fn empty_task() -> Self::Task;
68
69    /// 将多个异步任务合并为一个宏任务。
70    fn batch_tasks(tasks: Vec<Self::Task>) -> Self::Task;
71}
72
73/// 页面组件特征。
74///
75/// 代表一个拥有完整生命周期、独立状态并能响应全局上下文变化的页面模块。
76pub trait Activity<H: ActivityHost>: Send {
77    /// 渲染页面视图。
78    ///
79    /// 生命周期 `'a` 确保返回的视图可以安全地借用 `&self` 或 `&context` 中的数据。
80    fn view<'a>(&'a self, context: &'a H::Context) -> H::View<'a>;
81
82    /// 处理业务逻辑更新,并返回可能产生的异步任务。
83    fn update(&mut self, _context: &mut H::Context, _message: H::Message) -> H::Task {
84        H::empty_task()
85    }
86
87    /// 生命周期钩子:页面实例被创建时调用。
88    fn on_create(&mut self, _context: &mut H::Context) -> H::Task {
89        H::empty_task()
90    }
91
92    /// 生命周期钩子:页面进入前台、恢复交互时调用。
93    fn on_resume(&mut self, _context: &mut H::Context) -> H::Task {
94        H::empty_task()
95    }
96
97    /// 生命周期钩子:页面被新页面覆盖,或即将被销毁时调用。
98    fn on_pause(&mut self, _context: &mut H::Context) -> H::Task {
99        H::empty_task()
100    }
101
102    /// 生命周期钩子:页面实例被彻底销毁、移出堆栈前调用。
103    fn on_destroy(&mut self, _context: &mut H::Context) -> H::Task {
104        H::empty_task()
105    }
106
107    /// 生命周期钩子:当页面命中 `SingleTop` 或 `SingleTask` 复用机制时调用。
108    fn on_new_intent(&mut self, _context: &mut H::Context, _route: H::Route) -> H::Task {
109        H::empty_task()
110    }
111
112    /// 获取当前 Activity 对应的路由配置。
113    fn route(&self) -> H::Route;
114}
115
116/// 页面堆栈与路由调度器。
117///
118/// 负责管理 Activity 的入栈、出栈,协调生命周期方法的调用,并聚合页面切换产生的异步任务。
119pub struct ActivityManager<H: ActivityHost> {
120    stack: Vec<Box<dyn Activity<H>>>,
121    factory: Box<dyn Fn(&H::Route) -> Box<dyn Activity<H>>>,
122}
123
124impl<H: ActivityHost> ActivityManager<H> {
125    /// 创建一个新的 ActivityManager。
126    ///
127    /// `factory` 是一个闭包,负责根据路由枚举实例化对应的 `Activity`。
128    pub fn new<F>(factory: F) -> Self
129    where
130        F: Fn(&H::Route) -> Box<dyn Activity<H>> + 'static,
131    {
132        log::info!("ActivityManager initialized.");
133        Self {
134            stack: Vec::new(),
135            factory: Box::new(factory),
136        }
137    }
138
139    /// 将消息分发给当前处于栈顶的活动页面。
140    pub fn update(&mut self, context: &mut H::Context, message: H::Message) -> H::Task {
141        if let Some(top) = self.stack.last_mut() {
142            log::trace!("Dispatching update to top activity: {:?}", message);
143            top.update(context, message)
144        } else {
145            log::warn!("Attempted to update but activity stack is empty.");
146            H::empty_task()
147        }
148    }
149
150    /// 启动一个新页面,处理路由逻辑及堆栈变化,并返回相关的异步任务。
151    pub fn start_activity(&mut self, context: &mut H::Context, route: H::Route) -> H::Task {
152        let mode = route.launch_mode();
153        log::info!(
154            "Starting activity with route: {:?}, launch mode: {:?}",
155            route,
156            mode
157        );
158
159        match mode {
160            LaunchMode::Standard => self.push_activity(context, route),
161
162            LaunchMode::SingleTop => {
163                if let Some(top) = self.stack.last_mut() {
164                    if top.route() == route {
165                        log::debug!("SingleTop matched. Reusing top activity.");
166                        return top.on_new_intent(context, route);
167                    }
168                }
169                log::debug!("SingleTop not matched. Pushing as Standard.");
170                self.push_activity(context, route)
171            }
172
173            LaunchMode::SingleTask => {
174                let mut found_index = None;
175                for (i, act) in self.stack.iter().enumerate() {
176                    if act.route() == route {
177                        found_index = Some(i);
178                        break;
179                    }
180                }
181
182                if let Some(index) = found_index {
183                    log::debug!("SingleTask matched at index {}. Clearing top.", index);
184                    let mut tasks = Vec::new();
185
186                    // 1. 销毁目标上方所有页面 (Clear Top)
187                    while self.stack.len() > index + 1 {
188                        tasks.push(self.pop_internal(context));
189                    }
190
191                    // 2. 复用目标页面
192                    let target = &mut self.stack[index];
193                    tasks.push(target.on_new_intent(context, route));
194                    tasks.push(target.on_resume(context));
195
196                    H::batch_tasks(tasks)
197                } else {
198                    log::debug!("SingleTask target not found in stack. Pushing new.");
199                    self.push_activity(context, route)
200                }
201            }
202
203            LaunchMode::SingleInstance => {
204                log::debug!("SingleInstance mode. Clearing entire stack.");
205                let mut tasks = Vec::new();
206                while !self.stack.is_empty() {
207                    tasks.push(self.pop_internal(context));
208                }
209                tasks.push(self.push_activity(context, route));
210                H::batch_tasks(tasks)
211            }
212        }
213    }
214
215    /// 模拟物理返回键逻辑,弹出栈顶页面。
216    ///
217    /// 返回一个元组:
218    /// - `bool`: 如果成功返回上一页则为 `true`;如果栈已空或仅剩一页无法返回则为 `false`。
219    /// - `H::Task`: 退出动作产生的异步任务。
220    pub fn back(&mut self, context: &mut H::Context) -> (bool, H::Task) {
221        if self.stack.len() > 1 {
222            log::info!("Back navigation triggered. Popping top activity.");
223            let mut tasks = Vec::new();
224            tasks.push(self.pop_internal(context)); // 销毁当前页
225
226            if let Some(top) = self.stack.last_mut() {
227                log::debug!("Resuming the new top activity.");
228                tasks.push(top.on_resume(context)); // 恢复前一个页
229            }
230            (true, H::batch_tasks(tasks))
231        } else {
232            log::info!("Back navigation ignored. Stack has 1 or fewer activities.");
233            (false, H::empty_task())
234        }
235    }
236
237    /// 执行视图构建,利用“画家算法”收集需要渲染的页面。
238    ///
239    /// 从栈顶向下查找,直到遇到第一个不透明(`is_translucent() == false`)的页面为止,
240    /// 仅收集并返回可见层级的视图。
241    pub fn views<'a>(&'a self, context: &'a H::Context) -> Vec<H::View<'a>> {
242        log::trace!("Collecting views for rendering.");
243        let mut views = Vec::new();
244        let mut start_index = 0;
245
246        // 从后往前扫描,寻找渲染截断点
247        for (i, act) in self.stack.iter().enumerate().rev() {
248            if !act.route().is_translucent() {
249                start_index = i;
250                log::trace!(
251                    "Found opaque activity at index {}. Stopping downward scan.",
252                    i
253                );
254                break;
255            }
256        }
257
258        for i in start_index..self.stack.len() {
259            views.push(self.stack[i].view(context));
260        }
261        views
262    }
263
264    /// 内部方法:执行入栈逻辑并触发响应生命周期
265    fn push_activity(&mut self, context: &mut H::Context, route: H::Route) -> H::Task {
266        log::debug!("Pushing new activity: {:?}", route);
267        let mut tasks = Vec::new();
268
269        if let Some(top) = self.stack.last_mut() {
270            tasks.push(top.on_pause(context));
271        }
272
273        let mut new_act = (self.factory)(&route);
274        tasks.push(new_act.on_create(context));
275        tasks.push(new_act.on_resume(context));
276
277        self.stack.push(new_act);
278        H::batch_tasks(tasks)
279    }
280
281    /// 内部方法:执行出栈逻辑并触发响应生命周期
282    fn pop_internal(&mut self, context: &mut H::Context) -> H::Task {
283        if let Some(mut act) = self.stack.pop() {
284            log::debug!("Popping activity: {:?}", act.route());
285            let t1 = act.on_pause(context);
286            let t2 = act.on_destroy(context);
287            H::batch_tasks(vec![t1, t2])
288        } else {
289            H::empty_task()
290        }
291    }
292
293    /// 获取当前栈顶页面的路由。
294    ///
295    /// 如果栈为空则返回 `None`。
296    pub fn current_route(&self) -> Option<H::Route> {
297        self.stack.last().map(|act| act.route())
298    }
299
300    /// 获取当前栈中的页面数量(仅用于测试或状态监控)
301    pub fn stack_len(&self) -> usize {
302        self.stack.len()
303    }
304}
305
306// ============================================================================
307// 单元测试模块
308// ============================================================================
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    // 1. 定义 Mock 的路由
314    #[derive(Debug, Clone, PartialEq)]
315    enum TestRoute {
316        StandardPage,
317        SingleTopPage,
318        SingleTaskPage,
319        SingleInstancePage,
320        TranslucentPage,
321        OpaquePage,
322    }
323
324    impl Route for TestRoute {
325        fn launch_mode(&self) -> LaunchMode {
326            match self {
327                TestRoute::StandardPage => LaunchMode::Standard,
328                TestRoute::SingleTopPage => LaunchMode::SingleTop,
329                TestRoute::SingleTaskPage => LaunchMode::SingleTask,
330                TestRoute::SingleInstancePage => LaunchMode::SingleInstance,
331                _ => LaunchMode::Standard,
332            }
333        }
334
335        fn is_translucent(&self) -> bool {
336            matches!(self, TestRoute::TranslucentPage)
337        }
338    }
339
340    // 2. 定义 Mock 的宿主环境
341    struct TestHost;
342
343    impl ActivityHost for TestHost {
344        type Message = ();
345        type Context = Vec<String>; // 使用全局 Vec 记录生命周期调用日志
346        type Route = TestRoute;
347        type View<'a> = &'a str;
348        type Task = Vec<String>; // 任务简化为收集命令字符串
349
350        fn empty_task() -> Self::Task {
351            vec![]
352        }
353
354        fn batch_tasks(tasks: Vec<Self::Task>) -> Self::Task {
355            tasks.into_iter().flatten().collect()
356        }
357    }
358
359    // 3. 定义 Mock 的页面 Activity
360    struct TestActivity {
361        route: TestRoute,
362        name: String,
363    }
364
365    impl TestActivity {
366        fn new(route: TestRoute) -> Self {
367            let name = format!("{:?}", route);
368            Self { route, name }
369        }
370    }
371
372    impl Activity<TestHost> for TestActivity {
373        fn view<'a>(
374            &'a self,
375            _context: &'a <TestHost as ActivityHost>::Context,
376        ) -> <TestHost as ActivityHost>::View<'a> {
377            &self.name
378        }
379
380        fn on_create(
381            &mut self,
382            context: &mut <TestHost as ActivityHost>::Context,
383        ) -> <TestHost as ActivityHost>::Task {
384            context.push(format!("on_create: {}", self.name));
385            vec![format!("task_create_{}", self.name)]
386        }
387
388        fn on_resume(
389            &mut self,
390            context: &mut <TestHost as ActivityHost>::Context,
391        ) -> <TestHost as ActivityHost>::Task {
392            context.push(format!("on_resume: {}", self.name));
393            vec![]
394        }
395
396        fn on_pause(
397            &mut self,
398            context: &mut <TestHost as ActivityHost>::Context,
399        ) -> <TestHost as ActivityHost>::Task {
400            context.push(format!("on_pause: {}", self.name));
401            vec![]
402        }
403
404        fn on_destroy(
405            &mut self,
406            context: &mut <TestHost as ActivityHost>::Context,
407        ) -> <TestHost as ActivityHost>::Task {
408            context.push(format!("on_destroy: {}", self.name));
409            vec![format!("task_destroy_{}", self.name)]
410        }
411
412        fn on_new_intent(
413            &mut self,
414            context: &mut <TestHost as ActivityHost>::Context,
415            _route: <TestHost as ActivityHost>::Route,
416        ) -> <TestHost as ActivityHost>::Task {
417            context.push(format!("on_new_intent: {}", self.name));
418            vec![format!("task_new_intent_{}", self.name)]
419        }
420
421        fn route(&self) -> <TestHost as ActivityHost>::Route {
422            self.route.clone()
423        }
424    }
425
426    // 工厂函数
427    fn create_manager() -> ActivityManager<TestHost> {
428        ActivityManager::new(|route: &TestRoute| {
429            Box::new(TestActivity::new(route.clone())) as Box<dyn Activity<TestHost>>
430        })
431    }
432
433    #[test]
434    fn test_launch_mode_standard() {
435        let mut manager = create_manager();
436        let mut context = Vec::new();
437
438        // 连续启动两个相同的 Standard 页面
439        manager.start_activity(&mut context, TestRoute::StandardPage);
440        manager.start_activity(&mut context, TestRoute::StandardPage);
441
442        assert_eq!(manager.stack_len(), 2);
443        // 检查生命周期日志:A 创建 -> A 恢复 -> A 暂停 -> A2 创建 -> A2 恢复
444        assert_eq!(
445            context,
446            vec![
447                "on_create: StandardPage",
448                "on_resume: StandardPage",
449                "on_pause: StandardPage",
450                "on_create: StandardPage",
451                "on_resume: StandardPage"
452            ]
453        );
454    }
455
456    #[test]
457    fn test_launch_mode_single_top() {
458        let mut manager = create_manager();
459        let mut context = Vec::new();
460
461        manager.start_activity(&mut context, TestRoute::StandardPage);
462        manager.start_activity(&mut context, TestRoute::SingleTopPage);
463        context.clear(); // 清空前面的日志以聚焦复用逻辑
464
465        // 再次启动栈顶相同的 SingleTop 页面
466        let task = manager.start_activity(&mut context, TestRoute::SingleTopPage);
467
468        // 栈深度仍然是 2
469        assert_eq!(manager.stack_len(), 2);
470        // 只触发了 on_new_intent
471        assert_eq!(context, vec!["on_new_intent: SingleTopPage"]);
472        // 确保相关的 task 被正确聚合返回
473        assert_eq!(task, vec!["task_new_intent_SingleTopPage"]);
474    }
475
476    #[test]
477    fn test_launch_mode_single_task() {
478        let mut manager = create_manager();
479        let mut context = Vec::new();
480
481        manager.start_activity(&mut context, TestRoute::StandardPage);
482        manager.start_activity(&mut context, TestRoute::SingleTaskPage); // target
483        manager.start_activity(&mut context, TestRoute::StandardPage); // target top 1
484        manager.start_activity(&mut context, TestRoute::StandardPage); // target top 2
485
486        assert_eq!(manager.stack_len(), 4);
487        context.clear();
488
489        // 再次启动 SingleTask 页面
490        let task = manager.start_activity(&mut context, TestRoute::SingleTaskPage);
491
492        // 栈顶的两个 StandardPage 被销毁,栈深恢复为 2
493        assert_eq!(manager.stack_len(), 2);
494        assert_eq!(
495            context,
496            vec![
497                "on_pause: StandardPage",        // top 2 pause
498                "on_destroy: StandardPage",      // top 2 destroy
499                "on_pause: StandardPage",        // top 1 pause
500                "on_destroy: StandardPage",      // top 1 destroy
501                "on_new_intent: SingleTaskPage", // 目标复用
502                "on_resume: SingleTaskPage"
503            ]
504        );
505        // 验证任务批量合并(包含销毁任务和复用任务)
506        assert_eq!(
507            task,
508            vec![
509                "task_destroy_StandardPage",
510                "task_destroy_StandardPage",
511                "task_new_intent_SingleTaskPage"
512            ]
513        );
514    }
515
516    #[test]
517    fn test_launch_mode_single_instance() {
518        let mut manager = create_manager();
519        let mut context = Vec::new();
520
521        manager.start_activity(&mut context, TestRoute::StandardPage);
522        manager.start_activity(&mut context, TestRoute::StandardPage);
523
524        // 启动 SingleInstance
525        manager.start_activity(&mut context, TestRoute::SingleInstancePage);
526
527        // 栈被清空,仅剩下 SingleInstance
528        assert_eq!(manager.stack_len(), 1);
529        assert_eq!(manager.stack[0].route(), TestRoute::SingleInstancePage);
530    }
531
532    #[test]
533    fn test_back_navigation() {
534        let mut manager = create_manager();
535        let mut context = Vec::new();
536
537        manager.start_activity(&mut context, TestRoute::StandardPage); // root
538        manager.start_activity(&mut context, TestRoute::SingleTopPage); // top
539        context.clear();
540
541        let (success, task) = manager.back(&mut context);
542        assert!(success);
543        assert_eq!(manager.stack_len(), 1);
544        assert_eq!(
545            context,
546            vec![
547                "on_pause: SingleTopPage",
548                "on_destroy: SingleTopPage",
549                "on_resume: StandardPage" // 前一个页面恢复
550            ]
551        );
552        assert_eq!(task, vec!["task_destroy_SingleTopPage"]);
553
554        // 栈中仅剩一页,无法继续 back
555        context.clear();
556        let (success, _) = manager.back(&mut context);
557        assert!(!success);
558        assert!(context.is_empty());
559    }
560
561    #[test]
562    fn test_current_route_empty_stack() {
563        let manager = create_manager();
564        assert_eq!(manager.current_route(), None);
565    }
566
567    #[test]
568    fn test_current_route_with_activity() {
569        let mut manager = create_manager();
570        let mut context = Vec::new();
571
572        manager.start_activity(&mut context, TestRoute::StandardPage);
573        assert_eq!(manager.current_route(), Some(TestRoute::StandardPage));
574
575        manager.start_activity(&mut context, TestRoute::SingleTopPage);
576        assert_eq!(manager.current_route(), Some(TestRoute::SingleTopPage));
577    }
578
579    #[test]
580    fn test_current_route_after_back() {
581        let mut manager = create_manager();
582        let mut context = Vec::new();
583
584        manager.start_activity(&mut context, TestRoute::StandardPage);
585        manager.start_activity(&mut context, TestRoute::SingleTopPage);
586        manager.back(&mut context);
587
588        assert_eq!(manager.current_route(), Some(TestRoute::StandardPage));
589    }
590
591    #[test]
592    fn test_current_route_single_task_reuse() {
593        let mut manager = create_manager();
594        let mut context = Vec::new();
595
596        manager.start_activity(&mut context, TestRoute::StandardPage);
597        manager.start_activity(&mut context, TestRoute::SingleTaskPage);
598        manager.start_activity(&mut context, TestRoute::StandardPage);
599        manager.start_activity(&mut context, TestRoute::SingleTaskPage);
600
601        assert_eq!(manager.current_route(), Some(TestRoute::SingleTaskPage));
602        assert_eq!(manager.stack_len(), 2);
603    }
604
605    #[test]
606    fn test_views_painters_algorithm() {
607        let mut manager = create_manager();
608        let mut context = Vec::new();
609
610        manager.start_activity(&mut context, TestRoute::OpaquePage);
611        manager.start_activity(&mut context, TestRoute::TranslucentPage);
612        manager.start_activity(&mut context, TestRoute::TranslucentPage);
613
614        // Opaque -> Translucent -> Translucent
615        // 应该渲染全部三个
616        let views = manager.views(&context);
617        assert_eq!(views.len(), 3);
618        assert_eq!(
619            views,
620            vec!["OpaquePage", "TranslucentPage", "TranslucentPage"]
621        );
622
623        // 在顶部推入一个不透明页面: Opaque -> Trans -> Trans -> Opaque
624        manager.start_activity(&mut context, TestRoute::OpaquePage);
625        let views_top = manager.views(&context);
626
627        // 发生裁剪,仅渲染顶部那个 Opaque
628        assert_eq!(views_top.len(), 1);
629        assert_eq!(views_top, vec!["OpaquePage"]);
630    }
631}