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()、初始化变量、恢复savedInstanceState1 次onStart()Activity 即将进入前台可见状态注册广播、启动动画多次onResume()Activity 进入前台可接收用户输入获取焦点、启动相机/定位等需要前台权限的操作多次onPause()Activity 失去焦点被部分覆盖或弹窗保存关键数据、停止动画、释放相机多次onStop()Activity 完全不可见注销广播、释放资源、停止定位多次onRestart()从onStop状态回到前台时刷新数据多次onDestroy()Activity 被销毁前释放所有资源、取消网络请求、解绑服务1 次2.3 关键状态定义Active/RunningonResume() 后位于前台可与用户交互PausedonPause() 后失去焦点但仍部分可见如被透明/对话框 Activity 覆盖StoppedonStop() 后完全不可见进入后台Killed/DestroyedonDestroy() 后等待 GC 回收2.4 生命周期回调顺序典型场景场景A 的生命周期B 的生命周期A 启动 BonPause()→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 启动 BsingleTask后 结果栈A → B C、D 被清除3.2.2 singleTop 的 onNewIntent()当栈顶复用时Activity 不会被重建系统通过 onNewIntent(Intent) 传递新的 IntentOverrideprotectedvoidonNewIntent(Intentintent){super.onNewIntent(intent);setIntent(intent);// 重要更新 Intent否则 getIntent() 仍是旧的// 处理新数据}3.3 taskAffinity任务亲和性taskAffinity 定义了 Activity 倾向于归属哪个 Task默认值为应用包名。单独设置 taskAffinity 不会生效必须配合以下条件之一条件效果launchModesingleTaskActivity 启动时若不存在对应 affinity 的 Task则新建 TasklaunchModesingleInstance强制新建独立 TaskIntent.FLAG_ACTIVITY_NEW_TASK配合不同 taskAffinity 开启新 TaskallowTaskReparentingtrueActivity 可从启动它的 Task 迁移到 affinity 匹配的 Taskactivityandroid:name.MainActivityandroid:launchModesingleTaskandroid:taskAffinitycom.example.main/3.4 Intent Flag动态控制Intent Flag 的优先级高于 Manifest 中的静态配置Flag作用FLAG_ACTIVITY_NEW_TASK在新 Task 中启动通常配合taskAffinityFLAG_ACTIVITY_CLEAR_TOP若目标 Activity 存在清除其上所有 Activity 并复用目标FLAG_ACTIVITY_SINGLE_TOP同singleTop效果FLAG_ACTIVITY_CLEAR_TASK清除整个 Task目标 Activity 作为新根FLAG_ACTIVITY_NO_HISTORY启动后不保留在栈中典型用法退出登录后跳转登录页valintentIntent(this,LoginActivity::class.java).apply{flagsIntent.FLAG_ACTIVITY_CLEAR_TASKorIntent.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 ├── PhoneWindowWindow 的实现类 │ ├── DecorViewFrameLayout根 View │ │ ├── TitleBar系统标题栏可隐藏 │ │ └── ContentParentFrameLayoutid android.R.id.content │ │ └── 你的布局通过 setContentView 添加 │ │ │ └── WindowManager管理窗口的添加、更新、移除 │ └── Window.Callback接收窗口事件如 dispatchTouchEvent5.2 setContentView() 到底做了什么// Activity.setContentView()publicvoidsetContentView(LayoutResintlayoutResID){getWindow().setContentView(layoutResID);// 委托给 PhoneWindowinitWindowDecorActionBar();}// PhoneWindow.setContentView()publicvoidsetContentView(intlayoutResID){if(mContentParentnull){installDecor();// 1. 创建 DecorView ContentParent}// 2. 将用户布局 inflate 到 ContentParent 中mLayoutInflater.inflate(layoutResID,mContentParent);}流程拆解installDecor()若 DecorView 不存在创建它加载系统布局 screen_simple.xml 或 screen_title.xml找到 ContentParentid 为 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 显式启动valintentIntent(this,TargetActivity::class.java)intent.putExtra(key,value)startActivity(intent)6.2 隐式启动通过 Action、Category、Data 匹配activityandroid:name.ShareActivityintent-filteractionandroid:nameandroid.intent.action.SEND/categoryandroid:nameandroid.intent.category.DEFAULT/dataandroid:mimeTypetext/plain//intent-filter/activityvalintentIntent(Intent.ACTION_SEND).apply{typetext/plainputExtra(Intent.EXTRA_TEXT,分享内容)}startActivity(intent)6.3 带返回值的启动// A 启动 BvalintentIntent(this,BActivity::class.java)startActivityForResult(intent,REQUEST_CODE)// 已废弃推荐使用 Activity Result API// B 返回数据valresultIntentIntent().apply{putExtra(result,data)}setResult(Activity.RESULT_OK,resultIntent)finish()// A 接收返回overridefunonActivityResult(requestCode:Int,resultCode:Int,data:Intent?){if(requestCodeREQUEST_CODEresultCodeRESULT_OK){valresultdata?.getStringExtra(result)}}现代推荐方式Activity Result APIprivatevallauncherregisterForActivityResult(ActivityResultContracts.StartActivityForResult()){result-if(result.resultCodeRESULT_OK){valdataresult.data?.getStringExtra(result)}}// 使用launcher.launch(Intent(this,BActivity::class.java))七、配置变更与状态保存7.1 配置变更类型屏幕旋转orientation键盘可用性变化keyboardHidden屏幕尺寸变化screenSize夜间模式切换uiMode语言切换locale默认情况下配置变更会导致Activity 销毁并重建走完整的 onDestroy() → onCreate() 流程。7.2 状态保存与恢复OverrideprotectedvoidonSaveInstanceState(NonNullBundleoutState){super.onSaveInstanceState(outState);outState.putString(edit_text,editText.getText().toString());}OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);if(savedInstanceState!null){StringtextsavedInstanceState.getString(edit_text);editText.setText(text);}}保存时机onSaveInstanceState() 在 onStop() 之前调用API 28 在 onStop() 之后用于保存瞬时 UI 状态。恢复时机onCreate(Bundle) 或 onRestoreInstanceState(Bundle)。7.3 自行处理配置变更若不想重建 Activity可在 Manifest 中声明activityandroid:name.MainActivityandroid:configChangesorientation|screenSize|keyboardHidden/此时配置变更时系统不重建Activity而是调用OverridepublicvoidonConfigurationChanged(ConfigurationnewConfig){super.onConfigurationChanged(newConfig);if(newConfig.orientationConfiguration.ORIENTATION_LANDSCAPE){// 手动处理横屏布局}}八、Activity 的注册要求Activity必须在AndroidManifest.xml中声明否则启动时会抛出ActivityNotFoundException。manifest...application...!-- 入口 Activity --activityandroid:name.MainActivityandroid:exportedtrueintent-filteractionandroid:nameandroid.intent.action.MAIN/categoryandroid:nameandroid.intent.category.LAUNCHER//intent-filter/activity!-- 普通 Activity --activityandroid:name.SecondActivity//application/manifestMAIN LAUNCHER标识应用的入口 Activity桌面图标点击后启动exported“true”Android 12 强制要求允许外部应用启动该 Activity九、总结维度核心要点本质用户交互的窗口容器管理 View 树和生命周期生命周期7 个核心回调掌握状态转换和时序关系启动模式4 种模式控制实例复用和栈行为taskAffinity控制 Task 归属启动流程Instrumentation→ATMS→ActivityStarter→ApplicationThread→ActivityThreadUI 链路setContentView()→PhoneWindow→DecorView→WindowManager→ViewRootImpl→ 屏幕数据传递IntentBundle显式/隐式启动Activity Result API配置变更默认重建可通过configChanges自行处理或配合onSaveInstanceState()

相关新闻

使用Taotoken为Claude Code配置稳定API通道避免封号风险

使用Taotoken为Claude Code配置稳定API通道避免封号风险

使用Taotoken为Claude Code配置稳定API通道避免封号风险 对于频繁使用Claude Code进行编程辅助的开发者而言,直接依赖单一供应商的API服务常面临两个现实挑战:一是官方配额有限,在密集开发场景下容易快速耗尽;二是连接稳定性受网…

2026/10/15 4:03:57 阅读更多 →
OpenAI API实战:流式输出与对话管理技巧

OpenAI API实战:流式输出与对话管理技巧

1. 项目概述:大模型应用开发实战精要这个系列教程聚焦于当前AI领域最前沿的两个技术方向——RAG(检索增强生成)和Agent智能体开发,通过LangChain框架和OpenAI接口的实战演示,帮助开发者快速掌握企业级AI应用构建能力。…

2026/10/7 9:31:45 阅读更多 →
从RNN到BiLSTM:序列建模核心技术解析

从RNN到BiLSTM:序列建模核心技术解析

1. 序列建模的进化之路:从基础RNN到双向LSTM在自然语言处理和时间序列分析领域,序列建模技术经历了三次关键的技术跃迁。2012年我在处理股票价格预测时首次接触RNN,当时被其处理时序数据的能力震撼;2015年使用LSTM完成首个真正可用…

2026/10/9 15:09:11 阅读更多 →

最新新闻

Map 中删除一个 key,它的内存会释放么?

Map 中删除一个 key,它的内存会释放么?

在 Go 语言开发中,我们频繁使用内置函数 delete(m, key) 来删除哈希表中的元素。许多开发者直觉上会认为:既然删除了 key,对应的内存就应该被操作系统或 Go 垃圾回收器(GC)回收。然而事实并非如此:调用 del…

2026/10/15 4:03:46 阅读更多 →
编程猫、核桃、小码王、童程童美、学而思,到底哪家好?一次说清!

编程猫、核桃、小码王、童程童美、学而思,到底哪家好?一次说清!

编程猫、核桃编程、小码王、童程童美、学而思编程,这五个名字几乎覆盖了当下家长圈里讨论度较高的少儿编程选择。但机构多不等于好选,每家适配的年龄段、上课形式和培养目标都不一样。与其反复纠结,不如把它们的课程链路、师资配置和赛考出口摊开来看。本文从六大维度出发,逐家…

2026/10/15 4:03:46 阅读更多 →
技术路线要让评审一眼看懂逻辑:把开题报告的步骤串成可自证的推进关系

技术路线要让评审一眼看懂逻辑:把开题报告的步骤串成可自证的推进关系

技术路线被反复追问,大多不是图示不够漂亮,而是步骤之间缺少可追溯的因果关系。把每一步的产出约定为下一步的输入,再补上取舍依据与可行性论证,路线本身就能把逻辑讲清楚。下面按五个衔接层次拆解写法,并给出可直接套…

2026/10/15 4:03:46 阅读更多 →
收到秋招offer但还想等更好的,怎么办?

收到秋招offer但还想等更好的,怎么办?

其实很多同学拿到offer后,焦虑不会自动消失。因为会开始纠结: 这个 offer 要不要接?还有几家公司在流程中,要不要等?HR 催签,但自己还没想清楚。薪资还可以,但岗位不是最喜欢。…… 这种纠结不是…

2026/10/15 4:03:46 阅读更多 →
插件加载失败?拆解 did not activate 报错背后的排查与修复

插件加载失败?拆解 did not activate 报错背后的排查与修复

如果你在终端里看到类似这样的输出:failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p先别急着抄起键盘骂框架,这个报错我能看懂,它是插件加载器在启动阶段给你的一份“体检报告”。plugins这个词在工程化体系里…

2026/10/15 4:03:46 阅读更多 →
git 源代码里,出现一大堆红色文件的一次思考

git 源代码里,出现一大堆红色文件的一次思考

git 源代码里,出现一大堆红色文件 是因为切换分支的时候,底座导致的。 1.首先要理解rebase是什么意思? rebase是录制本地代码,把最新的代码作为背景,然后把最新修改加在最新代码后面。 2.如何解决呢? git c…

2026/10/15 4:02:46 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →