欢迎光临
我们一直在努力

Android随笔-Activity

一、Activity 是什么

Activity 是 Android 四大组件(Activity、Service、BroadcastReceiver、ContentProvider)中唯一负责与用户交互的组件。它提供了一个窗口(Window),用于绘制 UI 并接收用户的触摸、按键等输入事件。

1.1 继承体系

java.lang.Object
└─ android.content.Context ← 提供应用环境接口
└─ android.content.ContextWrapper ← 包装代理模式
└─ android.view.ContextThemeWrapper ← 主题支持
└─ android.app.Activity ← 核心实现

Activity 间接继承自 Context,因此它具备访问资源、启动其他组件、发送广播等能力。同时它实现了 Window.Callback、KeyEvent.Callback 等接口,负责接收窗口和按键事件。

1.2 核心职责

职责说明
UI 容器 通过 setContentView() 加载布局,管理 View 树
生命周期管理 系统通过回调方法通知 Activity 状态变化
任务栈管理 参与 Task 和 Back Stack 的调度
数据传递 通过 Intent 和 Bundle 在组件间传递数据
配置响应 响应屏幕旋转、语言切换等配置变更

二、生命周期详解

Activity 生命周期由 7 个核心回调 构成,系统在不同状态转换时触发对应方法。

2.1 完整生命周期图

┌─────────────┐
│ onCreate │ ← 实例创建,初始化 UI
└──────┬──────┘

┌─────────────┐
│ onStart │ ← 可见但不可交互
└──────┬──────┘

┌─────────────┐
│ onResume │ ← 前台,可交互 ★
└──────┬──────┘

┌────────────────────────────┼────────────────────────────┐
│ 其他 Activity 部分覆盖 │ 其他 Activity 完全覆盖 │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ onPause │ │ onPause │ │ │
│ (仍可见) │ └─────┬─────┘ │ │
└─────┬─────┘ ▼ │ │
│ ┌───────────┐ │ │
│ │ onStop │ ← 完全不可见 │ │
│ │ (后台状态) │ │ │
│ └─────┬─────┘ │ │
│ │ │ │
│ ┌────────────┘ │ │
│ ▼ │ │
│ ┌───────────┐ │ │
│ │ onRestart │ ← 从后台回到前台 │ │
│ └─────┬─────┘ │ │
│ │ │ │
└─────────────┘ │ │
▼ │
┌───────────┐ │
│ onDestroy │ ← 销毁
└───────────┘ │

←─────────────────────────────────────────────────────────────────────┘
返回/退出

2.2 各回调方法详解

回调触发时机典型操作调用次数
onCreate() Activity 实例首次创建,PhoneWindow、ContextImpl 赋值完成后 setContentView()、初始化变量、恢复 savedInstanceState 1 次
onStart() Activity 即将进入前台可见状态 注册广播、启动动画 多次
onResume() Activity 进入前台,可接收用户输入 获取焦点、启动相机/定位等需要前台权限的操作 多次
onPause() Activity 失去焦点(被部分覆盖或弹窗) 保存关键数据、停止动画、释放相机 多次
onStop() Activity 完全不可见 注销广播、释放资源、停止定位 多次
onRestart() 从 onStop 状态回到前台时 刷新数据 多次
onDestroy() Activity 被销毁前 释放所有资源、取消网络请求、解绑服务 1 次

2.3 关键状态定义

  • Active/Running:onResume() 后,位于前台,可与用户交互
  • Paused:onPause() 后,失去焦点,但仍部分可见(如被透明/对话框 Activity 覆盖)
  • Stopped:onStop() 后,完全不可见,进入后台
  • Killed/Destroyed:onDestroy() 后,等待 GC 回收

2.4 生命周期回调顺序(典型场景)

场景A 的生命周期B 的生命周期
A 启动 B onPause() → onStop() onCreate() → onStart() → onResume()
按返回键从 B 回到 A onPause() → onStop() → onDestroy()
onRestart() → onStart() → onResume()
按 Home 键 onPause() → onStop()
从最近任务恢复 onRestart() → onStart() → onResume()
屏幕旋转 onPause() → onStop() → onDestroy() → onCreate() → onStart() → onResume()

重要:新 Activity 的 onResume() 总是在旧 Activity 的 onPause() 之后执行。系统保证这两个回调的时序,以确保前台 Activity 始终只有一个。

三、启动模式与任务栈

3.1 Task(任务)与 Back Stack(返回栈)

Task 是用户在执行某项任务时与之交互的 Activity 集合,这些 Activity 按照打开顺序排列在 Back Stack(返回栈) 中。栈顶的 Activity 是当前正在显示的页面,按返回键时栈顶 Activity 出栈并销毁。

3.2 四种启动模式

在 AndroidManifest.xml 中通过 android:launchMode 配置:

启动模式行为适用场景
standard(默认) 每次启动都创建新实例,入栈 普通页面
singleTop 栈顶复用:若目标在栈顶,调用 onNewIntent() 而非创建新实例;不在栈顶则新建 通知点击跳转、搜索页面
singleTask 栈内复用:整个任务栈中只有一个实例。启动时若已存在,将其上方所有 Activity 清除,并调用 onNewIntent() 应用首页、WebView 页面
singleInstance 独占一个任务栈,栈中只有该 Activity 一个实例 来电界面、视频通话
3.2.1 singleTask 的 “clearTop” 效果

当前栈:A → B → C → D
启动 B(singleTask)后:
结果栈:A → B (C、D 被清除)

3.2.2 singleTop 的 onNewIntent()

当栈顶复用时,Activity 不会被重建,系统通过 onNewIntent(Intent) 传递新的 Intent:

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent); // 重要:更新 Intent,否则 getIntent() 仍是旧的
// 处理新数据
}

3.3 taskAffinity(任务亲和性)

taskAffinity 定义了 Activity 倾向于归属哪个 Task,默认值为应用包名。
单独设置 taskAffinity 不会生效,必须配合以下条件之一:

条件效果
launchMode="singleTask" Activity 启动时,若不存在对应 affinity 的 Task,则新建 Task
launchMode="singleInstance" 强制新建独立 Task
Intent.FLAG_ACTIVITY_NEW_TASK 配合不同 taskAffinity 开启新 Task
allowTaskReparenting="true" Activity 可从启动它的 Task 迁移到 affinity 匹配的 Task

<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:taskAffinity="com.example.main" />

3.4 Intent Flag(动态控制)

Intent Flag 的优先级高于 Manifest 中的静态配置:

Flag作用
FLAG_ACTIVITY_NEW_TASK 在新 Task 中启动(通常配合 taskAffinity)
FLAG_ACTIVITY_CLEAR_TOP 若目标 Activity 存在,清除其上所有 Activity 并复用目标
FLAG_ACTIVITY_SINGLE_TOP 同 singleTop 效果
FLAG_ACTIVITY_CLEAR_TASK 清除整个 Task,目标 Activity 作为新根
FLAG_ACTIVITY_NO_HISTORY 启动后不保留在栈中

典型用法:退出登录后跳转登录页

val intent = Intent(this, LoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)

四、Activity 启动流程(Framework 层)

用户点击图标 / startActivity()


Instrumentation.execStartActivity() ← 应用进程


ActivityTaskManagerService.startActivity() ← Binder IPC 到 system_server


ATMS 进行权限检查、Intent 解析、启动模式计算


ActivityStarter.execute() ← 决定如何启动(新实例?复用?新 Task?)


RootWindowContainer / Task 管理栈状态


如果需要创建新进程:
ActivityManagerService.startProcessLocked()
Zygote fork 新进程


新进程启动后:
ActivityThread.main() → Looper.prepare() → Looper.loop()
attachApplication() 到 AMS


ATMS 调度 Activity 启动


ApplicationThread.scheduleTransaction() ← Binder 回调到应用进程


ClientTransactionHandler.handleLaunchActivity()


Instrumentation.newActivity() ← 反射创建 Activity 实例


Activity.attach() ← 创建 PhoneWindow、WindowManager


Instrumentation.callActivityOnCreate() ← 调用 onCreate()


Activity.onCreate() → setContentView()


WindowManager.addView(decorView) ← 将 DecorView 添加到 WMS


Choreographer 触发 VSync → 测量/布局/绘制 → SurfaceFlinger 合成 → 显示

关键关联点:

  • Instrumentation 是 Activity 启动的"指挥棒"
  • ApplicationThread 是应用进程与 system_server 通信的 Binder 代理
  • ActivityThread 是应用进程的主线程入口
  • PhoneWindow 在 Activity.attach() 中被创建

五、Activity 与 Window / View 的关系

5.1 三者关系

Activity
├── PhoneWindow(Window 的实现类)
│ ├── DecorView(FrameLayout,根 View)
│ │ ├── TitleBar(系统标题栏,可隐藏)
│ │ └── ContentParent(FrameLayout,id = android.R.id.content)
│ │ └── 你的布局(通过 setContentView 添加)
│ │
│ └── WindowManager(管理窗口的添加、更新、移除)

└── Window.Callback(接收窗口事件,如 dispatchTouchEvent)

5.2 setContentView() 到底做了什么

// Activity.setContentView()
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID); // 委托给 PhoneWindow
initWindowDecorActionBar();
}

// PhoneWindow.setContentView()
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 1. 创建 DecorView + ContentParent
}
// 2. 将用户布局 inflate 到 ContentParent 中
mLayoutInflater.inflate(layoutResID, mContentParent);
}

流程拆解:

  • installDecor():若 DecorView 不存在,创建它(加载系统布局 screen_simple.xml 或 screen_title.xml)
  • 找到 ContentParent(id 为 R.id.content 的 FrameLayout)
  • 将用户的布局文件 inflate 到 ContentParent 中
  • 5.3 从 onCreate 到显示在屏幕上

    onCreate() 中调用 setContentView()


    布局被 inflate 到 ContentParent,但尚未测量/布局


    onResume() 执行完毕


    Activity.makeVisible() 被调用


    mDecor.setVisibility(View.VISIBLE)


    WindowManager.addView(mDecor, params) ← 第一次真正将 DecorView 添加到 WMS


    ViewRootImpl 被创建,执行 requestLayout()


    performTraversals():measure → layout → draw


    Surface 锁定 Canvas → Skia/OpenGL 绘制 → SurfaceFlinger 合成 → 屏幕显示

    关键:setContentView() 只是将布局加载到内存中的 View 树,真正的测量、布局、绘制发生在 onResume() 之后、Window 首次变为可见时。

    六、数据传递与返回

    6.1 显式启动

    val intent = Intent(this, TargetActivity::class.java)
    intent.putExtra("key", "value")
    startActivity(intent)

    6.2 隐式启动

    通过 Action、Category、Data 匹配:

    <activity android:name=".ShareActivity">
    <intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
    </intent-filter>
    </activity>

    val intent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "分享内容")
    }
    startActivity(intent)

    6.3 带返回值的启动

    // A 启动 B
    val intent = Intent(this, BActivity::class.java)
    startActivityForResult(intent, REQUEST_CODE) // 已废弃,推荐使用 Activity Result API

    // B 返回数据
    val resultIntent = Intent().apply {
    putExtra("result", "data")
    }
    setResult(Activity.RESULT_OK, resultIntent)
    finish()

    // A 接收返回
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
    val result = data?.getStringExtra("result")
    }
    }

    现代推荐方式(Activity Result API):

    private val launcher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
    ) { result ->
    if (result.resultCode == RESULT_OK) {
    val data = result.data?.getStringExtra("result")
    }
    }

    // 使用
    launcher.launch(Intent(this, BActivity::class.java))

    七、配置变更与状态保存

    7.1 配置变更类型

    • 屏幕旋转(orientation)
    • 键盘可用性变化(keyboardHidden)
    • 屏幕尺寸变化(screenSize)
    • 夜间模式切换(uiMode)
    • 语言切换(locale)

    默认情况下,配置变更会导致 Activity 销毁并重建(走完整的 onDestroy() → onCreate() 流程)。

    7.2 状态保存与恢复

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("edit_text", editText.getText().toString());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
    String text = savedInstanceState.getString("edit_text");
    editText.setText(text);
    }
    }

    保存时机:onSaveInstanceState() 在 onStop() 之前调用(API 28+ 在 onStop() 之后),用于保存瞬时 UI 状态。
    恢复时机:onCreate(Bundle) 或 onRestoreInstanceState(Bundle)。

    7.3 自行处理配置变更

    若不想重建 Activity,可在 Manifest 中声明:

    <activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden" />

    此时配置变更时,系统不重建 Activity,而是调用:

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    // 手动处理横屏布局
    }
    }

    八、Activity 的注册要求

    Activity必须在AndroidManifest.xml中声明,否则启动时会抛出ActivityNotFoundException。

    <manifest >
    <application >
    <!– 入口 Activity –>
    <activity android:name=".MainActivity"
    android:exported="true">

    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>

    <!– 普通 Activity –>
    <activity android:name=".SecondActivity" />
    </application>
    </manifest>

    • MAIN + LAUNCHER:标识应用的入口 Activity,桌面图标点击后启动
    • exported=“true”(Android 12+ 强制要求):允许外部应用启动该 Activity

    九、总结

    维度核心要点
    本质 用户交互的窗口容器,管理 View 树和生命周期
    生命周期 7 个核心回调,掌握状态转换和时序关系
    启动模式 4 种模式控制实例复用和栈行为,taskAffinity 控制 Task 归属
    启动流程 Instrumentation → ATMS → ActivityStarter → ApplicationThread → ActivityThread
    UI 链路 setContentView() → PhoneWindow → DecorView → WindowManager → ViewRootImpl → 屏幕
    数据传递 Intent + Bundle,显式/隐式启动,Activity Result API
    配置变更 默认重建,可通过 configChanges 自行处理或配合 onSaveInstanceState()
    赞(0)
    未经允许不得转载:171主机测评 » Android随笔-Activity
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址