Android Compose是如何使用什么架构,多个Activity?还是Fragment?compose的ui又是如何卸载和挂载的呢?

目录:

  1. 前言
  2. 定义导航图(Navigation Graph)和路由(Route),用代码声明页面跳转逻辑 2.1 composeable页面卸载的时候,数据如何保存? 2.2 composeable挂载页面的时候,是覆盖,还是替换呢?

一、前言

最近在学习compose,先是找文章来学习,然后看github上面的源码来学习,最后再找视频来学习,然后再上github的项目学习,接着在根据项目的源码,自己删除再重新写一遍。一波三折。

在学习的过程,我就有一个疑问,只有一个Activity,也没有看到Fragment呢,也没有看到更多的Fragment,我好奇,compose的生命周期是如何的,他的页面又是如何加载的,没看到onstart方法,或者页面停止的时候onstop方法。

接下来,我们来解答一下这些疑惑。


二、通过定义导航图(Navigation Graph)和路由(Route),用代码声明页面跳转逻辑

在 Jetpack Compose 中,不再推荐使用多个 Activity 或 Fragment 来实现页面跳转,而是采用 单一 Activity + 多 Composable 组件 + 导航库 的架构。这是 Compose 的核心理念:通过声明式 UI 和状态驱动的导航来简化开发。

  1. 单一 Activity:整个应用通常只有一个 Activity(MainActivity),所有页面切换通过 Composable 组件 的替换完成。
  2. 无 Fragment:Compose 完全抛弃 Fragment,直接通过 NavController 和 NavHost 管理页面栈。
  3. 声明式导航:通过定义导航图(Navigation Graph)和路由(Route),用代码声明页面跳转逻辑。

那么,为什么要使用这种方式呢?

  1. 传统多 Activity/Fragment 的痛点: 1.1 每个 Activity 都有自己的生命周期,跨页面数据传递需要序列化(Parcelable/Serializable),容易导致内存泄漏或状态不一致 。 1.2 每次跳转新 Activity 都会触发冷启动,导致界面卡顿。
  2. Compose 新架构的核心优势 2.1 Composable 函数根据状态自动重组,无需手动更新视图。页面切换本质是 Composable 组件的重组,无需启动新 Activity 或 Fragment。 2.2 通过 NavController 管理全局导航栈,跳转逻辑集中且类型安全。

接下来,我们就看看导航是如何使用的(需要有一定基础~~)。


2.1 代码例子

1、导航代码

kotlin 复制代码
fun AppScaffold() {
    val navCtrl = rememberNavController() // 获取导航控制器
    val navBackStackEntry by navCtrl.currentBackStackEntryAsState() // 监听导航栈变化 从而确定当前选中的底部导航项。这样可以确保底部导航栏的状态与当前显示的路由一致。
    val currentDestination = navBackStackEntry?.destination // 当前路由目标
    //接下来我要构建底部一个导航栏,所以需要使用到Scaffold
    Scaffold(
        modifier = Modifier.
        statusBarsPadding().
        navigationBarsPadding(),
        //底部的导航栏内容,会放到NavHost里面。
        bottomBar = {
            when (currentDestination?.route) {
                 RouteName.HOME-> {BottomNavBarView(navCtrl = navCtrl)}
                RouteName.CATEGORY-> {BottomNavBarView(navCtrl = navCtrl)}
                RouteName.COLLECTION-> {BottomNavBarView(navCtrl = navCtrl)}
                RouteName.PROFILE-> {BottomNavBarView(navCtrl = navCtrl)}
            }
        },
        content = {
            // 定义需要保持状态的页面索引(防止页面切换时重置)
            var homeIndex = remember { 0 }
            var categoryIndex = remember { 0 }

            // 导航宿主容器
            NavHost(
                modifier = Modifier.background(MaterialTheme.colors.background),
                navController = navCtrl,
                startDestination = RouteName.HOME // 初始路由
            ) {
                // 首页路由
                composable(route = RouteName.HOME) {
                    HomePage(navCtrl)
                }
                // 分类页路由
                composable(route = RouteName.CATEGORY) {
                    CategoryPage(navCtrl)
                }
                // 收藏页路由
                composable(route = RouteName.COLLECTION) {
                    CollectPage(navCtrl)
                }

                // 个人中心页路由
                composable(route = RouteName.PROFILE) {
                    ProfilePage(navCtrl)
                }
            }
        }
    )
}

2、点击切换

kotlin 复制代码
@Composable
fun BottomNavBarView(navCtrl: NavHostController) {
    //导航的标题,图片,路径
    val bottomNavList = listOf(
        BottomNavRoute.Home,
        BottomNavRoute.Category,
        BottomNavRoute.Collection,
        BottomNavRoute.Profile
    )
    //底部导航栏ui。
    BottomNavigation {
        val navBackStackEntry by navCtrl.currentBackStackEntryAsState() // 监听导航栈变化 从而确定当前选中的底部导航项。这样可以确保底部导航栏的状态与当前显示的路由一致。
        val currentDestination = navBackStackEntry?.destination// 当前路由目标
        bottomNavList.forEach { screen ->
            BottomNavigationItem(
                modifier = Modifier.background(AppTheme.colors.themeUi),
                icon = {
                    Icon(
                        imageVector = screen.icon,
                        contentDescription = null
                    )
                },
                label = { Text(text = stringResource(screen.stringId)) },
                //判断当前页面是否在某个导航图中
                selected = currentDestination?.hierarchy?.any { it.route == screen.routeName } == true,//那个被选择,那个就高亮。
                onClick = {
                    //如果点击相同的,那么就不管
                    if (currentDestination?.route == screen.routeName) {
                        return@BottomNavigationItem
                    }

                    //跳转
                    navCtrl.navigate(screen.routeName)

                }
            )
        }
    }
}

3、HomePage :一个背景,其他页面也都是,用来测试,就写的简单一些~

kotlin 复制代码
@Composable
fun HomePage(
    navCtrl: NavHostController,
) {
   Box(
       modifier =Modifier.fillMaxSize().background(Color.Red)
   )
}

这里,还有一个疑问,那么他重组,其他页面内容又是如何保存的呢?还是说直接就销毁了呢?比如从首页切换到分类,那么是覆盖,还是什么呢?


2.2 挂载和卸载

Composable 的挂载/卸载:当 Composable 进入/退出组合(Composition)时,可以视为类似 onStart/onStop 的时机。可以通过

1、挂载(进入组合)

当 Composable ​首次被调用 或 ​在重组中被重新需要 时,会挂载到组合中。

初始化状态:通过 remember 或 mutableStateOf 创建的状态会被初始化。 ​执行副作用:如 LaunchedEffect、DisposableEffect 的首次运行。 ​构建 UI 树:将 Composable 添加到 Compose 的 UI 树中。

示例

kotlin 复制代码
@Composable
fun MyComponent() {
    // 挂载时初始化状态
    val count = remember { mutableStateOf(0) }
    
    // 挂载时启动副作用
    LaunchedEffect(Unit) {
        delay(1000)
        println("Component mounted!")
    }
    
    Text("Count: ${count.value}")
}
2、卸载(退出组合)

当 Composable ​在重组过程中不再被调用 时,会从组合中卸载。

常见场景:

条件语句跳过:因 if/when 条件不满足而不再执行。 ​导航离开页面:通过 NavController 跳转到其他页面。 ​父组件卸载:父 Composable 被卸载导致子组件连带卸载。

行为: ​清理副作用:触发 DisposableEffect 的 onDispose 或取消 LaunchedEffect 的协程。 ​释放资源:如取消网络请求、关闭数据库连接。 ​从 UI 树移除:Composable 的 UI 节点被销毁。

示例

kotlin 复制代码
@Composable
fun ParentComponent(showChild: Boolean) {
    if (showChild) {
        ChildComponent() // 挂载
    }
    // 当 showChild 变为 false 时,ChildComponent 卸载,因为条件不满足了,这里要好好理解。因为ChildComponent方法不被调用了,就会卸载掉。
}

@Composable
fun ChildComponent() {
    // 副作用:挂载时启动,卸载时清理
    DisposableEffect(Unit) {
        val resource = allocateResource()
        onDispose {
            releaseResource(resource) // 卸载时执行清理
        }
    }
    
    Text("Child Component")
}

现在我们可以回答一个问题,比如从首页切换到分类,那么是覆盖,还是什么呢?不是覆盖,是重新绘制,消失就会被卸载掉,不会留着,绘制变更的内容。

那么页面数据呢?也会初始化,重新获取数据?看你使用的是什么样的

一、Composable 卸载后的状态

  1. ​状态是否销毁?

    ​普通状态(remember)​: 使用 remember 保存的状态 ​会被销毁。当 Composable 卸载后,其内部状态会被释放,下次挂载时会重新初始化。

    kotlin 复制代码
    @Composable
    fun Counter() {
        val count = remember { mutableStateOf(0) } // 卸载时销毁
        Button(onClick = { count.value++ }) {
            Text("Count: ${count.value}")
        }
    }

    ​可保存状态(rememberSaveable)​: 使用 rememberSaveable 的状态会在 ​配置变更(如屏幕旋转)时保留,但 ​Composable 完全卸载后依然会被销毁。

kotlin 复制代码
  val count = rememberSaveable { mutableStateOf(0) } // 配置变更保留,卸载销毁
  1. ​副作用是否清理?

    LaunchedEffect/DisposableEffect: 卸载时会自动取消协程或触发 onDispose 清理资源。

    kotlin 复制代码
     DisposableEffect(Unit) {
         // 挂载时执行
         onDispose { 
             // 卸载时清理 
         }
     }

二、重新挂载时的行为

当 Composable ​再次被调用进入组合 时(例如条件语句重新满足、导航返回页面等),会触发 ​重新挂载,此时:

  1. 重新执行整个函数体

Composable 函数会从头到尾重新执行,包括:

所有 remember 的初始化
所有副作用的启动(如 LaunchedEffect)
  1. ​状态重置

除非使用 ​状态提升 或 ​持久化存储,否则状态会被重置:

kotlin 复制代码
// 示例:重新挂载时状态重置
@Composable
fun DynamicComponent(visible: Boolean) {
    if (visible) {
        val count = remember { mutableStateOf(0) } // 每次挂载都从 0 开始
        Button(onClick = { count.value++ }) {
            Text("Count: ${count.value}")
        }
    }
}

那么怎么办呢?使用viewmodel,大家应该都知道viewmodel的生命周期。其生命周期与 Activity/Fragment 或导航图绑定。通过 ViewModel 保存状态。

kotlin 复制代码
@Composable
fun PersistentCounter(viewModel: CounterViewModel = viewModel()) {
    Button(onClick = { viewModel.increment() }) {
        Text("Count: ${viewModel.count.value}")
    }
}

class CounterViewModel : ViewModel() {
    val count = mutableStateOf(0)
    fun increment() { count.value++ }
}

比如说,我们发起一起请求,请求到的数据放到viewModel中,那么重新挂载的时候,直接把数据拿出来就可以,不需要重新执行请求

相关推荐
然后就去远行吧1 小时前
小程序 wxml 语法 —— 37 setData() - 修改对象类型数据
android·前端·小程序
熙曦Sakura1 小时前
【MySQL】数据类型
android·mysql·adb
故事与他6451 小时前
CTFHub-上传文件
android·ide·windows·web安全·网络安全·android studio·xss
大胃粥2 小时前
Android app 冷启动(7) 执行动画
android
yi诺千金2 小时前
Android U 分屏——SystemUI侧处理
android
顾林海2 小时前
Flutter Dart 流程控制语句详解
android·前端·flutter
Cui晨2 小时前
Android 滑块开关 自定义Switch
android
&有梦想的咸鱼&2 小时前
Android Retrofit 框架注解定义与解析模块深度剖析(一)
android·retrofit
烬奇小云2 小时前
安卓7.0到11.0的更新变化(简单理解)
android·安卓逆向
whatever who cares2 小时前
android:实现圆角效果
android