Android四大组件学习总结

1. Activity 启动模式问题

面试官​:
“我看你项目里用了 SingleTask 模式,能具体说说为什么用它吗?如果从 Activity A(SingleTask)跳转到 B(Standard),再返回 A,任务栈会怎么变化?”

候选人​:
技巧​:先明确问题,再分场景解释)
“好的。在我们的项目里,主页 Activity 被设置为 SingleTask 模式,主要是为了防止重复创建主页。比如用户从通知栏跳转到主页时,如果已经有主页实例在后台,就直接复用,而不是新建一个,这样可以避免用户按返回键时多次退出。

您提到的从 A 跳转到 B 再返回 A 的情况,假设 A 是 SingleTask,B 是 Standard:

  1. 当第一次启动 A 时,系统会为 A 创建一个独立的任务栈,假设叫 Task1。
  2. 从 A 跳转 B,因为 B 是 Standard 模式,B 会被压入 Task1 的栈顶。
  3. 当从 B 返回 A 时,由于 A 是 SingleTask,系统会先检查任务栈。发现 Task1 中已经存在 A 实例,于是会把 A 之上的所有 Activity(也就是 B)弹出栈,让 A 回到栈顶。这时候用户再按返回键,就直接退出应用了。”

加分话术​:
“这里有个实际踩过的坑:如果 A 的启动模式设置不当,可能会导致返回栈混乱。我们当时用 adb shell dumpsys activity 命令查看任务栈,验证逻辑是否符合预期。”


2. Service 保活与 ANR 问题

面试官​:
“Service 里做耗时操作为什么会 ANR?你们项目里怎么解决的?”

候选人​:
技巧​:承认问题 + 解决方案 + 实际案例)
“是的,Service 默认运行在主线程,如果直接在里面做网络请求或大量计算,肯定会阻塞主线程导致 ANR。我们在项目里是这样处理的:

  1. 异步处理​:比如用 IntentService,它内部自己开了工作线程,处理完自动停止。
  2. 结合 HandlerThread​:如果是长期运行的后台任务,会创建一个 HandlerThread,再通过 Handler 发送任务到子线程执行。
  3. 前台服务保活​:像音乐播放功能,我们用了前台服务,显示通知栏避免被系统回收。不过保活很难完全做到,Android 8.0 之后限制更多,我们最终改用 JobScheduler 在合适时机重启服务。”

加分话术​:
“其实现在更推荐用 WorkManager 处理后台任务,它能根据系统版本自动选择底层实现,比如用 JobScheduler 或 AlarmManager,这样兼容性更好。”

Service 扩展​

场景一:Service 混合启动模式

面试官​:
“假设我先用 startService() 启动了一个 Service,接着又用 bindService() 绑定它。这时候 Service 的生命周期会怎么走?销毁时要注意什么?”

候选人​:
自然思考状
“嗯,这个问题其实涉及到 Service 的两种启动方式混合使用的情况。我举个例子吧:比如我们做一个音乐播放器,先用 startService() 启动播放服务,保证音乐在后台持续播放;然后在 Activity 里调用 bindService() 绑定它,用来调节音量或者切歌。这时候 Service 的生命周期大概是这样的——”

  1. 第一步​:startService() 触发后,Service 会先走 onCreate(),然后 onStartCommand(),这时候服务就算正式启动了。
  2. 第二步​:再调用 bindService(),这时候不会重新创建 Service,而是直接调用 onBind(),返回一个 IBinder 对象给客户端。
  3. 销毁时​:得同时解绑和停止服务。比如用户退出 Activity 时调用 unbindService(),然后还得主动调用 stopService(),否则 Service 会一直活着,直到系统资源不足被干掉。”

加分话术​:
“在源码中,Service 的生命周期由 ActivityManagerService 管理。每次 startService() 会增加一个 ‘started’ 状态计数,而 bindService() 会增加一个 ‘bound’ 计数。只有当两个计数都归零时,才会调用 onDestroy()。我们可以通过 adb shell dumpsys activity services 查看当前 Service 的状态。” 

补充踩坑经验
“对了,我之前遇到过这种情况:只调了 unbindService() 没调 stopService(),结果发现通知栏的音乐控件还在,用户点了之后又恢复播放。后来用 adb shell dumpsys activity services 一查,发现 Service 的 ‘started’ 状态还没归零,这才恍然大悟。”


场景二:IntentService 的线程模型

面试官​:
“IntentService 是怎么在子线程处理任务的?如果我连续发 10 个 Intent,它们是排队一个一个执行,还是能并发?”

候选人​:
        这个问题我当初也好奇,还特意写代码验证过。IntentService 内部其实藏了个 HandlerThread,这个类特别有意思,它自己带了一个 Looper,专门用来在子线程处理消息队列。
        当您调用 startService() 发送 Intent 时,IntentService 会把每个 Intent 包装成一个 Message,扔进 HandlerThread 的消息队列里。这个队列是先进先出的,所以哪怕同时发 10 个 Intent,也得老老实实排队。比如第一个任务是下载文件,耗时 5 秒,后面 9 个任务都得等它完了才能执行。

举反例
“不过有一次,我手贱在 onHandleIntent() 里开了个新线程,结果任务全并行跑起来了,日志乱成一团。这才明白:IntentService 的串行特性全靠 HandlerThread 的消息队列,如果自己开线程反而会打破这个机制。”


场景三:Service 的 ANR 避坑

面试官​:
“听说在 Service 里直接做网络请求会 ANR?你们项目里是怎么处理的?”

候选人​:
“这事儿我们还真踩过坑!早期图省事,在 onStartCommand() 里直接写了个网络请求,结果线上 ANR 率飙升。后来复盘发现,Service 默认在主线程跑,一个慢请求就能卡死整个 APP。”

分步骤解释
“我们的解决方案分了三步走:

  1. 紧急修复​:先在 onStartCommand() 里手动 new Thread(),把请求扔到子线程。虽然土,但能快速止血。
  2. 中期优化​:换成 IntentService,但很快就发现它只能串行执行,下个版本需求要支持多文件同时上传,只好再改。
  3. 最终方案​:上线程池!用 Executors.newFixedThreadPool(3) 控制并发数,搭配 LiveData 把结果抛回主线程更新 UI。代码大概是这样的——”

模拟写代码

public class UploadService extends Service {private ExecutorService pool = Executors.newFixedThreadPool(3);@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {pool.execute(() -> {String url = intent.getStringExtra("url");boolean success = doUpload(url); // 模拟上传LiveDataBus.getInstance().post(new UploadEvent(success));});return START_NOT_STICKY;}
}

场景四:IntentService 的淘汰原因

面试官​:
“听说 IntentService 过时了?你们现在用什么替代?”

候选人​:
“确实,Android 8.0 之后 IntentService 有点力不从心了。比如我们之前用它做日志上报,结果在后台经常被系统干掉,查文档才发现:8.0 开始限制后台 Service,startService() 得配合前台通知才能用。”

对比分析
“后来我们全面转向 WorkManager,这玩意儿聪明在哪呢?它底层自动适配系统版本——在 Android 6.0 用 AlarmManager,7.0 用 JobScheduler,甚至还能在国产 ROM 上兼容。比如上传失败的任务会自动重试,还能设置网络条件,比 IntentService 手动重试省心多了。”

举例说明
“比如一个夜间日志上传的需求,用 WorkManager 可以这么搞:”

// 定义任务
class LogUploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {override fun doWork(): Result {return if (uploadLogs()) Result.success() else Result.retry()}
}// 设置约束:充电状态 + 夜间时段
val constraints = Constraints.Builder().setRequiresCharging(true).build()val request = OneTimeWorkRequestBuilder<LogUploadWorker>().setConstraints(constraints).setInitialDelay(6, TimeUnit.HOURS) // 延迟到凌晨.build()WorkManager.getInstance(context).enqueue(request)

“现在除非兼容老系统,否则新项目基本不用 IntentService 了。不过它的设计思想——子线程干活、干完自毁,在 WorkManager 的 Worker 里还能看到影子。”


3. BroadcastReceiver 使用场景

面试官​:
“你提到用广播实现登录状态同步,能具体说说吗?静态注册和动态注册怎么选?”

候选人​:
技巧​:场景化描述 + 安全提醒)
“比如用户登录成功后,我们需要更新多个页面的 UI。这时候发送一个自定义广播,各个页面注册 Receiver 监听。登录页发送广播后,主页、个人中心页收到通知,主动刷新数据。

关于注册方式的选择:

  • 静态注册​:适合监听系统广播,比如开机启动。但要注意 Android 8.0 之后对静态广播的限制。
  • 动态注册​:灵活且优先级高,但要在 Activity 或 Fragment 的 onResume 注册,onPause 注销,防止内存泄漏。

不过现在更推荐用 LiveData 或 EventBus 替代广播,减少系统开销。只有跨进程通信时才会用 ContentProvider 或广播。”

加分话术​:
“我们之前遇到过一个坑:动态注册的 Receiver 没及时注销,导致 Activity 泄漏。后来用 LeakCanary 检测出来,加了个 try-catch 确保 unregisterReceiver 一定执行。”


4. ContentProvider 安全与多进程

面试官​:
“如果想让其他应用访问我们的数据,用 ContentProvider 怎么保证安全?”

候选人​:
技巧​:分层回答 + 权限细节)
“我们分了三层防护:

  1. 权限声明​:在 AndroidManifest.xml 里定义读写权限,比如 com.example.READ_DATA,并设置 protectionLevel="signature",只允许相同签名的应用访问。
  2. Uri 权限控制​:在 Provider 的 query 方法里校验调用方的包名,通过 Binder.getCallingUid() 获取 UID,对比白名单。
  3. 数据加密​:敏感数据(如用户手机号)在存储时用 AES 加密,即使被恶意读取也无法解密。”

加分话术​:
“其实 Android 的 FileProvider 也是类似的思路,通过 grantUriPermission 动态授权临时权限,避免长期暴露数据。”


基础知识讲解

Activity 启动模式(LaunchMode)及场景
  • Standard​:默认模式,每次启动创建新实例(可能产生重复登录页面的问题)。
  • SingleTop​:栈顶复用,若已在栈顶则不创建新实例(适用于通知跳转页)。
  • SingleTask​:栈内复用,在任务栈中只存在一个实例(主页面常用)。
  • SingleInstance​:独立任务栈,全局唯一(如系统来电页面)。

真题​:
Q​:从 Activity A(SingleTask)跳转到 B(Standard),再从 B 跳转回 A,描述任务栈变化。
A​:A 启动时创建新任务栈,B 在默认栈中。当 B 跳转回 A 时,由于 A 的 SingleTask 特性,系统会清空 A 所在栈顶之上的所有 Activity,直接复用 A 实例。


Q1:Activity A 跳转 B,B 跳转 C,按返回键时的生命周期回调顺序? (假设 A, B, C 都是 Standard 模式)

A: (这是一个经典问题,关键在于理解 Activity 栈和生命周期)

  1. 当前状态: 栈顶是 C,其下是 B,再下是 A。 [A, B, C]

  2. 在 C 按返回键:

    • C: onPause()
    • B: onRestart() (如果 B 之前 onStop() 了) -> onStart() -> onResume() (B 变为可见并获得焦点)
    • C: onStop() -> onDestroy() (C 被销毁并出栈)
    • 此时栈: [A, B]
  3. 在 B 按返回键:

    • B: onPause()
    • A: onRestart() (如果 A 之前 onStop() 了) -> onStart() -> onResume()
    • B: onStop() -> onDestroy()
    • 此时栈: [A]

Activity 与 Fragment 通信方式
  • Bundle + setArguments​:传递初始参数。
  • 接口回调​:Fragment 定义接口,Activity 实现。
  • ViewModel​:通过共享 ViewModel 实现数据监听。
  • EventBus​:事件总线解耦通信(需注意内存泄漏)。

真题​:
Q​:Fragment 如何回传数据给 Activity?
A​:在 Fragment 中定义接口,在 onAttach() 中绑定 Activity 实例,调用接口方法传值。代码示例:

// Fragment 中
public interface OnDataCallback {void onDataReceived(String data);
}@Override
public void onAttach(Context context) {super.onAttach(context);if (context instanceof OnDataCallback) {callback = (OnDataCallback) context;}
}// 传值时调用
callback.onDataReceived("data");

Q1​:Activity A 跳转 B,B 跳转 C,按返回键的生命周期回调顺序?
A​:

  • C → onPause() → B → onRestart() → onStart() → onResume() → C → onStop() → onDestroy()

Q2​:Service 中执行耗时操作为何可能引发 ANR?如何解决?
A​:Service 默认运行在主线程,直接执行耗时操作会阻塞 UI 线程。应使用 IntentService 或 HandlerThread 在子线程处理。

Q3​:BroadcastReceiver 的 onReceive() 中能否启动弹窗?
A​:可以,但需通过 Intent 跳转 Activity 并添加 FLAG_ACTIVITY_NEW_TASK(因为 Receiver 无任务栈)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/82184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SamOutV8的序列生成模型实现与分析

项目概述 本项目实现了基于SamOutV8架构的序列生成模型&#xff0c;核心组件包括MaxStateSuper、FeedForward和DecoderLayer等模块。通过结合自注意力机制与状态编码策略&#xff0c;该模型在处理长序列时表现出良好的性能。 核心组件解析 1. MaxStateSuper&#xff08;状态编…

从脑电图和大脑记录中学习稳健的深度视觉表征

从脑电图和大脑记录中学习稳健的深度视觉表征 印度&#xff0c;印度&#xff0c;印度&#xff0c;印度大脑实验室&#xff0c;印度 例如&#xff0c;达拉普&#xff0c;克普拉萨德&#xff0c;山&#xff0c;山&#xff0c;新的。ac .在 摘要 解码人类大脑一直是新机器人科学家…

2025.5个人感悟

本人是一名2025级大四学生&#xff0c;离毕业就一个月了&#xff0c;目前论文终稿已写完&#xff0c;有多的时间可以来写一写博客了。 &#xff08;1&#xff09;越焦虑什么&#xff0c;未来就有可能变成什么样子。以前一直焦虑考不上研&#xff0c;秋招找不到工作&#xff0c…

使用腾讯云3台轻量云服务器快速部署K8s集群实战

一、服务器配置 1.集群数量 节点ip备注master10.0.4.9安全组放通&#xff0c;3节点内网互通node110.0.4.14安全组放通&#xff0c;3节点内网互通node210.0.4.17安全组放通&#xff0c;3节点内网互通 2.配置服务器&#xff08;每个节点执行&#xff09; 执行步骤1 #在对应的…

bitbar环境搭建(ruby 2.4 + rails 5.0.2)

此博客为武汉大学WA学院网络安全课程&#xff0c;理论课大作业Web环境搭建。 博主搭了2天&#xff01;&#xff01;&#xff01;血泪教训是还是不能太相信ppt上的教程。 一开始尝试了ppt上的教程&#xff0c;然后又转而寻找网络资源 cs155源代码和docker配置&#xff0c;做到…

leetcode:2469. 温度转换(python3解法,数学相关算法题)

难度&#xff1a;简单 给你一个四舍五入到两位小数的非负浮点数 celsius 来表示温度&#xff0c;以 摄氏度&#xff08;Celsius&#xff09;为单位。 你需要将摄氏度转换为 开氏度&#xff08;Kelvin&#xff09;和 华氏度&#xff08;Fahrenheit&#xff09;&#xff0c;并以数…

python 实现一个完整的基于Python的多视角三维重建系统,包含特征提取与匹配、相机位姿估计、三维重建、优化和可视化等功能

多视角三维重建系统 下面我将实现一个完整的基于Python的多视角三维重建系统,包含特征提取与匹配、相机位姿估计、三维重建、优化和可视化等功能。 1. 环境准备与数据加载 首先安装必要的库: pip install opencv-python opencv-contrib-python numpy matplotlib plotly s…

什么是国密、密评、商密

一、国密 定义与本质&#xff1a;国密即国家密码管理局公布认定的国产密码算法&#xff0c;也称为商用密码&#xff08;在此语境下与国密通用&#xff09;&#xff0c;指能够实现商用密码算法的加密、解密和认证等功能的技术&#xff0c;涵盖密码算法编程技术和密码算法芯片、…

打卡35天

模型可视化与推理 知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化 进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观 推理的写法&#xff1a;评估模式 作业&#xff1a;调整模型定义时的超参数&…

kafka之操作示例

一、常用shell命令 #1、创建topic bin/kafka-topics.sh --create --zookeeper localhost:2181 --replications 1 --topic test#2、查看创建的topic bin/kafka-topics.sh --list --zookeeper localhost:2181#3、生产者发布消息命令 &#xff08;执行完此命令后在控制台输入要发…

网络安全基础--第七课

路由表 路由器的转发原理&#xff1a;当一个数据包进入路由器&#xff0c;路由器将基于数据包中的目标IP地址&#xff0c;查询本地 路由表&#xff0c;若表中存在记录&#xff0c;则将无条件按记录转发&#xff0c;若没有记录&#xff0c;路由器不能泛洪&#xff0c;因为路由器…

Java SpringBoot 扣子CozeAI SseEmitter流式对话完整实战 打字机效果

书接上回&#xff1a;springBoot 整合 扣子cozeAI 智能体 对话https://blog.csdn.net/weixin_44548582/article/details/147457236 上文实现的是一次性等待并得到完整的AI回复内容&#xff0c;但随着问题和AI的逻辑日趋复杂&#xff0c;会明显增加这个等待时间&#xff0c;这对…

《AVL树完全解析:平衡之道与C++实现》

目录 AVL树的核心概念数据结构与节点定义插入操作与平衡因子更新旋转操作&#xff1a;从理论到代码双旋场景深度剖析平衡检测与测试策略性能分析与工程实践总结 0.前置知识&#xff1a;BS树 代码实现部分对和BS树相似的部分会省略。 1. AVL树的核心概念 1.1 平衡二叉搜索树…

跨平台游戏引擎 Axmol-2.6.0 发布

Axmol 2.6.0 版本是一个以错误修复和功能改进为主的次要LTS长期支持版本 &#x1f64f;感谢所有贡献者及财务赞助者&#xff1a;scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox、DelinWorks 相对于2.5.0版本的重要变更&#xff1a; 通…

【Django Serializer】一篇文章详解 Django 序列化器

第一章 Django 序列化器概述 1.1 序列化器的定义 1.1.1 序列化与反序列化的概念 1. 序列化 想象你有一个装满各种物品&#xff08;数据对象&#xff09;的大箱子&#xff08;数据库&#xff09;&#xff0c;但是你要把这些物品通过一个狭窄的管道&#xff08;网络&#xff…

关于spring @Bean里调用其他产生bean的方法

背景 常常见到如下代码 Bean public TestBean testBean() {TestBean t new TestBean();System.out.println("testBean:" t);return t; }Bean public FooBean fooBean() {TestBean t testBean();System.out.println("这里看似是自己new的&#xff0c;但因为…

Level1.7列表

1.7_1列表&#xff08;索引切片&#xff09; #1.列表 students[Bob,Alice,Jim,Mike,Judy] print(students)#2.在列表&#xff08;添加不同数据类型&#xff0c;查看列表是否可以运行&#xff1f;是否为列表类型&#xff1f;&#xff09; students[Bob,Alice,Jim,Mike,Judy,123…

Python爬虫实战:研究Cola框架相关技术

一、Cola 框架概述 Cola 是一款基于 Python 的异步爬虫框架,专为高效抓取和处理大规模数据设计。它结合了 Scrapy 的强大功能和 asyncio 的异步性能优势,特别适合需要高并发处理的爬虫任务。 1.1 核心特性 异步 IO 支持:基于 asyncio 实现非阻塞 IO,大幅提高并发性能模块…

vue2中el-table 实现前端分页

一些接口不分页的数据列表&#xff0c;一次性返回大量数据会导致前端渲染卡顿&#xff0c;接口不做分页的情况下前端可以截取数据来做分页 以下是一个例子&#xff0c;被截取的列表和全量数据在同一个栈内存空间&#xff0c;所以如果有表格内的表单编辑&#xff0c;新的值也会事…

Python + moviepy:根据图片或数据高效生成视频全流程详解

前言 在数据可视化、自媒体内容生产、学术汇报等领域,我们常常需要将一组图片或一段变动的数据,自动合成为视频文件。这样不仅能提升内容表现力,也极大节省了人工操作时间。Python作为数据处理和自动化领域的王者,其`moviepy`库为我们提供了灵活高效的视频生成方案。本文将…