Android Activity、Fragment、ViewModel 完全入门指南

1. 概述:为什么需要这三者?

在 Android 中,一个用户看到的界面通常由 Activity 承载,内部由 Fragment 拆分成多个可复用的模块,而界面需要的数据则由 ViewModel 提供。这三者各司其职:

  • Activity:应用的一个独立界面(窗口),是用户交互的入口。

  • Fragment:Activity 内部的一个 UI 片段,可以独立管理自己的布局和生命周期,方便在大屏或横屏时组装界面。

  • ViewModel :专门用来存储和管理 UI 相关的数据,它的生命周期比 Activity/Fragment 更长,可以在屏幕旋转等配置变更时保留数据,避免重新加载。

简单地说,Activity 是"大管家",Fragment 是"拼图块",ViewModel 是"数据管家"。


2. Activity 详解

2.1 Activity 是什么?

Activity 是 Android 四大组件之一,负责向用户提供一个可以交互的界面。一个应用通常包含多个 Activity,它们之间通过 Intent 跳转,构成完整的用户流程。

2.2 Activity 的主要作用

  • 显示布局(setContentView)

  • 响应用户交互(点击、滑动等)

  • 启动其他 Activity 或服务

  • 处理系统回调(如权限请求结果)

2.3 Activity 的生命周期

理解生命周期是避免崩溃和内存泄漏的基础。关键回调方法如下(按调用顺序):

text

复制代码
onCreate() → onStart() → onResume() → (Activity 运行中)
→ onPause() → onStop() → onDestroy()
→ 当 Activity 回到前台:onRestart() → onStart() → onResume()
  • onCreate():Activity 首次创建时调用。这里通常做初始化,如 setContentView、初始化 ViewModel、恢复保存的数据。

  • onStart() :Activity 变为可见时调用,但还没到达前台。

  • onResume():Activity 准备好与用户交互,位于栈顶。

  • onPause():失去焦点,但仍部分可见(如弹出透明对话框),应该保存轻量数据、停止动画等。

  • onStop():完全不可见,应释放较重资源、取消注册广播等。

  • onDestroy():Activity 被销毁前调用,清理所有资源。

  • onRestart():从 onStop 回到 onStart 时调用。

注意 :当发生配置变更(如旋转屏幕)时,系统会销毁并重建 Activity,依次调用 onPause()onStop()onDestroy(),然后重新创建新的 Activity 实例,调用 onCreate() 等。这就是 ViewModel 存在的意义------避免数据在重建时丢失。

2.4 如何使用 Activity

一个最简单的 Activity:

kotlin

复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 初始化ViewModel,设置点击监听等
    }
}

启动另一个 Activity

kotlin

复制代码
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)

在 AndroidManifest.xml 中注册

xml

复制代码
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:label="主页">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

3. Fragment 详解

3.1 Fragment 是什么?

Fragment 是嵌套在 Activity 中的 UI 模块,可以拥有自己的布局和生命周期。它必须依附于一个 Activity 才能存在,一个 Activity 可以包含多个 Fragment。

3.2 Fragment 的主要作用

  • 适应不同屏幕尺寸:在手机上可能是单页面,在平板上左右分屏,左边列表右边详情,可以用两个 Fragment 实现。

  • 代码复用:同样的 Fragment 可用于不同的 Activity。

  • 模块化:将复杂的 UI 拆分成小块,便于维护。

3.3 Fragment 的生命周期

Fragment 的生命周期与宿主 Activity 的生命周期有紧密的联系。在 Activity 的 onCreate() 中,Fragment 被附加时,会依次调用:

text

复制代码
onAttach() → onCreate() → onCreateView() → onViewCreated() 
→ onStart() → onResume() → (Fragment 活跃)
→ onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()
  • onAttach():Fragment 关联到 Activity 时调用。

  • onCreate():初始化 Fragment。

  • onCreateView():创建并返回 Fragment 的布局视图。这里我们 inflate 布局文件。

  • onViewCreated():在 onCreateView() 之后立即调用,可安全地访问布局中的 view,进行初始化操作。

  • onStart():Fragment 可见。

  • onResume():Fragment 可交互。

  • onDestroyView():当 Fragment 视图被移除(如切换到另一个 Fragment)时调用,可以清理视图相关资源。

  • onDestroy() / onDetach():Fragment 被销毁或与 Activity 解除关联。

重点 :Fragment 的视图(View)生命周期与 Fragment 本身的生命周期是解耦的;onDestroyView() 之后 Fragment 对象可能仍然存活,但 View 已销毁。

3.4 如何使用 Fragment

现代开发推荐使用 Jetpack 的 FragmentContainerViewFragmentManager

1. 创建 Fragment 的布局 fragment_example.xml

xml

复制代码
<LinearLayout ...>
    <TextView android:id="@+id/tv_text" ... />
    <Button android:id="@+id/btn_click" ... />
</LinearLayout>

2. 创建 Fragment 类

kotlin

复制代码
class ExampleFragment : Fragment(R.layout.fragment_example) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 可以安全地访问视图了
        view.findViewById<Button>(R.id.btn_click).setOnClickListener {
            // 处理点击
        }
    }
}

这里使用 Fragment(R.layout.fragment_example) 构造器传入了布局,系统会自动 inflate。也可以重写 onCreateView() 手动 inflate。

3. 在 Activity 中添加 Fragment

在 Activity 的布局 XML 中使用 FragmentContainerView

xml

复制代码
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/fragment_container"
    android:name="com.example.app.ExampleFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

或者在代码中动态添加:

kotlin

复制代码
supportFragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment())
    .commit()

4. ViewModel 详解

4.1 ViewModel 是什么?

ViewModel 是 Android Jetpack 架构组件之一,专门用于以生命周期感知的方式存储和管理 UI 相关数据。它能够在配置变更(如屏幕旋转)时保留数据,使得 UI 重建后无需重新加载。

4.2 ViewModel 的主要作用

  • 数据持久性 :当 Activity 因配置变更而销毁重建时,ViewModel 对象会被保留,持有的数据不丢失。

  • 生命周期感知:ViewModel 自动知道什么时候该清理资源,当关联的 Activity 彻底 finish 时会被清除。

  • 分离 UI 和数据:把处理数据的逻辑从 Activity/Fragment 中抽离出来,便于测试和维护。

  • 共享数据 :在同一 Activity 的多个 Fragment 间共享数据非常方便(使用 activityViewModels())。

4.3 ViewModel 的生命周期

ViewModel 的生命周期比它的宿主 Activity 或 Fragment 更长

  • 当 Activity 首次创建时,ViewModel 被创建。

  • 当屏幕旋转导致 Activity 重建时,同一个 ViewModel 实例会幸存下来,并传递给新创建的 Activity 实例。

  • 只有在 Activity 真正被销毁(finish() 或被系统杀死且不重新创建)时,ViewModel 才会调用 onCleared()

这使得在 ViewModel 中持有 UI 状态数据不会因旋转屏幕而丢失,也避免了内存泄漏(不会因 Activity 重建而持有旧 Activity 引用)。

4.4 如何使用 ViewModel

我们通常将 ViewModel 与 LiveDataStateFlow 结合使用,暴露可观察的数据给 UI。

1. 定义 ViewModel

kotlin

复制代码
class MyViewModel : ViewModel() {
    // 使用 MutableLiveData 或 MutableStateFlow
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

或者使用 StateFlow

kotlin

复制代码
class MyViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count

    fun increment() {
        _count.update { it + 1 }
    }
}

2. 在 Activity/Fragment 中使用 ViewModel

由于 ViewModel 需要在组件的第一时间初始化,可使用 by viewModels() Kotlin 属性委托(需要添加 fragment-ktxactivity-ktx 依赖)。

kotlin

复制代码
class MainActivity : AppCompatActivity() {
    // 通过 viewModels() 委托创建 ViewModel
    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 观察数据并更新 UI
        viewModel.count.observe(this) { value ->
            textViewCount.text = value.toString()
        }

        buttonIncrement.setOnClickListener {
            viewModel.increment()
        }
    }
}

如果是 Fragment,用法类似:

kotlin

复制代码
class MyFragment : Fragment(R.layout.fragment_my) {
    // 注意:如果在多个 Fragment 之间共享同一个 ViewModel,使用 activityViewModels()
    private val viewModel: MyViewModel by viewModels()
    ...
}

3. 在多个 Fragment 间共享数据

使用 activityViewModels(),这两个 Fragment 将获得同一个 ViewModel 实例,共享数据。

kotlin

复制代码
// 在 FragmentA 和 FragmentB 中都可以这样写
private val sharedViewModel: SharedViewModel by activityViewModels()

4.5 ViewModel 的进阶:配合 SavedStateHandle

当进程被系统杀死并恢复时,ViewModel 也会被销毁,此时可以使用 SavedStateHandle 保存少量数据(类似 onSaveInstanceState)。

kotlin

复制代码
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    val query = savedStateHandle.getLiveData("query", "")
    fun setQuery(newQuery: String) {
        savedStateHandle["query"] = newQuery
    }
}

这样即使进程意外被杀,数据也能恢复。


5. 三者的协作关系

一个典型的 UI 交互流程如下:

  1. Activity 创建 ,通过 by viewModels() 获取 ViewModel

  2. Fragment 被添加到 Activity ,同样获取 ViewModel(可能是同一个实例,用于数据共享)。

  3. Fragment 中的按钮点击 ,调用 ViewModel 的方法(如 viewModel.increment())。

  4. ViewModel 更新内部的数据容器(LiveData/StateFlow)。

  5. Activity/Fragment 在 observe()collect() 中收到新数据,自动更新 UI。

  6. 屏幕旋转 :Activity 和 Fragment 会销毁重建,但 ViewModel 存活,数据不丢失,UI 重建后立即显示最新值。

  7. 用户离开 Activity (按返回键):Activity 真正销毁,ViewModel 调用 onCleared(),清理资源。


6. 完整示例:计数器应用

让我们结合三者实现一个简单计数器,包含一个 Activity 和一个 Fragment。

依赖配置(build.gradle 模块级)

kotlin

复制代码
implementation("androidx.fragment:fragment-ktx:1.6.2")
implementation("androidx.activity:activity-ktx:1.8.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")

1. 定义 ViewModel

kotlin

复制代码
class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

2. 创建 Fragment 布局 (fragment_counter.xml)

xml

复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">
    <TextView
        android:id="@+id/tv_count"
        android:textSize="30sp"
        android:text="0" />
    <Button
        android:id="@+id/btn_increment"
        android:text="增加" />
</LinearLayout>

3. Fragment 类

kotlin

复制代码
class CounterFragment : Fragment(R.layout.fragment_counter) {
    // 共享 Activity 级别的 ViewModel,确保与 Activity 同步
    private val viewModel: CounterViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 观察 LiveData
        viewModel.count.observe(viewLifecycleOwner) { count ->
            view.findViewById<TextView>(R.id.tv_count).text = count.toString()
        }
        view.findViewById<Button>(R.id.btn_increment).setOnClickListener {
            viewModel.increment()
        }
    }
}

注意:在 Fragment 中观察 LiveData 时,应该使用 viewLifecycleOwner 而不是 this,以确保观察与 Fragment 视图生命周期绑定,防止内存泄漏。

4. Activity 布局 (activity_main.xml)

xml

复制代码
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/fragment_container"
    android:name="com.example.app.CounterFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

5. MainActivity

kotlin

复制代码
class MainActivity : AppCompatActivity() {
    // 由于我们只在 Fragment 中使用 ViewModel,Activity 本身甚至不需要获取 ViewModel
    // 如果 Activity 自己也需操作数据,同样可获取
    private val viewModel: CounterViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 可以在 Activity 中观察或调用 ViewModel,UI 会同步更新
    }
}

现在,无论你旋转屏幕多少次,计数器的数值都不会丢失,因为 ViewModel 在幕后默默守护着数据。


7. 最佳实践与常见注意事项

  • ViewModel 绝不应该持有 View、Context、Activity 等的引用 :否则会导致内存泄漏或崩溃。如果需要 Context,可以用 AndroidViewModel 获取 Application Context。

  • 在 Fragment 中观察 LiveData/Flow 时,使用 viewLifecycleOwner:否则在 Fragment 视图销毁而 Fragment 对象存活时,UI 更新会异常。

  • Activity 和 Fragment 只负责 UI 逻辑:数据操作、网络请求等全部交给 ViewModel,ViewModel 通过 LiveData/Flow 暴露结果。

  • 不要在 ViewModel 中执行长期存活的协程而没有管理 :可以使用 viewModelScope 自动取消协程。

  • 配置变更(旋转屏幕)不是丢失数据的借口 :正是 ViewModel 存在的原因;对于进程被杀死的情况,配合 SavedStateHandle 保存少量数据。

  • 使用 by viewModels() 时,确保添加了 fragment-ktxactivity-ktx 依赖


8. 总结

  • Activity:提供窗口,管理用户交互,作为 Fragment 的容器。

  • Fragment:模块化 UI,有自己的生命周期,可以嵌套、复用,适应多屏幕。

  • ViewModel:存储 UI 数据,跨配置变更保留,配合可观察数据持有者(LiveData/StateFlow)实现响应式 UI。

三者的关系是:Activity(容器) → Fragment(UI模块) → ViewModel(数据提供者)。理解它们各自的生命周期和责任边界,是写出稳定、干净 Android 应用的基石。

延伸阅读:

相关推荐
V搜xhliang02468 小时前
OpenClaw科研全场景用法:从文献到实验室的完整自动化方案
运维·开发语言·人工智能·python·算法·microsoft·自动化
叶小鸡13 小时前
Java 篇-项目实战-天机学堂(从0到1)-day10
windows·microsoft
宝桥南山1 天前
AI - 在命令行中尝试一下ACP(Agent Client Protocol)通信
microsoft·微软·github·aigc·copilot
技术钱1 天前
Prompt组件以及使用技巧
microsoft·prompt
宝桥南山1 天前
GitHub Models - 尝试一下使用GitHub Models
microsoft·ai·微软·c#·github·.netcore
syounger1 天前
SAP新API政策引发AI生态焦虑:开放平台还是变相锁定?
人工智能·microsoft
Data-Miner1 天前
用DeepSeek V4做表:数以轻舟Agent让做Excel表像聊天一样简单
microsoft·excel
nbwenren2 天前
2026实测:用 Gemini 3.1镜像站 自然语言转 SQL,打造内部数据分析对话式接口
microsoft
武藤一雄2 天前
WPF:MessageBox系统消息框
前端·microsoft·c#·.net·wpf