1. 四大组件
Activity, Service, ContentProvider, BroadcastReceiver.
2. Activity生命周期
一、生命周期方法(7个核心回调)
- onCreate()
- 首次创建 Activity 时调用,用于初始化布局(如
setContentView()
)和数据绑定。
- 首次创建 Activity 时调用,用于初始化布局(如
- onStart()
- Activity 可见但未进入前台,用户无法交互(如后台加载资源)。
- onResume()
- Activity 进入前台并获得焦点,用户可进行交互(如点击按钮)。
- onPause()
- Activity 失去焦点但部分可见(如弹窗覆盖),需保存临时数据或释放资源。
- onStop()
- Activity 完全不可见(如被其他界面覆盖),可能被系统回收内存。
- onDestroy()
- Activity 被销毁前调用,释放长期占用的资源24。
- onRestart()
- Activity 从停止状态重新回到可见前调用(如用户返回应用)。
二、生命周期流程
- 完整启动流程:
onCreate → onStart → onResume
- 从后台返回前台:
onRestart → onStart → onResume
- 退出或销毁流程:
onPause → onStop → onDestroy
- 特殊情况:若快速返回(如短暂覆盖),可能跳过
onStop
直接执行onPause → onResume
。
三、状态与生命周期关联
- 运行状态(Active/Running)
- Activity 处于栈顶,可见且可交互(对应
onResume
阶段)。
- Activity 处于栈顶,可见且可交互(对应
- 暂停状态(Paused)
- 部分可见但无焦点(如弹窗存在时),可能被系统回收(对应
onPause
阶段)。
- 部分可见但无焦点(如弹窗存在时),可能被系统回收(对应
- 停止状态(Stopped)
- 完全不可见,保留成员信息但可能被系统终止(对应
onStop
阶段)。
- 完全不可见,保留成员信息但可能被系统终止(对应
- 销毁状态(Destroyed)
- Activity 被销毁或未启动,资源完全释放(对应
onDestroy
阶段)。
- Activity 被销毁或未启动,资源完全释放(对应
四、开发注意事项
- 数据保存:在
onPause()
中保存临时数据,避免因内存回收丢失。 - 资源释放:在
onStop()
或onDestroy()
中释放数据库连接等长期占用资源。 - 避免耗时操作:
onPause()
和onStop()
中不宜执行耗时任务,否则影响用户体验或导致 ANR。
3. ANR
ANR(Application Not Responding)解析
一、定义
ANR即应用程序无响应,是Android系统中检测到主线程被阻塞超过特定时间后触发的错误机制。此时系统会向用户弹窗提示,用户可选择等待或强制关闭应用。
二、触发条件
当主线程未在规定时间内完成以下操作时触发:
- Activity:5秒内未处理输入事件(如点击);
- BroadcastReceiver:前台广播10秒、后台广播60秒未完成处理;
- Service:前台服务20秒、后台服务200秒未完成生命周期回调(如
onStart
); - ContentProvider:10秒内未执行完操作。
三、根本原因
主线程被耗时操作阻塞,例如:
- 复杂的UI布局计算或大数据处理;
- 同步I/O操作(如文件读写、数据库查询);
- 网络请求未异步执行。
四、影响与重要性
- 用户体验:频繁ANR会导致用户流失;
- 系统机制:ANR是Android保护应用响应性的核心机制,通过强制弹窗避免长期卡顿;
- 性能指标:ANR率是衡量应用流畅度的重要指标。
五、典型场景示例
- 主线程中直接执行网络请求且等待响应;
- 未使用异步任务处理大型图片加载或数据解析;
- 在
BroadcastReceiver.onReceive()
中执行耗时逻辑且未异步化。
4. Activity启动模式
Activity 启动模式详解
一、核心模式分类与行为
-
Standard(标准模式)
- 行为:默认模式,每次启动均创建新实例并压入任务栈,允许同一 Activity 存在多个实例。
- 生命周期:完整执行
onCreate → onStart → onResume
。 - 应用场景:需独立展示新内容的页面(如新闻详情页)。
-
SingleTop(栈顶复用模式)
- 行为:若目标 Activity 已处于栈顶,直接复用实例并回调
onNewIntent()
,否则创建新实例。 - 生命周期:复用时不触发
onCreate
和onStart
,仅调用onNewIntent → onResume
。 - 应用场景:避免重复打开同一页面(如通知跳转页)。
- 行为:若目标 Activity 已处于栈顶,直接复用实例并回调
-
SingleTask(栈内复用模式)
- 行为:若任务栈中存在目标 Activity 实例,清除其上方所有 Activity 使其位于栈顶,否则创建新实例。
- 生命周期:复用实例时调用
onNewIntent → onResume
,原实例以上的 Activity 被销毁。 - 应用场景:应用主界面(确保全局唯一入口)或需全局状态管理的页面。
-
SingleInstance(单实例模式)
- 行为:目标 Activity 独占一个独立任务栈,其他应用启动该 Activity 时复用该实例。
- 生命周期:首次启动时完整生命周期,后续复用触发
onNewIntent → onResume
。 - 应用场景:全局唯一性需求(如来电界面)或跨应用共享的页面。
二、关键特性对比
启动模式 | 任务栈规则 | 实例复用条件 | 典型场景 |
---|---|---|---|
Standard | 允许同一栈中多个实例 | 无复用,每次新建 | 动态生成新内容的页面(如新闻详情) |
SingleTop | 仅复用栈顶实例 | 目标 Activity 位于栈顶 | 防止重复打开同一页面(如通知跳转) |
SingleTask | 清除栈内上方 Activity 复用实例 | 目标 Activity 存在于栈中 | 主界面或核心功能页(如首页) |
SingleInstance | 独占独立任务栈 | 全局唯一实例 | 跨应用共享或独立进程页面(如来电界面) |
三、配置方式
-
AndroidManifest 声明:
<--通过 android:launchMode 属性指定--> <activity android:name=".MainActivity" android:launchMode="singleTask" />
-
Intent Flags 动态设置:
//优先级高于Manifest声明 Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
四、开发注意事项
- 避免滥用 SingleTask:随意使用可能导致任务栈混乱(如误清用户操作历史)。
- 正确处理 onNewIntent():在复用实例时需在此方法中更新数据和界面。
- 多任务栈管理:SingleInstance 模式需注意跨栈跳转时的返回逻辑。
通过合理选择启动模式,可优化任务栈管理、提升用户体验,并减少资源消耗。
5. 安卓线程间通信方式
一、主线程与子线程通信
-
Handler 机制
- 原理:通过消息队列实现异步通信,子线程发送
Message
或Runnable
对象到主线程的Handler
,主线程通过Looper
循环处理消息。 - 实现:
// 主线程创建 Handler Handler handler = new Handler(Looper.getMainLooper()); // 子线程发送消息 new Thread(() -> { Message msg = handler.obtainMessage(); msg.obj = "更新UI"; handler.sendMessage(msg); }).start();
- 原理:通过消息队列实现异步通信,子线程发送
-
AsyncTask
- 原理:封装后台任务与UI更新逻辑,通过
doInBackground()
执行耗时操作,onPostExecute()
在主线程更新UI。 - 注意:已过时,建议结合协程或
ExecutorService
替代。
- 原理:封装后台任务与UI更新逻辑,通过
-
runOnUiThread
- 原理:在
Activity
中直接切换至主线程执行代码,适用于简单UI操作。 - 实现:
new Thread(() -> { runOnUiThread(() -> textView.setText("结果")); }).start();
- 原理:在
-
View.post()
- 原理:通过
View
对象直接提交Runnable
到主线程消息队列。 - 示例:
imageView.post(() -> imageView.setImageBitmap(bitmap));
- 原理:通过
二、子线程间通信
-
同步工具
- CountDownLatch/Semaphore:通过计数器或信号量协调多个子线程的执行顺序。
CountDownLatch latch = new CountDownLatch(2); new Thread(() -> { task1(); latch.countDown(); }).start(); new Thread(() -> { task2(); latch.countDown(); }).start(); latch.await(); // 等待所有子线程完成
- CountDownLatch/Semaphore:通过计数器或信号量协调多个子线程的执行顺序。
-
共享内存+锁机制
- 原理:使用
synchronized
或ReentrantLock
保护共享资源,避免竞态条件。 - 风险:需谨慎处理死锁和性能问题。
- 原理:使用
三、其他通信方式
-
BroadcastReceiver
- 原理:通过发送广播实现跨线程/组件通信,需注册接收器并定义
IntentFilter
。 - 场景:适用于全局事件通知(如网络状态变化)。
- 原理:通过发送广播实现跨线程/组件通信,需注册接收器并定义
-
EventBus
- 原理:第三方库通过发布-订阅模式实现解耦通信,支持线程切换。
- 示例:
EventBus.getDefault().post(new MessageEvent("数据"));
四、核心注意事项
- 避免主线程阻塞:所有耗时操作(网络请求、文件IO)必须放在子线程。
- 内存泄漏:
Handler
需使用弱引用或静态内部类,防止持有Activity
引用。 - 线程安全:多线程访问共享数据时需同步(如
volatile
、Atomic
类)。
通过合理选择通信方式,可提升应用响应速度并降低ANR风险。
6. 安卓进程间通信方式
安卓进程间通信(IPC)方式详解
一、常用通信方式
-
Bundle + Intent
- 原理:通过
Intent
携带Bundle
对象传递数据,支持Parcelable
或Serializable
序列化,适用于四大组件间通信。 - 特点:简单快捷,但仅支持单次单向数据传输,无法处理复杂场景。
- 原理:通过
-
文件共享
- 原理:通过读写文件(如XML、JSON或普通文件)实现数据交换,需处理并发读写问题。
- 适用场景:数据同步要求低、无需实时性的场景(如配置信息缓存)。
-
Messenger
- 原理:基于
Handler
和AIDL
的轻量级通信,以消息队列传递Message
对象,支持跨进程单向异步通信。 - 特点:简化了
AIDL
的使用,但仅支持串行处理请求,不适合高并发。
- 原理:基于
-
AIDL(Android接口定义语言)
- 原理:通过定义跨进程接口实现方法调用,自动生成代理类处理通信细节,支持多线程和同步/异步操作。
- 适用场景:需要高性能、复杂数据交互的服务调用(如远程音乐播放控制)。
-
ContentProvider
- 原理:封装数据并提供标准
CRUD
接口,通过Uri
标识数据源,支持跨应用数据共享(如读取系统通讯录)。 - 特点:数据访问权限可控,适合结构化数据共享。
- 原理:封装数据并提供标准
-
Binder
- 原理:基于
C/S
架构的内核级通信机制,通过一次数据拷贝实现高效传输,支持身份验证和引用计数管理。 - 特点:Android 核心 IPC 方案,
AIDL
、Messenger
等均基于其实现。
- 原理:基于
-
Socket(套接字)
- 原理:基于网络协议(如TCP/IP)实现跨设备或本机进程通信,需处理连接管理和数据流解析。
- 适用场景:网络通信或对传输效率要求不高的本地进程交互。
二、特殊场景通信方式
-
BroadcastReceiver
- 原理:通过广播发送
Intent
实现一对多通信,支持有序广播和粘性广播,适用于系统级事件通知(如网络状态变化)。 - 限制:数据量小(受
Intent
大小限制),实时性较低。
- 原理:通过广播发送
-
共享内存
- 原理:通过
MemoryFile
或Ashmem
共享内存区域,避免数据拷贝,适用于大数据量传输(如图像处理)。 - 风险:需自行处理同步和内存泄漏问题。
- 原理:通过
三、选型建议
场景需求 | 推荐方式 | 优势 |
---|---|---|
简单数据传递(如启动组件) | Bundle + Intent | 无需额外配置,开发成本低 |
跨应用结构化数据共享 | ContentProvider | 标准化接口,权限可控 |
远程服务调用(高并发) | AIDL | 支持多线程和复杂数据类型 |
单向异步通知(低并发) | Messenger | 轻量级,代码简洁 |
系统级事件广播 | BroadcastReceiver | 一对多通信,组件解耦 |
高效大数据传输 | Binder / 共享内存 | 减少拷贝次数,性能最优 |
四、开发注意事项
- 线程安全:
AIDL
需手动处理多线程同步,避免并发冲突。 - 内存泄漏:
Binder
通信需及时注销监听器或释放资源。 - 性能优化:避免在主线程执行耗时 IPC 操作(如大文件传输),防止 ANR。
- 权限控制:跨进程访问敏感数据需声明权限(如
ContentProvider
的read/write
权限)。
通过合理选择 IPC 方式,可平衡性能、开发效率及安全性,满足不同业务场景需求。
7. view的绘制流程
Android View 绘制流程详解
一、整体流程框架
View 的绘制流程始于 ViewRootImpl,通过 performTraversals()
方法依次触发 测量(Measure)、布局(Layout) 和 绘制(Draw) 三个阶段。
- 触发时机:Activity 的
onResume()
完成后,ViewRootImpl
被创建并关联DecorView
(视图树的根节点)。 - 递归逻辑:从根视图
DecorView
开始,逐层向下遍历所有子 View,最终完成整棵视图树的绘制。
二、核心阶段分解
-
测量(Measure)
- 触发入口:
performTraversals()
调用performMeasure()
,执行根视图的测量逻辑。 - 核心逻辑:
- 父容器根据自身尺寸和子 View 的
LayoutParams
生成 MeasureSpec(包含测量模式和尺寸),传递给子 View。 - 子 View 的
onMeasure()
根据MeasureSpec
计算自身宽高(通过setMeasuredDimension()
保存结果)。
- 父容器根据自身尺寸和子 View 的
- 关键类/方法:
MeasureSpec
、View#onMeasure()
、ViewGroup#measureChildWithMargins()
。
- 触发入口:
-
布局(Layout)
- 触发入口:
performTraversals()
调用performLayout()
,确定 View 在父容器中的位置。 - 核心逻辑:
- 父容器通过
layout()
设置自身位置,并调用子 View 的layout()
传递位置参数(left
、top
、right
、bottom
)。 - 子 View 在
onLayout()
中根据父容器分配的位置调整自身布局(如ViewGroup
需计算子 View 的位置)。
- 父容器通过
- 触发入口:
-
绘制(Draw)
- 触发入口:
performTraversals()
调用performDraw()
,通过draw()
方法完成绘制。 - 核心逻辑:
- 绘制顺序:背景 → 自身内容(
onDraw()
) → 子 View(dispatchDraw()
) → 装饰(如滚动条)。 - 绘制工具:通过
Canvas
和Paint
对象进行图形绘制。
- 绘制顺序:背景 → 自身内容(
- 触发入口:
三、关键数据结构与机制
-
View 树结构
- 根节点:
DecorView
(继承FrameLayout
),包含标题栏和内容区域(R.id.content
)。 - 递归遍历:父容器负责触发子 View 的绘制流程(如
ViewGroup
调用子 View 的measure()
和layout()
)。
- 根节点:
-
MeasureSpec 的作用
- 组成:32 位整数,高 2 位表示测量模式(
EXACTLY
、AT_MOST
、UNSPECIFIED
),低 30 位表示尺寸值。 - 传递规则:父容器根据自身约束生成子 View 的
MeasureSpec
,子 View 基于此计算实际尺寸。
- 组成:32 位整数,高 2 位表示测量模式(
四、开发注意事项
-
性能优化
- 避免在
onMeasure()
或onDraw()
中执行耗时操作(如复杂计算或 IO 操作),防止主线程卡顿。 - 使用
ViewStub
或动态加载减少视图层级复杂度。
- 避免在
-
自定义 View
- 重写
onMeasure()
时必须调用setMeasuredDimension()
保存测量结果,否则可能抛出异常。 - 在
onDraw()
中避免频繁创建对象(如Paint
或Path
),建议复用或提前初始化。
- 重写
通过理解并遵循上述流程,可高效实现复杂 UI 布局,同时规避性能瓶颈。
8. 触摸事件的传递
Android 触摸事件传递机制解析
一、事件传递流程
-
起点:Activity → DecorView
用户触摸屏幕后,事件首先由 Activity 的dispatchTouchEvent()
接收,并传递至根视图 DecorView(继承自ViewGroup
),作为事件分发的入口。 -
ViewGroup → 子 View
- 自上而下传递:父容器(如
ViewGroup
)通过dispatchTouchEvent()
向子 View 分发事件,若未拦截则逐级向下传递。 - 拦截机制:父容器可通过
onInterceptTouchEvent()
拦截事件,强制由自身处理(返回true
时停止向下传递)。
- 自上而下传递:父容器(如
-
子 View 处理与事件回传
- 若子 View 的
onTouchEvent()
返回true
(表示消费事件),事件传递终止;否则事件会向上回传至父容器,直至由 Activity 的onTouchEvent()
处理。
- 若子 View 的
二、核心方法及作用
-
dispatchTouchEvent(MotionEvent)
- 入口方法:所有事件分发的起点,决定事件是否继续传递或终止。
- 返回值逻辑:
true
:事件被消费,停止传递;false
:未处理事件,回传给上层处理。
-
onInterceptTouchEvent(MotionEvent)
(仅 ViewGroup)- 拦截控制:在父容器中判断是否截获事件(如滑动冲突场景),默认返回
false
。
- 拦截控制:在父容器中判断是否截获事件(如滑动冲突场景),默认返回
-
onTouchEvent(MotionEvent)
- 最终消费:处理事件逻辑(如点击、滑动),返回
true
表示消费,终止传递链。
- 最终消费:处理事件逻辑(如点击、滑动),返回
三、关键特性与规则
-
事件消费优先级
OnTouchListener
优先于onTouchEvent()
:若 View 设置了OnTouchListener
且返回true
,则onTouchEvent()
不会触发。
-
ACTION_DOWN 的特殊性
- 后续事件依赖:若
ACTION_DOWN
未被消费(所有onTouchEvent()
返回false
),后续MOVE
、UP
等事件将不再传递。
- 后续事件依赖:若
-
事件序列完整性
- 同一事件序列(如
DOWN → MOVE → UP
)应由同一 View 消费,避免中途切换处理者导致逻辑异常57。
- 同一事件序列(如
四、开发注意事项
- 避免过度拦截:频繁在
onInterceptTouchEvent()
中拦截事件可能导致子 View 无法响应正常操作(如点击失效)26。 - 性能优化:在
onTouchEvent()
中减少耗时操作,防止主线程卡顿58。 - 滑动冲突处理:通过父容器与子 View 的拦截协作(如
ScrollView
与ListView
)解决嵌套滑动问题26。
通过理解上述机制,可精准控制事件分发路径,优化交互逻辑并解决复杂场景下的冲突问题15。