
概念
作用
概念
作用
概念
作用
特性 | 核心思想 | Android引用举例 | 作用 |
|---|---|---|---|
封装 | 隐藏实现,暴露接口 | Activity 生命周期、自定义 View、数据模型类 | 安全、解耦、易维护 |
继承 | 代码复用,建立层次 | 创建 Activity/Fragment、自定义 View、BaseActivity | 复用、扩展、结构化 |
多态 | 一个接口,多种实现 | View 事件分发、Adapter、回调接口、设计模式 | 灵活、可扩展、高内聚低耦合 |
这三大特性相辅相成,共同构成了 Android 应用健壮、灵活、可维护的代码基础。熟练掌握并灵活运用它们,是成为一名优秀 Android 开发者的必经之路。
接口 | 抽象类 | |
|---|---|---|
实现 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 | 子类使用extends关键字继承抽象类。子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现 |
构造器 | 接口不能有构造器 | 抽象类可以有构造器 |
与正常J类的区别 | 接口是完全不同的类型 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 |
访问修饰符 | 接口方法默认修饰符是public,不可以使用其它修饰符 | 抽象方法可以有public、protected和default这些修饰符 |
main方法 | 方法 接口没有main方法,因此我们不能运行它 | 抽象方法可以有main方法并且我们可以运行它 |
继承与实现 | 可以实现多个接口 | 只能继承一个抽象类 |
速度 | 接口是稍微有点慢的,它需要时间去寻找在类中实现的方法 | 速度较快 |
添加新方法 | 接口中添加方法,那么你必须改变实现该接口的类 | 抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码 |
Java 内部类是一个功能强大且灵活的特性,它通过提供更细粒度的封装和组织方式,帮助开发者构建更优雅、更健壮的代码。理解四种内部类的区别和适用场景是关键:
ArrayList
LinkedList
操作 | ArrayList | LinkedList | 说明 |
|---|---|---|---|
随机访问 (get/set by index) | O(1) | O(n) | ArrayList 可以通过索引直接计算内存地址,速度极快。LinkedList 必须从头或尾开始遍历链表,直到找到目标位置,速度慢。 |
尾部插入/删除 (add/remove last) | O(1) (平均) | O(1) | ArrayList 在尾部操作通常很快,但如果触发扩容,成本为 O(n)。LinkedList 只需修改尾节点的引用,始终很快 |
头部插入/删除 (add/remove first) | O(n) | O(1) | ArrayList 在头部插入或删除,需要将后面所有元素向前或向后移动一位。LinkedList 只需修改头节点的引用,速度极快 |
中间插入/删除 (by index) | O(n) | O(n) | 两者都需要先找到指定位置(ArrayList 是 O(1),LinkedList 是 O(n)),然后 ArrayList 需要移动元素,LinkedList 需要修改引用。虽然修改引用比移动元素快,但查找位置的成本主导了总时间,因此平均来看两者复杂度相同。但在实践中,对于靠近两端的操作,LinkedList 通常更快;对于靠近中间的操作,ArrayList 可能更快(因为移动元素是内存块拷贝,而链表遍历涉及多次指针跳转) |
迭代遍历 (Iterator) | 较快 | 较慢 | ArrayList 的元素在内存中连续,CPU 缓存命中率高(空间局部性好)。LinkedList 的节点分散,缓存命中率低 |
ArrayList
LinkedList
LinkedList:
除了实现 List 接口,还实现了 Deque (Double-ended Queue) 接口,因此,它提供了一系列针对头尾操作的高效方法,使其可以方便地用作栈(Stack)、队列(Queue)或双端队列(Deque)。
ArrayList
没有针对头尾操作的特殊优化方法,所有位置的操作都通过通用的 add(index, E e) 和 remove(int index) 等方法实现。
特性 | HashMap | Hashtable |
|---|---|---|
线程安全 | ❌ 非线程安全。在多线程环境下使用可能导致数据不一致或死循环。 | ✅ 线程安全。所有公共方法都用 synchronized 关键字修饰,同一时间只允许一个线程访问。 |
性能 | ⚡ 性能高。因为没有同步开销,在单线程环境下速度远超 Hashtable。 | 🐢 性能低。由于每个方法都加锁,导致并发性能极差。 |
Null 值支持 | ✅ 允许 null。允许一个 null 键和任意多个 null 值。 | ❌ 不允许 null。键或值为 null 时会抛出 NullPointerException。 |
出现时间 | JDK 1.2 引入。 | JDK 1.0 就已存在,是早期的遗留类。 |
继承关系 | 继承自 AbstractMap 类。 | 继承自已废弃的 Dictionary 类。 |
推荐替代方案 | 在需要线程安全时,推荐使用 ConcurrentHashMap。 | 无。官方不推荐在新代码中使用。 |
Hashtable
put(), get(), remove())上添加 synchronized 关键字来实现线程安全。HashMap
get() 操作陷入无限循环。ConcurrentModificationException 异常,这是一种安全预警机制。Collections.synchronizedMap(new HashMap<...>()) 包装,但这同样是全局锁,性能与 Hashtable 类似。ConcurrentHashMap。它采用分段锁(JDK 7)或 CAS + synchronized(JDK 8+)等更精细的并发控制策略,在保证线程安全的同时,大幅提升了并发性能。HashMap
null 作为键或值。null 键会被特殊处理,存储在哈希表数组的第 0 个位置(table[0])。null 值。Hashtable
null 键或 null 值。put 或 get 方法内部会进行 null 检查,一旦发现 null,立即抛出 NullPointerException。Hashtable
Dictionary 类。Dictionary 是一个非常古老的抽象类,在现代 Java 中已被标记为 @Deprecated(不推荐使用)。HashMap
AbstractMap 类,这是 Java 集合框架(JCF)中为 Map 实现提供的标准抽象基类。HashMap
Iterator 进行遍历。Iterator 是 fail-fast 的。如上所述,在迭代过程中检测到结构修改会立即失败并抛出异常,有助于快速发现问题。Hashtable
Iterator,还保留了旧的 Enumeration 接口(通过 elements() 和 keys() 方法获取)。Iterator 不是 fail-fast 的,在迭代过程中即使结构被修改,也可能不会抛出异常,导致结果不可预测。特性 | HashMap | Hashtable |
|---|---|---|
默认初始容量 | 16 | 11 |
容量要求 | 必须是 2 的幂。这使得在计算元素存储位置时,可以用位运算 (&) 替代取模运算 (%),效率更高。 | 无特殊要求,可以是任意整数。 |
扩容方式 | 容量变为原来的 2 倍。 | 容量变为原来的 2 倍 + 1(即 old * 2 + 1)。这种设计旨在使容量尽量为奇数或质数,以期在使用取模运算时减少哈希冲突。 |
Hashtable
hashCode() 方法返回的值。index = (hashCode & 0x7FFFFFFF) % capacity。HashMap
hashCode() 的结果进行了一次扰动函数(hash function)处理,目的是让哈希值的高位也参与到低位的计算中,使哈希值分布更均匀,减少冲突。index = hash & (capacity - 1)(得益于容量是 2 的幂,位运算效率极高)。HashMap
Hashtable
String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下,便于大家观看,也便于加深自己学习过程中对这些知识点的记忆,如果哪里有误,恳请指正。这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。
StringBuilder > StringBuffer > String
// 以下面一段代码为例:
String str="abc";
System.out.println(str);
str=str+"de";
System.out.println(str); String str="abc"+"de";
StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
System.out.println(str);
System.out.println(stringBuilder.toString()); String str1="abc";
String str2="de";
String str=str1+str2;StringBuilder是线程不安全的,而StringBuffer是线程安全的
特性 | synchronized | volatile |
|---|---|---|
作用对象 | 方法、代码块 | 变量 |
核心机制 | 互斥锁 (Monitor) | 内存屏障 (Memory Barrier) |
原子性 | 保证 (通过互斥实现) | 不保证 (仅保证单次读/写原子) |
可见性 | 保证 (进出同步块时刷新主内存) | 保证 (强制读写主内存) |
有序性 | 保证 (通过互斥和禁止重排序) | 保证 (通过内存屏障禁止重排序) |
线程阻塞 | 会阻塞 (未获取锁的线程进入阻塞状态) | 不会阻塞 (线程可以继续执行) |
性能开销 | 较大 (涉及操作系统,可能上下文切换) | 较小 (主要是内存屏障开销) |
适用场景 | 复杂的原子操作、临界区保护 | 简单的状态标志、一次性安全发布、DCL单例模式 |
共享变量:如果一个变量在多个线程中都使用到了,那么这个变量就是这几个线程的共享变量。 可见性:一个线程对共享变量的修改,能够及时地到主内存并且让其他的线程看到。 原子性:线程要么执行完整个代码块,要么完全不执行,不会被其他线程打断。
详见 Java中 synchronized 和 volatile 详解
在高并发的 Java 应用开发中,频繁地创建和销毁线程会带来巨大的系统开销(如内存分配、CPU 调度),并可能导致系统资源耗尽。线程池(Thread Pool)技术应运而生,它通过预先创建并管理一组可复用的线程,极大地优化了多线程任务的执行效率和资源利用率。
一个标准的 Java 线程池主要由以下几个部分组成:
当一个新任务被提交到线程池时,其处理流程如下:
corePoolSize,则立即创建一个新线程来执行该任务,无论是否有空闲线程。corePoolSize,则尝试将任务放入 workQueue 队列中。maximumPoolSize,则创建一个非核心线程来执行该任务。maximumPoolSize,则触发 RejectedExecutionHandler 拒绝策略来处理该任务。ThreadPoolExecutor 提供了非常灵活的构造函数,允许开发者精确控制线程池的行为。
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程空闲存活时间
TimeUnit unit, // 存活时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)CPU 密集型任务
I/O 密集型任务
混合型任务
选择合适的任务队列
ArrayBlockingQueue),并设置合理的容量,以防止资源耗尽。LinkedBlockingQueue),除非你能完全掌控任务的提交速率。Java 虚拟机(JVM,Java Virtual Machine)是 Java 平台的核心和基石,它使得 Java 语言能够实现“一次编写,到处运行”(Write Once, Run Anywhere)的跨平台特性。JVM 并非一个真实的物理机器,而是一个由软件模拟的、运行在操作系统之上的抽象计算机
JVM 在执行 Java 程序时,会将其管理的内存划分为若干个不同的区域,这些区域各有各的用途。
OutOfMemoryError 情况的区域。StackOverflowError。OutOfMemoryError。StackOverflowError 或 OutOfMemoryError。OutOfMemoryError。OutOfMemoryError。"Hello")和符号引用(如类和接口的全限定名、字段和方法的名称及描述符)。在 JDK 8 之后,字符串常量池被移到了堆中。OutOfMemoryError。java.nio 包中的 DirectByteBuffer 对象可以分配直接内存。这部分内存不受 JVM 堆大小限制,但受本机总物理内存限制。它在进行 I/O 操作(如 NIO)时能显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。OutOfMemoryError。类加载机制和双亲委派模型。
泛型的核心思想是参数化类型,即在定义类、接口或方法时,不指定具体的类型,而是使用一个“类型参数”(如 , , <K, V>)来代替。在使用时,再传入具体的类型。
反射是 Java 在运行时动态获取类、接口、字段、方法等信息,并能动态调用对象方法、修改字段值的能力。它打破了编译时的类型检查限制。
特性 | 泛型 (Generics) | 反射 (Reflection) |
|---|---|---|
主要作用时期 | 编译期 (Compile-time) | 运行期 (Runtime) |
核心目的 | 提供编译时类型安全,消除强制转换,提高代码复用性 | 在运行时动态获取和操作类、方法、字段等信息 |
关键机制 | 类型擦除 (Type Erasure) | Class, Field, Method, Constructor 等 API |
相互关系 | 类型擦除使得标准反射 API 无法直接获取泛型实例的具体类型 | 可通过 Type 接口(如 ParameterizedType)获取在声明处(字段、方法、父类)的泛型信息,从而部分“突破”类型擦除的限制 |
简单来说:
java.lang.reflect.Type 体系保留了关键的声明信息,使得反射在特定场景下依然能够与泛型协同工作,这为构建高度灵活的框架和工具提供了可能。双亲委派机制的核心思想是:当一个类加载器收到加载某个类的请求时,它不会立即尝试自己去加载,而是先将这个请求“委托”给它的父类加载器去处理。只有当父类加载器反馈自己无法完成这个加载请求时,子类加载器才会尝试自己去加载
在 Java 中,类加载器以树状的父子层级结构组织,主要包含以下类加载器:
java.lang.ClassLoader 的子类。在 Java 代码中获取其引用时结果为 null。<JAVA_HOME>/jre/lib 下的 rt.jar、resources.jar、charsets.jar 等)。例如,java.lang.Object、java.lang.String 等类由其加载。java.lang.ClassLoader 的子类。<JAVA_HOME>/jre/lib/ext 目录或系统变量 java.ext.dirs 指定路径下的 JAR 包。java.lang.ClassLoader 类实现。当类加载器(如应用程序类加载器)收到加载 com.example.MyClass 的请求时,流程如下:
jre/lib 核心类库中查找 com.example.MyClass(失败,因用户类不存在于此)。jre/lib/ext 目录下查找(失败)。ClassNotFoundException)。概括:自底向上检查,自顶向下加载。
双亲委派机制带来以下关键好处:
java.lang.String 并植入恶意代码。在 Android 的 Activity 中,Window、DecorView 和 Layout 是构成用户界面的三个核心层级,它们共同协作,将您在 XML 布局文件中定义的 UI 最终呈现到屏幕上


方法 | 触发场景 |
|---|---|
onCreate() | Activity 首次创建时调用 |
onStart() | Activity 对用户可见时调用 |
onResume() | Activity 获得焦点(可交互)时调用 |
onPause() | Activity 失去焦点(如弹出对话框)时调用 |
onStop() | Activity 不可见时调用 |
onDestroy() | Activity 被销毁时调用 |
A onPause()—> B onCreate() ---->B onStart() —> B o’nResume() —> A onstop()
B onPause() —>A onRestart() —> A onStart() —>A onResume() —> B onStop —> B onDestroy()
A onPause()—> B onCreate() ---->B onStart() —> B o’nResume()
B onPause() —>A onResume() —> B onStop —> B onDestroy()
步骤 1:在第一个 Activity 中注册回调使用 ActivityResultLauncher 启动第二个 Activity,并处理返回结果:
public class FirstActivity extends AppCompatActivity {
// 1. 定义 Activity Result Launcher
private ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
if (data != null) {
String returnedValue = data.getStringExtra("key_return");
Toast.makeText(this, "回传值: " + returnedValue, Toast.LENGTH_SHORT).show();
}
}
}
);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
// 2. 启动第二个 Activity
findViewById(R.id.button).setOnClickListener(v -> {
Intent intent = new Intent(this, SecondActivity.class);
someActivityResultLauncher.launch(intent);
});
}
}步骤 2:在第二个 Activity 中设置回传值 通过 setResult() 返回数据,并结束当前 Activity:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.button_return).setOnClickListener(v -> {
// 1. 准备回传数据
Intent resultIntent = new Intent();
resultIntent.putExtra("key_return", "这是从SecondActivity返回的数据");
// 2. 设置结果并结束当前Activity
setResult(Activity.RESULT_OK, resultIntent);
finish();
});
}
}关键点 ActivityResultLauncher:替代 startActivityForResult(),更安全且避免内存泄漏。 RESULT_OK:表示操作成功,也可用 RESULT_CANCELED 表示取消。 setResult():必须在 finish() 前调用。

启动服务和绑定服务对比
特性 | startService | bindService |
|---|---|---|
启动目的 | 长期后台运行(如音乐播放、数据同步) | 与调用者交互(如 Activity 调用 Service 方法) |
生命周期控制 | 独立于调用者,需手动调用 stopService() 或 stopSelf() 停止 | 依赖调用者,调用者解绑或销毁时自动解绑,无绑定时销毁 |
回调方法 | onCreate() → onStartCommand() → onDestroy() | onCreate() → onBind() → onUnbind() → onDestroy() |
重复调用行为 | 多次调用 startService() 仅触发 onStartCommand(),不重复创建 Service | 多次调用 bindService() 仅首次触发 onCreate() 和 onBind() |
调用者与 Service 关系 | 无直接交互,通过 Intent 传递数据 | 通过 ServiceConnection 获取 IBinder,直接调用 Service 方法 |
IntentService 的原理 IntentService 是 Android 提供的一种特殊 Service,其核心原理基于 HandlerThread + 工作队列,通过以下机制实现后台任务处理
IntentService 的适用场景
对比其他方案
方案 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
IntentService | 串行后台任务、自动生命周期管理 | 简单易用,自动销毁 | 无法并行,效率较低 |
HandlerThread | 自定义线程管理 | 灵活控制线程优先级 | 需手动处理任务队列和销毁 |
WorkManager | 延迟或周期性任务 | 支持后台限制和电量优化 | 适合非实时任务 |
Kotlin 协程 | 复杂异步逻辑 | 代码简洁,支持并发 | 学习成本较高 |
前台 Service 是 Android 中一种优先级更高的后台服务,通过在通知栏显示持续存在的通知来告知用户其正在运行,从而避免被系统轻易回收(尤其在低内存或后台限制严格的情况下)
前台service的作用
创建前台service
代码示例 (1)定义通知渠道(Android 8.0+)
// MainActivity 或 Application 类中初始化通知渠道
private static final String CHANNEL_ID = "foreground_service_channel";
private static final String CHANNEL_NAME = "Foreground Service";
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}(2)创建前台 Service 类
public class ForegroundService extends Service {
private static final int NOTIFICATION_ID = 1;
@Override
public void onCreate() {
super.onCreate();
// 初始化逻辑(如加载资源)
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 1. 创建通知
Notification notification = buildNotification();
// 2. 启动前台 Service(必须调用)
startForeground(NOTIFICATION_ID, notification);
// 3. 执行后台任务(示例:模拟耗时操作)
new Thread(() -> {
// 模拟任务
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 任务完成后停止 Service
stopSelf();
}).start();
return START_STICKY; // 服务被杀死后自动重启
}
@Override
public IBinder onBind(Intent intent) {
return null; // 若无需绑定,返回 null
}
// 构建通知
private Notification buildNotification() {
Intent stopIntent = new Intent(this, ForegroundService.class);
stopIntent.setAction("STOP_SERVICE");
PendingIntent stopPendingIntent = PendingIntent.getService(
this, 0, stopIntent, PendingIntent.FLAG_IMMUTABLE
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("前台服务运行中")
.setContentText("点击停止服务")
.setSmallIcon(R.drawable.ic_notification) // 必须设置图标
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.addAction(R.drawable.ic_stop, "停止", stopPendingIntent); // 添加停止按钮
return builder.build();
}
@Override
public void onDestroy() {
super.onDestroy();
// 移除通知(可选)
stopForeground(true);
}
}(3)启动和停止 Service
// 启动 Service
Intent serviceIntent = new Intent(this, ForegroundService.class);
startService(serviceIntent);
// 停止 Service(通过广播或绑定方式)
// 示例:通过 Action 停止
Intent stopIntent = new Intent(this, ForegroundService.class);
stopIntent.setAction("STOP_SERVICE");
startService(stopIntent); // 实际需在 Service 中处理 Action注意事项
<service android:name=".ForegroundService" />
<!-- 若需后台定位等权限,需额外声明 -->核心区别分析
维度 | JobScheduler | WorkManager |
|---|---|---|
API 兼容性 | 仅支持 Android 5.0+(API 21+) | 支持 Android 4.0+(API 14+) |
底层实现 | 系统原生调度服务 | 根据设备 API 动态选择:- API 23+:JobScheduler- API 14-22:AlarmManager + BroadcastReceiver |
任务可靠性 | 依赖系统调度,可能因资源限制延迟执行 | 持久化存储 + 自动重试,确保任务最终执行 |
功能扩展性 | 基础条件约束(网络、充电、空闲) | 支持任务链、输入/输出数据传递、优先级设置 |
开发复杂度 | 需手动定义 JobInfo 和 JobService | 通过 Worker 类简化任务定义,API 更简洁 |
适用场景 | 高性能节能调度,对电池寿命敏感的任务 | 跨版本兼容、持久化任务、复杂任务流程 |
代码示例
// 定义 JobInfo
ComponentName componentName = new ComponentName(this, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // 仅 Wi-Fi 下执行
.setPersisted(true) // 设备重启后保留任务
.build();
// 调度任务
JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
// 实现 JobService
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 执行耗时任务
new Thread(() -> {
// 任务逻辑
jobFinished(params, false); // 通知系统任务完成
}).start();
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
// 任务取消逻辑
return true;
}
}// 定义 Worker
public class MyWorker extends Worker {
public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
// 执行耗时任务
return Result.success(); // 返回任务结果
}
}
// 定义约束条件
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // 仅 Wi-Fi 下执行
.setRequiresCharging(true) // 充电时执行
.build();
// 调度任务
WorkManager.getInstance(this).enqueue(
new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(constraints)
.build()
);BroadcastReceiver(广播接收器)是 Android 四大组件之一,用于接收和响应来自系统或其他应用程序发出的广播消息(Intent)。它是一种全局的监听器,可以在应用未运行时被系统唤醒以处理特定事件。
根据发送和接收方式,广播主要分为两大类:
广播类型 | 特点 | 优点 | 缺点 | 发送方法 |
|---|---|---|---|---|
标准广播 (Normal Broadcast) | 完全异步。广播发出后,所有符合条件的接收器几乎同时收到,没有先后顺序。 | 效率高 | 接收器无法截断(abort)广播,也无法将处理结果传递给下一个接收器。 | Context.sendBroadcast(Intent) |
有序广播 (Ordered Broadcast) | 同步执行。广播按照接收器声明的优先级(android:priority,数值越大优先级越高)依次接收。前面的接收器可以截断广播(调用 abortBroadcast()),使其不再传递;也可以通过 setResultData() / getResultData() 将数据传递给下一个接收器。 | 可以控制广播的传递流程。 | 效率相对较低。 | Context.sendOrderedBroadcast(Intent, String receiverPermission) |
广播接收器必须注册才能生效,有两种注册方式:
传统的 BroadcastReceiver 是跨进程的,存在安全风险(其他应用可能监听或伪造你的广播)和性能开销(涉及 Binder 通信)因此,官方推出 LocalBroadcastManager。
特点:
用法:
// 发送本地广播
Intent intent = new Intent("com.example.MY_LOCAL_ACTION");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
// 注册本地广播接收器
IntentFilter localFilter = new IntentFilter("com.example.MY_LOCAL_ACTION");
LocalBroadcastManager.getInstance(this).registerReceiver(mLocalReceiver, localFilter);
// 注销本地广播接收器
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalReceiver);本地广播总结
要点 | 说明 |
|---|---|
优先使用本地广播 | 应用内通信首选 LocalBroadcastManager,安全高效。 |
谨慎使用静态注册 | 仅在需要应用未启动时接收广播的场景下使用(如开机启动),并注意 Android 8.0+ 的后台限制。 |
动态注册必须注销 | 在 Activity/Service 的 onDestroy() 中调用 unregisterReceiver(),防止内存泄漏。 |
onReceive() 禁止耗时 | 10秒内必须完成,否则 ANR。耗时任务交给 Service。 |
明确指定接收者 | 发送自定义广播时,使用 setPackage() 或 setComponent(),避免广播被滥用。 |
注意权限 | 某些系统广播需要在 AndroidManifest.xml 中声明相应权限才能接收。 |
开发中不可避免的要去接收系统的事件以完成业务的开发
Intent Action 常量 | 说明 |
|---|---|
android.intent.action.BATTERY_CHANGED | 持久的广播,包含电池的充电状态、级别和其他信息 |
android.intent.action.BATTERY_LOW | 标识设备的低电量条件 |
android.intent.action.BATTERY_OKAY | 标识电池在电量低之后,现在已经恢复正常 |
android.intent.action.BOOT_COMPLETED | 在系统完成启动后广播一次 |
android.intent.action.BUG_REPORT | 显示报告bug的活动 |
android.intent.action.CALL | 执行呼叫数据指定的某人 |
android.intent.action.CALL_BUTTON | 用户点击"呼叫"按钮打开拨号器或者其他拨号的合适界面 |
android.intent.action.DATE_CHANGED | 日期发生改变 |
android.intent.action.REBOOT | 设备重启 |

URI 是 ContentProvider 的“地址”,用于唯一标识要访问的数据资源。其标准格式为: content://<_authority>/<data_path>[/<_id>]
content://com.example.myapp.provider/users:访问 users 表中的所有用户数据。 content://com.example.myapp.provider/users/5:访问 ID 为 5 的单个用户数据。 content://media/external/images/media:访问系统媒体库中的所有外部存储图片。
这是客户端用来与 ContentProvider 交互的统一接口。你的应用不需要直接实例化 ContentProvider,而是通过 Context.getContentResolver() 方法获取 ContentResolver 对象,然后调用其 insert(), query(), update(), delete() 方法,并传入对应的 URI 和其他参数来操作数据。
// 获取 ContentResolver
ContentResolver resolver = getContentResolver();
// 查询数据
Cursor cursor = resolver.query(
Uri.parse("content://com.example.myapp.provider/users"),
new String[]{"_id", "name", "email"}, // 要查询的列
null, // WHERE 子句
null, // WHERE 子句的参数
null // 排序
);
// 插入数据
Uri newUri = resolver.insert(
Uri.parse("content://com.example.myapp.provider/users"),
contentValues // 包含新数据的 ContentValues 对象
);ContentProvider 需要实现 getType(Uri uri) 方法,用于返回对应 URI 所代表数据的 MIME 类型。这对于系统(如 Intent 选择器)识别数据类型非常重要。
单条记录:通常以 vnd.android.cursor.item/ 开头。 多条记录:通常以 vnd.android.cursor.dir/ 开头。
特性 | ContentProvider | SharedPreferences | SQLite Database | File Storage |
|---|---|---|---|---|
主要用途 | 跨应用数据共享 | 存储简单键值对配置 | 存储结构化关系数据 | 存储文件(图片、音频、缓存等) |
访问范围 | 应用间 | 应用内 | 应用内 (或通过 CP 共享) | 应用内 (或通过文件权限共享) |
数据结构 | 任意 (通过 URI 和列定义) | 键值对 (String, int, boolean等) | 表格 (行和列) | 二进制或文本流 |
复杂度 | 高 (需实现接口、注册、权限) | 低 | 中 | 低到中 |
性能 | 中 (涉及 IPC) | 高 | 高 | 高 |
View 体系是 Android 界面编程的基础,它是一个树形结构,所有 UI 元素都基于 View 或 ViewGroup。
View: Android 中所有 UI 控件的基类,代表屏幕上的一个矩形区域,负责绘制和事件处理。例如:Button, TextView, ImageView。 ViewGroup: View 的子类,也是容器控件的基类。它可以包含并管理多个子 View 或子 ViewGroup,形成一个树状结构(View Tree)。例如:LinearLayout, RelativeLayout, FrameLayout, RecyclerView。 层级关系:Activity -> Window -> DecorView (根 ViewGroup) -> ViewGroup -> View。
理解坐标是处理触摸事件和滑动的基础。Android 中主要有两种坐标系:
Android 坐标系 (屏幕坐标系): 原点:屏幕左上角。 X 轴:向右为正。 Y 轴:向下为正。 获取方法:MotionEvent.getRawX(), MotionEvent.getRawY()。 视图坐标系 (View 坐标系): 原点:当前 View 的左上角(相对于其直接父容器)。 X 轴:向右为正。 Y 轴:向下为正。 获取方法:MotionEvent.getX(), MotionEvent.getY()。
ACTION_DOWN: 手指初次接触屏幕。 ACTION_MOVE: 手指在屏幕上移动(会触发多次)。 ACTION_UP: 手指离开屏幕。 ACTION_CANCEL: 事件被上层拦截,子 View 不再接收后续事件。 一个完整的触摸操作(如点击)通常由 ACTION_DOWN -> ACTION_MOVE (0次或多次) -> ACTION_UP 组成,称为一个事件序列。
方法名 | 作用 | 返回值说明 | 存在位置 |
|---|---|---|---|
dispatchTouchEvent(MotionEvent event) | 事件分发的入口,决定事件是自己处理、拦截,还是分发给子View | true:事件被消费,流程结束;false:事件未被消费,向上传递 | Activity, ViewGroup, View |
onInterceptTouchEvent(MotionEvent event) | 仅ViewGroup拥有,在事件传递给子View前决定是否拦截 | true:拦截事件,交由自身onTouchEvent处理;false:不拦截,事件继续向下分发 | ViewGroup(默认返回false) |
onTouchEvent(MotionEvent event) | 处理触摸事件的核心方法,编写点击/滑动逻辑 | true:事件被消费,流程结束;false:事件未被消费,向上传递 | Activity, View(ViewGroup继承自View) |

思考 事件分发机制: dispatchTouchEvent(), onInterceptTouchEvent(), onTouchEvent() 三个方法的作用、返回值含义及调用顺序。如何解决滑动冲突?
事件分发机制最常见的应用就是解决复杂的滑动冲突,例如:ScrollView 嵌套 RecyclerView,或 ViewPager 嵌套 ScrollView。
public class MyParentLayout extends ViewGroup {
private int mLastXIntercept, mLastYIntercept;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false; // ACTION_DOWN 一定不能拦截,否则子 View 无法收到事件
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
// 根据滑动方向判断,例如:竖直滑动交给父容器,水平滑动交给子 View
if (Math.abs(deltaY) > Math.abs(deltaX)) {
intercepted = true; // 父容器拦截竖直滑动
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false; // UP 事件一般不拦截
break;
default:
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}
}public class MyChildView extends View {
private int mLastX, mLastY;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true); // 请求父容器不要拦截
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (Math.abs(deltaY) > Math.abs(deltaX)) {
// 如果是竖直滑动,让父容器来处理
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
}根据需求和复杂度,通常有以下三种途径:
方式 | 继承 View 或 ViewGroup(完全自绘) | 继承现有的 View(功能扩展) | 继承 ViewGroup(组合控件) |
|---|---|---|---|
适用场景 | 需要从零开始绘制一个全新的、系统没有提供的控件,例如一个仪表盘、一个签名板、一个游戏视图或一个复杂的动画效果。 | 在现有控件的基础上增加新功能或修改其行为。例如,创建一个带清空按钮的 EditText,或者一个可以显示进度的 Button。 | 将多个现有的 View 组合在一起,形成一个具有特定功能的复合控件。这是最常用、最推荐的方式,因为它符合“组合优于继承”的设计原则。例如,标题栏(包含返回按钮、标题文字、设置按钮)、商品卡片(包含图片、标题、价格、购买按钮)等。 |
核心工作 | 重写 onMeasure() 方法,精确测量控件的尺寸。重写 onDraw() 方法,使用 Canvas 和 Paint 对象绘制所有内容。重写 onTouchEvent() 方法,处理触摸事件以实现交互。 | 通常需要重写 onDraw() 方法,在原有绘制基础上添加新元素。可能需要重写 onTouchEvent() 方法来处理新的交互逻辑。有时也需要重写 onMeasure() 以适应新增内容的尺寸。 | 重写 onMeasure() 方法,测量自身及其所有子 View 的尺寸。重写 onLayout() 方法,确定每个子 View 在容器中的具体位置。可能需要重写 onInterceptTouchEvent() 和 onTouchEvent() 来处理内部的事件分发和冲突。 |
优点 | 灵活性最高,可以实现任何视觉效果。 | 复用性强,开发效率高,可以利用现有控件的成熟逻辑。 | 结构清晰,易于维护和扩展,代码复用性极高,能有效减少布局嵌套层级。 |
缺点 | 工作量最大,需要自己处理所有细节,包括 padding、wrap_content 等。 | 受限于父类控件的结构和行为。 | 需要管理子 View 的测量和布局,逻辑相对复杂。 |
1. Measure (测量) - onMeasure(int widthMeasureSpec, int heightMeasureSpec)
MeasureSpec 是一个 32 位的 int 值,高 2 位是测量模式,低 30 位是测量大小。 EXACTLY: 父容器已经确定了子View 的精确大小。对应 match_parent 或具体的数值(如 100dp)。 AT_MOST: 子 View 的大小不能超过父容器指定的值。对应 wrap_content。 UNSPECIFIED: 子 View 想要多大就多大,父容器不限制。通常在 ScrollView 等容器中出现。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int myWidth, myHeight;
// 处理宽度
if (widthMode == MeasureSpec.EXACTLY) {
myWidth = widthSize;
} else {
// 计算内容所需宽度 + padding
myWidth = calculateContentWidth() + getPaddingLeft() + getPaddingRight();
if (widthMode == MeasureSpec.AT_MOST) {
myWidth = Math.min(myWidth, widthSize);
}
}
// 处理高度 (逻辑同上)
if (heightMode == MeasureSpec.EXACTLY) {
myHeight = heightSize;
} else {
myHeight = calculateContentHeight() + getPaddingTop() + getPaddingBottom();
if (heightMode == MeasureSpec.AT_MOST) {
myHeight = Math.min(myHeight, heightSize);
}
}
setMeasuredDimension(myWidth, myHeight);
}// 在自定义 ViewGroup 中
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
// 根据你的布局逻辑计算每个子 View 的位置
int childLeft = ...;
int childTop = ...;
int childRight = childLeft + child.getMeasuredWidth();
int childBottom = childTop + child.getMeasuredHeight();
child.layout(childLeft, childTop, childRight, childBottom);
}
}
}绘制背景 (background.draw(canvas)) 保存画布图层 (如果需要) 绘制内容 (onDraw(canvas)) 绘制子 View (dispatchDraw(canvas)) 绘制装饰 (如滚动条)
private Paint mPaint;
private int mRadius = 50;
public MyCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 在构造函数中初始化,避免在 onDraw 中创建
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 考虑 padding,将圆绘制在 View 的中心
int centerX = getPaddingLeft() + (getWidth() - getPaddingLeft() - getPaddingRight()) / 2;
int centerY = getPaddingTop() + (getHeight() - getPaddingTop() - getPaddingBottom()) / 2;
canvas.drawCircle(centerX, centerY, mRadius, mPaint);
}RecyclerView 是 Android 开发中用于高效显示大量数据列表的核心组件,它取代了旧的 ListView 和 GridView,提供了更高的灵活性和性能。以下是 RecyclerView 的核心要点解析:
RecyclerView 的设计遵循了“分离关注点”的原则,其功能被拆分为几个独立的组件:
RecyclerView 本身 (The Container)
ViewGroup,负责管理子视图(Item Views)的布局和回收。LayoutManager (布局管理器)
LinearLayoutManager: 线性布局(垂直或水平)。GridLayoutManager: 网格布局。StaggeredGridLayoutManager: 瀑布流布局(交错网格)。RecyclerView 设置一个 LayoutManager,否则它不会显示任何内容。Adapter (适配器)
RecyclerView 之间的桥梁。它负责创建 Item View (onCreateViewHolder) 和将数据绑定到这些视图上 (onBindViewHolder)。RecyclerView.Adapter<YourViewHolder>。onCreateViewHolder(ViewGroup parent, int viewType): 创建一个新的 ViewHolder 实例。通常在这里通过 LayoutInflater 从布局文件 (item_layout.xml) 填充视图。onBindViewHolder(YourViewHolder holder, int position): 将数据绑定到指定位置的 ViewHolder 上。这是性能优化的关键点,应避免在此方法中进行耗时操作。getItemCount(): 返回数据集中项目的总数。ViewHolder (视图持有者)
onBindViewHolder 中重复调用 findViewById,从而显著提升滚动性能。Adapter 的内部类,继承自 RecyclerView.ViewHolder。onCreateViewHolder 中创建 ViewHolder 时,会将整个 Item View 传递给父类构造函数。RecyclerView 的四级缓存机制是其性能优化的核心,通过分级管理 ViewHolder 的复用与回收,显著减少了重复创建和绑定数据的开销。
缓存级别 | 名称 | 组成 | 作用 | 核心特点 |
|---|---|---|---|---|
一级缓存 | Scrap 缓存 | mAttachedScrap(未移除/更新的无效 ViewHolder)mChangedScrap(数据变更的无效 ViewHolder) | 临时保存屏幕内因布局变化(如动画、数据更新)分离的 ViewHolder,不参与滚动回收 | 复用优先级最高,直接匹配 position 或 id 返回,无需重新绑定数据 |
二级缓存 | mCachedViews | 默认容量为 2 的 ArrayList<ViewHolder> | 保存最新移出屏幕的 ViewHolder(如快速滑动时滑出的视图) | 精准复用(匹配 position/id),容量可调(setItemViewCacheSize()),FIFO 淘汰策略 |
三级缓存 | ViewCacheExtension | 开发者通过 RecyclerView.setViewCacheExtension() 自定义实现 | 提供业务逻辑相关的缓存(如固定位置的特殊视图) | 灵活性高,但使用率低(多数场景无需自定义) |
四级缓存 | RecycledViewPool | 按 viewType 分组的 SparseArray<ArrayList<ViewHolder>>(默认每个 viewType 缓存 5 个) | 存放被废弃的 ViewHolder(其他缓存均未命中时使用) | 数据需重新绑定,容量可调(setMaxRecycledViews()),支持跨 RecyclerView 共享 |
示意图:

RecyclerView 实例。recyclerView.setLayoutManager(new LinearLayoutManager(this))。RecyclerView.Adapter 的类。recyclerView.setAdapter(new YourAdapter(dataList))。RecyclerView 通过 Adapter 的 getItemCount() 获取数据总量。LayoutManager 请求创建一个不可见的 Item View。Adapter 的 onCreateViewHolder 被调用,创建一个新的 ViewHolder(包含新创建的视图)。LayoutManager 将此 ViewHolder 的视图添加到屏幕上。Adapter 的 onBindViewHolder 被调用,将数据集中对应位置的数据填充到 ViewHolder 的视图组件中。ViewHolder 不会被销毁。RecyclerView 将它们放入缓存(Scrap Heap 和 Recycled Pool)。LayoutManager 会优先从缓存中获取一个 ViewHolder。onBindViewHolder 被调用,用新数据刷新这个重用的 ViewHolder 的视图内容。LayoutManager、ItemDecoration 和 ItemAnimator。RecyclerView.ItemDecoration 并重写 onDraw 和 getItemOffsets 方法。DefaultItemAnimator,也可以自定义。RecyclerView 的局部更新(如 notifyItemChanged, notifyItemInserted 等),避免 notifyDataSetChanged() 导致的全局刷新和性能损耗。强烈推荐在数据集变化较大时使用。RecyclerView 内部维护的 ViewHolder 缓存池,NestedRecyclerView 可以共享此池以优化性能。getItemViewType(int position) 方法,可以在同一个 RecyclerView 中显示不同类型的 Item(例如,聊天消息中的用户消息和系统消息)。onBindViewHolder 中做耗时操作: 如数据库查询、网络请求。应在绑定前准备好数据。DiffUtil 进行数据更新: 提升更新效率和用户体验。LayoutManager: 根据需求选择合适的布局管理器。ListAdapter: 它是 RecyclerView.Adapter 的子类,内置了 DiffUtil 的支持,简化了列表更新的代码。onViewRecycled 或 onViewDetachedFromWindow 中取消操作或清理资源。ViewPager 和 ViewPager2 是 Android 中用于实现页面滑动(左右或上下)功能的核心组件,常用于引导页、图片轮播、标签页切换等场景。ViewPager2 是 ViewPager 的现代化替代品,解决了其诸多限制并提供了更强大的功能。
ViewPager 是早期 Android 支持库(androidx.viewpager:1.x)中提供的组件。
PagerAdapter。Fragment 配合使用,通过 FragmentPagerAdapter 或 FragmentStatePagerAdapter 管理 Fragment 列表。notifyDataSetChanged() 可能导致不可预测的行为或崩溃,因为 PagerAdapter 的 getItemPosition() 方法处理逻辑复杂。RecyclerView.Adapter。Fragment 时,FragmentStatePagerAdapter 虽能销毁不可见的 Fragment,但仍有优化空间。ViewPager2 是在 ViewPager 基础上重构的现代化组件(androidx.viewpager2:1.x),基于 RecyclerView 构建,解决了 ViewPager 的大部分痛点。
setOrientation() 方法设置:ViewPager2.ORIENTATION_HORIZONTAL 或 ViewPager2.ORIENTATION_VERTICAL。RecyclerView 实现,继承了其优秀的性能、回收机制和扩展性。RecyclerView.Adapter 作为适配器。notifyDataSetChanged(): RecyclerView,数据集更新更加稳定和可预测。PageTransformer: Fragment 状态保存: FragmentStateAdapter 管理 Fragment,能更好地保存和恢复 Fragment 状态。OffscreenPageLimit 控制: ViewPager2: 主容器。RecyclerView.Adapter: 适配器,取代了 PagerAdapter。FragmentStateAdapter: 专为管理 Fragment 列表设计的适配器,是 RecyclerView.Adapter 的子类。它会自动处理 Fragment 的创建、销毁和状态保存,是 FragmentStatePagerAdapter 的现代化替代。PageTransformer: 用于自定义页面滑动时的动画效果。// Kotlin 示例
val viewPager2 = findViewById<ViewPager2>(R.id.viewPager2)
// 设置垂直滑动
viewPager2.orientation = ViewPager2.ORIENTATION_VERTICAL
// 使用 FragmentStateAdapter
class MyFragmentStateAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
override fun getItemCount(): Int = 3 // 页面数量
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> FirstFragment()
1 -> SecondFragment()
2 -> ThirdFragment()
else -> throw IllegalArgumentException("Invalid position")
}
}
}
viewPager2.adapter = MyFragmentStateAdapter(this)// Java 示例
ViewPager2 viewPager2 = findViewById(R.id.viewPager2);
viewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
viewPager2.setAdapter(new FragmentStateAdapter(this) {
@Override
public int getItemCount() {
return 3;
}
@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0: return new FirstFragment();
case 1: return new SecondFragment();
case 2: return new ThirdFragment();
default: throw new IllegalArgumentException("Invalid position");
}
}
});特性 | ViewPager | ViewPager2 |
|---|---|---|
方向 | 仅水平 | 水平 & 垂直 |
RTL 支持 | ❌ 不支持 | ✅ 支持 |
基础架构 | 自定义 | 基于 RecyclerView |
适配器 | PagerAdapter | RecyclerView.Adapter / FragmentStateAdapter |
notifyDataSetChanged() | 不稳定 | 稳定可靠 |
与 RecyclerView 集成 | 差 | 原生支持 |
Fragment 管理 | FragmentPagerAdapter / FragmentStatePagerAdapter | FragmentStateAdapter |
性能 | 一般 | 更优(得益于 RecyclerView) |
API 现代化 | 较旧 | 更现代、更简洁 |
依赖库 | androidx.viewpager:1.x | androidx.viewpager2:1.x |
ViewPager2 是 ViewPager 的全面升级版,解决了其历史遗留问题,并提供了更强大、更灵活的功能。对于新开发的 Android 应用,应优先选择 ViewPager2。
Handler 是 Android 中用于实现线程间通信和消息调度的核心机制。它主要用于将任务(Runnable)或消息(Message)从一个线程发送到另一个线程的消息队列中执行,最常见的是在子线程中处理耗时操作后,通过 Handler 将结果发送回主线程(UI线程)以更新界面。
Handler 机制是 Android 消息循环系统的基础,其核心由四个关键组件构成:Handler, Looper, MessageQueue, 和 Message。
what: 消息类型标识符,通常用于 Handler 区分不同消息。arg1, arg2: 可传递简单的整型数据。obj: 可传递任意对象(注意序列化和内存泄漏风险)。target: 指向处理该消息的 Handler(由 Looper 在分发时自动设置)。when: 消息的执行时间戳(毫秒)。Message.obtain(): 推荐,从消息池中获取,避免频繁创建对象。new Message(): 直接创建,不推荐,可能导致内存抖动。Message 的单链表结构队列。Message.when(执行时间)进行排序,时间越早的消息越靠前。Looper 负责从队列中取出消息 (next() 方法)。Looper 和 Handler 之间的数据桥梁。MessageQueue 中取出消息,并将消息分发给对应的 Handler 去处理。prepare(): 为当前线程准备一个 Looper。一个线程只能调用一次 prepare()。loop(): 启动消息循环。这是一个死循环,会持续调用 MessageQueue.next() 获取消息,并调用 msg.target.dispatchMessage(msg) 将消息分发给 Handler。Looper。Looper.prepareMainLooper() 和 Looper.loop(),因此主线程天生就具备消息循环能力。post(Runnable): 将 Runnable 作为消息发送。sendMessage(Message): 发送一个 Message 对象。postDelayed(Runnable, delay): 延迟发送。sendEmptyMessage(what): 发送一个只有 what 字段的空消息。handleMessage(Message msg) 方法来处理 Message。Runnable 会被封装在 Message 的 callback 字段中,Handler 会直接执行它。Looper 的线程中创建。通常在主线程创建的 Handler 用于更新 UI。Looper.prepareMainLooper() 和 Looper.loop()。Handler,必须手动调用 Looper.prepare() 创建 Looper,然后调用 Looper.loop() 启动循环。Handler 实例。Handler 会自动绑定到当前线程的 Looper 和 MessageQueue。handler.sendMessage(msg) 或 handler.post(runnable)。Handler 将 Message 插入到其绑定的 MessageQueue 的适当位置(按时间排序)。Looper 的 loop() 方法在死循环中调用 MessageQueue.next()。next() 方法会阻塞,直到有消息到达其执行时间或队列非空。Looper 从 MessageQueue 取出一个 Message。msg.target.dispatchMessage(msg)(target 是发送该消息的 Handler)。Handler 的 dispatchMessage 方法会: msg.callback 不为空(即 post 的 Runnable),则执行 Runnable.run()。Callback(通过构造函数或 setCallback),则调用 mCallback.handleMessage(msg)。handleMessage(msg) 方法(通常由子类重写)。graph TD
A[发送消息 handler.sendMessage(msg)] --> B[Message 加入 MessageQueue]
B --> C[Looper.loop() 循环]
C --> D[Looper 从 MessageQueue 取出 msg]
D --> E[Looper 调用 msg.target.dispatchMessage(msg)]
E --> F[Handler.dispatchMessage(msg)]
F --> G{msg.callback 是否为空?}
G -->|是| H[执行 Callback 或 handleMessage]
G -->|否| I[执行 Runnable.run()]原理:为每个线程提供独立的变量副本,确保 Looper 实例的线程隔离性。 在 Handler 中的应用:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); // 存储 Looper 到当前线程
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); // 获取当前线程的 Looper
}作用:支持优先级调度,确保关键任务(如 UI 渲染)优先执行。 实现:通过插入特殊消息(Message.setAsynchronous(true))拦截后续同步消息,处理完高优先级任务后再恢复。
核心原因:线程不安全与UI框架设计 Android 的 UI 框架采用单线程模型,所有界面操作(如视图绘制、布局计算、事件分发)必须在主线程(UI 线程)中执行。原因如下:
解决方案:通过 Handler、runOnUiThread() 或 View.post() 将 UI 更新操作切换到主线程。例如:
new Thread(() -> {
String result = doBackgroundWork(); // 子线程耗时操作
runOnUiThread(() -> textView.setText(result)); // 切换到主线程更新UI
}).start();创建时机: 主线程的 Looper 在 ActivityThread.main() 方法中初始化,流程如下:
步骤:
示例代码:
new Thread(() -> {
Looper.prepare(); // 初始化Looper
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("SubThread", "Received message: " + msg.what);
}
};
handler.sendEmptyMessage(1); // 发送消息
Looper.loop(); // 启动消息循环
}).start();注意事项:
内存泄漏原因:
示例代码(内存泄漏):
public class MainActivity extends Activity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText("Updated"); // 持有Activity引用
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.sendEmptyMessageDelayed(1, 1000); // 延迟消息可能导致泄漏
}
}解决方案: 静态内部类 + 弱引用:
示例代码(修复后):
public class MainActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> activityRef;
public MyHandler(MainActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityRef.get();
if (activity != null) {
activity.textView.setText("Updated"); // 安全访问UI
}
}
}
private final MyHandler handler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.sendEmptyMessageDelayed(1, 1000);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null); // 清理消息队列
}
}其他优化:
SharedPreferences (SP) 是 Android 原生的轻量级数据存储方案,而 MMKV 是由腾讯开源的基于 mmap (内存映射文件) 的高性能、跨平台键值对存储组件。两者都用于持久化简单配置数据,但底层实现和性能表现差异巨大。
> (基于 Android 原生 API)
/data/data/<package>/shared_prefs/)。apply() 或 commit() 时,将整个键值对集合序列化为 XML 字符串,然后覆盖写入磁盘文件。Editor 非线程安全,但框架内部有同步控制。// 获取实例
SharedPreferences sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
// 写入数据
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean("key_bool", true);
editor.putString("key_str", "value");
editor.apply(); // 推荐:异步提交
// editor.commit(); // 同步提交,阻塞线程
// 读取数据
boolean bool = sp.getBoolean("key_bool", false);
String str = sp.getString("key_str", "default");
// 监听变化
SharedPreferences.OnSharedPreferenceChangeListener listener = ...;
sp.registerOnSharedPreferenceChangeListener(listener);
// 别忘了 unregister!</code></pre>apply() 异步但仍有 I/O 开销;commit() 同步阻塞。每次写入都重写整个文件。MODE_MULTI_PROCESS 已废弃且不可靠,极易导致数据损坏。put 操作的原子性。(腾讯开源高性能 KV 存储)
1. 初始化 (Application.onCreate)
MMKV.initialize(this)
// 2. 获取实例
val kv = MMKV.defaultMMKV() // 默认实例
// val kv = MMKV.mmkvWithID("UserSettings", MMKV.MULTI_PROCESS_MODE) // 指定名称和模式
// 3. 写入数据 (所有操作均为内存级速度)
kv.encode("bool", true)
kv.encode("string", "Hello")
kv.encode("int", 123)
kv.encode("bytes", byteArrayOf(1, 2, 3))
// 4. 读取数据
val bool = kv.decodeBool("bool", false)
val string = kv.decodeString("string", "default")
val int = kv.decodeInt("int", -1)
// 5. 删除/清空
kv.removeValueForKey("key")
kv.clearAll()
// 6. 多进程模式 (安全可靠)
val multiKV = MMKV.mmkvWithID("MultiProcess", MMKV.MULTI_PROCESS_MODE)</code></pre>set() 即“写入”。mmap 机制保证写入过程崩溃也不会损坏原文件。特性 | SharedPreferences | MMKV |
|---|---|---|
底层原理 | XML 文件 I/O | ✅ mmap 内存映射 |
序列化格式 | 明文 XML | ✅ 二进制 Protobuf |
写入性能 | 慢 (I/O 瓶颈,全量写) | ✅✅✅ 极快 (内存操作,增量更新) |
读取性能 | 慢 (首次解析全文件) | ✅✅✅ 极快 (内存访问) |
多进程支持 | ❌ 不支持 / 不可靠 | ✅✅✅ 原生支持,安全可靠 |
数据加密 | ❌ 无 | ✅✅✅ 支持 AES-128-CBC |
数据完整性 | ❌ 无校验 | ✅✅✅ CRC64 校验 |
事务支持 | ❌ 无 | ⚠️ 有限支持 (begin/commitTransaction) |
跨平台 | ❌ Android 专用 | ✅✅✅ 全平台支持 |
数据大小限制 | 无硬限,大文件性能差 | 默认 1MB,可配置 |
调试友好性 | ✅ 明文,易读 | ❌ 二进制,需工具解析 |
API 复杂度 | ✅ 简单,原生 | ⚠️ 需引入库,API 类似 |
库大小 | 0 (系统) | ~50-100KB |
适用场景 | 简单配置、小量数据 | ✅ 高性能、多进程、加密、大数据 |
migrateFromSharedPreferences() API,可一键迁移旧 SP 数据。SQLite 是 Android 内置的轻量级、嵌入式关系型数据库。它无需独立的服务器进程,数据以文件形式存储在应用的私有目录中,非常适合在移动设备上存储结构化数据。Android 提供了 SQLiteOpenHelper 和 SQLiteDatabase 等 API 来方便地操作 SQLite 数据库。
onCreate(SQLiteDatabase db): 在数据库首次创建时调用。通常在这里执行 CREATE TABLE 等 DDL 语句。onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion): 当数据库版本号增加时调用。用于执行数据表结构的修改(如 ALTER TABLE)和数据迁移。onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion): 当版本号降低时调用(默认抛出异常,需重写)。onOpen(SQLiteDatabase db): 每次数据库打开时调用。Application 中初始化。getWritableDatabase(): 获取可读写的数据库实例。如果磁盘满,此方法可能抛出异常。getReadableDatabase(): 优先尝试获取可读写实例;如果失败(如磁盘满),则返回只读实例。推荐使用此方法。public class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "app.db";
private static final int DATABASE_VERSION = 1;
// 表名和列名建议定义为常量
public static final String TABLE_USERS = "users";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_EMAIL = "email";
// 创建表的 SQL 语句
private static final String CREATE_TABLE_USERS =
"CREATE TABLE " + TABLE_USERS + " (" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_NAME + " TEXT NOT NULL, " +
COLUMN_EMAIL + " TEXT UNIQUE" +
");";
public MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_USERS);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 版本升级时的处理逻辑
switch (oldVersion) {
case 1:
// 从版本 1 升级到 2
// db.execSQL("ALTER TABLE ...");
// break;
// case 2:
// ...
}
// 简单粗暴方式:删除旧表,创建新表(会丢失数据!)
// db.execSQL("DROP TABLE IF EXISTS " + TABLE_USERS);
// onCreate(db);
}
}</code></pre>SQLiteDatabase db = dbHelper.getWritableDatabase();
// 方法一:使用 ContentValues (推荐)
ContentValues values = new ContentValues();
values.put(MyDatabaseHelper.COLUMN_NAME, "Alice");
values.put(MyDatabaseHelper.COLUMN_EMAIL, "alice@example.com");
// insert 返回新记录的行 ID,失败返回 -1
long newRowId = db.insert(MyDatabaseHelper.TABLE_USERS, null, values);
// 方法二:使用 rawQuery 执行 INSERT SQL
// String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
// db.execSQL(sql, new Object[]{"Bob", "bob@example.com"});</code></pre>SQLiteDatabase db = dbHelper.getWritableDatabase();
// 删除条件
String selection = MyDatabaseHelper.COLUMN_NAME + " = ?";
String[] selectionArgs = {"Alice"};
// delete 返回删除的行数
int deletedRows = db.delete(MyDatabaseHelper.TABLE_USERS, selection, selectionArgs);
// 删除所有数据
// db.delete(TABLE_USERS, null, null);</code></pre>SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(MyDatabaseHelper.COLUMN_EMAIL, "alice_new@example.com");
String selection = MyDatabaseHelper.COLUMN_NAME + " = ?";
String[] selectionArgs = {"Alice"};
// update 返回更新的行数
int updatedRows = db.update(MyDatabaseHelper.TABLE_USERS, values, selection, selectionArgs);</code></pre>SQLiteDatabase db = dbHelper.getReadableDatabase();
// 查询条件
String[] projection = { // 要查询的列
MyDatabaseHelper.COLUMN_ID,
MyDatabaseHelper.COLUMN_NAME,
MyDatabaseHelper.COLUMN_EMAIL
};
String selection = MyDatabaseHelper.COLUMN_NAME + " LIKE ?";
String[] selectionArgs = {"%li%"};
String sortOrder = MyDatabaseHelper.COLUMN_NAME + " DESC";
// query 返回 Cursor
Cursor cursor = db.query(
MyDatabaseHelper.TABLE_USERS, // 表名
projection, // 列
selection, // 条件
selectionArgs, // 条件参数
null, // groupBy
null, // having
sortOrder // 排序
);
// 遍历结果
if (cursor != null && cursor.moveToFirst()) {
do {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MyDatabaseHelper.COLUMN_ID));
String name = cursor.getString(cursor.getColumnIndexOrThrow(MyDatabaseHelper.COLUMN_NAME));
String email = cursor.getString(cursor.getColumnIndexOrThrow(MyDatabaseHelper.COLUMN_EMAIL));
// 处理数据...
} while (cursor.moveToNext());
cursor.close(); // 必须关闭!
}对于需要保证原子性的操作(如转账),必须使用事务。
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction(); // 开始事务
try {
// 执行多个数据库操作
db.insert(...);
db.update(...);
// db.delete(...);
db.setTransactionSuccessful(); // 标记事务成功
} catch (Exception e) {
e.printStackTrace();
// 异常时,事务会自动回滚
} finally {
db.endTransaction(); // 结束事务
}</code></pre>ContentValues: 避免直接拼接 SQL 字符串,防止 SQL 注入。? 占位符和参数数组,是防止 SQL 注入的标准做法。Cursor 使用后必须调用 close(),否则会导致内存泄漏。推荐使用 try-with-resources (API 16+) 或在 finally 块中关闭。onUpgrade() 逻辑,避免简单粗暴地 DROP TABLE 导致用户数据丢失。AsyncTask、HandlerThread、ExecutorService 或现代方案 Room + Coroutine/LiveData。/data/data/<package_name>/databases/ 目录下。SQLiteOpenHelper 通常设计为单例,避免频繁创建和打开数据库连接。WHERE 条件中的列)创建索引,可大幅提升查询速度。GreenDAO 和 Room 都是 Android 平台上流行的 ORM (对象关系映射) 框架,旨在简化 SQLite 数据库的操作。它们将 Java/Kotlin 对象映射到数据库表,避免了编写大量重复的 SQL 语句。尽管目标相似,但两者在设计理念、性能、易用性和生态上存在显著差异。
DaoSession、DaoMaster 等概念。DaoSession 缓存层。同一个 DaoSession 中查询过的实体会被缓存,再次查询时直接从内存返回,显著提升查询效率。Entity (实体)、DAO (数据访问对象) 和 Database (数据库) 三个组件。EncryptedRoomDatabase。特性 | GreenDAO | Room |
|---|---|---|
开发者 | 第三方 | ✅ Google 官方 (Jetpack) |
核心优势 | ✅✅✅ 极致性能 (尤其批量操作) | ✅✅ 类型安全、编译时检查 |
学习曲线 | ⚠️ 较陡峭 | ✅ 较平缓 |
API 简洁性 | ⚠️ 生成类多,概念稍复杂 | ✅ 简洁,组件清晰 |
性能 (批量操作) | ✅✅✅ 最优 | ✅ 良好,但通常慢于 GreenDAO |
性能 (单条操作) | ✅ 优秀 | ✅ 优秀 |
缓存机制 | ✅✅ 有 DaoSession 缓存层 | ❌ 无内置实体缓存 |
编译时检查 | ⚠️ 有限 | ✅✅✅ SQL 语句编译时验证 |
与 LiveData 集成 | ⚠️ 需手动实现 | ✅✅✅ 无缝集成,自动通知 |
与 Kotlin 协程集成 | ⚠️ 需手动实现 | ✅✅✅ 原生支持 suspend 函数 |
与 RxJava 集成 | ⚠️ 需额外库 | ✅✅✅ 内置支持 |
测试支持 | ⚠️ 一般 | ✅✅ 良好,易于测试 |
数据库加密 | ✅ 原生支持 (集成) | ⚠️ 需额外集成 |
数据迁移 | ✅ 灵活 | ✅ 支持,需手动实现 |
Kotlin 友好度 | ⚠️ 支持 | ✅✅✅ 原生优先 |
生态与支持 | ⚠️ 第三方,社区支持 | ✅✅✅ 官方支持,长期维护,生态完善 |
适用场景 | 高性能数据采集、大量数据处理 | 中大型商业应用、注重开发效率和类型安全 |
DaoSession 的一级缓存来优化频繁查询的场景。框架 | 定位 |
|---|---|
GreenDAO | 性能怪兽:为速度而生,适合对性能有极致要求的场景。 |
Room | 官方亲儿子:平衡了性能、安全性和开发效率,是现代 Android 开发的推荐首选。 |
Android 提供了多种文件存储方式,用于保存应用数据。选择合适的存储方式对于应用的性能、安全性和用户体验至关重要。主要分为 内部存储 (Internal Storage) 和 外部存储 (External Storage) 两大类。
/data/data/<package_name>/files/)。openFileOutput() 和 openFileInput()这是最基础的文件读写方式。
// 写入文件
try {
String filename = "config.txt";
String content = "Hello, Internal Storage!";
// MODE_PRIVATE: 文件仅本应用可读写
FileOutputStream fos = openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
// 读取文件
try {
String filename = "config.txt";
FileInputStream fis = openFileInput(filename);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader bufferedReader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
fis.close();
String content = sb.toString();
} catch (IOException e) {
e.printStackTrace();
}getFilesDir() 和 getCacheDir()getFilesDir(): 返回应用私有文件目录的 File 对象 (/data/data/<package_name>/files/)。适合存储重要、不应被系统清理的文件。getCacheDir(): 返回应用私有缓存目录的 File 对象 (/data/data/<package_name>/cache/)。适合存储可再生的临时文件。当系统存储空间不足时,可能被系统自动清理。File filesDir = getFilesDir(); // /data/data/com.example.myapp/files
File cacheDir = getCacheDir(); // /data/data/com.example.myapp/cache
// 在 filesDir 下创建文件
File configFile = new File(filesDir, "settings.conf");
// ... 使用 FileOutputStream/FileInputStream 操作Download, Pictures, Music)或应用专属的外部目录中。/storage/emulated/0/Android/data/<package_name>/files/ 或 /storage/emulated/0/Android/data/<package_name>/cache/。WRITE_EXTERNAL_STORAGE 权限 (API 29+)。getExternalFilesDir(String type): 获取指定类型(如 Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC)的应用专属目录。传 null 获取根目录。getExternalCacheDir(): 获取应用专属的外部缓存目录。系统存储不足时可能被清理。File externalPicturesDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File photoFile = new File(externalPicturesDir, "photo.jpg");
// 获取外部缓存目录
File externalCacheDir = getExternalCacheDir();</code></pre>Downloads, DCIM/Camera, Music 等。用户和其他应用可以访问。READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。File API 访问公共目录受限。若必须使用,需在 AndroidManifest.xml 中声明 android:requestLegacyExternalStorage="true" (临时方案,非长久之计)。ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "my_image.jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyApp");
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (uri != null) {
try (OutputStream os = getContentResolver().openOutputStream(uri)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, os);
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}// 检查外部存储是否可读写
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// 可读写
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// 只读
} else {
// 不可用
}cacheDir,避免占用过多空间。try-with-resources 语句确保 InputStream、OutputStream、Cursor 等资源被正确关闭。Auto Backup 或 Key/Value Backup 服务。存储位置 | 访问方式 | 权限要求 (API 29+) | 卸载时是否删除 | 适用场景 |
|---|---|---|---|---|
内部存储 | openFileOutput, getFilesDir | 无 | ✅ 是 | 私有、敏感数据 |
内部缓存 | getCacheDir | 无 | ✅ 是 | 临时、可再生文件 |
外部专属 | getExternalFilesDir | 无 | ✅ 是 | 较大私有文件 (如离线地图) |
外部专属缓存 | getExternalCacheDir | 无 | ✅ 是 | 大型临时文件 |
公共目录 (旧) | File API | WRITE_EXTERNAL_STORAGE | ❌ 否 | (不推荐) |
公共目录 (新) | MediaStore, SAF | 通常无 | ❌ 否 | 用户共享内容、媒体文件 |
核心原则: 最小权限、数据隔离、遵守 Scoped Storage。根据数据的私密性、生命周期和共享需求,选择最合适的存储方案。
HTTP 协议基础(GET/POST 区别、状态码、Header)。 OkHttp 的核心架构(拦截器链 Interceptor)和使用。 Retrofit 的原理(动态代理、注解解析)和使用。 JSON 解析库(Gson, Moshi)的使用。
如何检测和分析内存泄漏?(工具:Android Profiler, LeakCanary) 如何避免内存抖动(频繁创建和回收对象)? 对象池 (Pools) 的使用场景。 Bitmap 的高效加载和内存管理(采样率压缩、复用、及时回收)。
如何减少布局层级?(使用 ConstraintLayout, , , ) 什么是过度绘制 (Overdraw)?如何检测和避免? onDraw() 中避免创建对象和执行耗时操作。
卡顿的根本原因是什么?(主线程执行耗时任务) 如何监控应用的帧率 (FPS)? 工具:Systrace, TraceView (已废弃,推荐使用 CPU Profiler)。
应用启动流程(Application, Activity)。 优化方案:异步初始化、延迟初始化、启动预加载、闪屏页优化。
代码混淆 (ProGuard/R8) 的作用。 资源压缩(移除无用资源、使用 WebP 格式、资源混淆)。 So 库的优化(按 ABI 打包)。 Lint 工具的使用。
1. 单例模式(Singleton)
定义:确保一个类只有一个实例,并提供全局访问点。
应用场景:
Application类本质是单例。示例代码:
class NetworkManager private constructor() {
companion object {
val instance: NetworkManager by lazy { NetworkManager() }
}
// 网络请求相关方法
}2. 工厂模式(Factory)
定义:定义一个创建对象的接口,由子类决定实例化哪个类。
应用场景:
BitmapFactory用于创建Bitmap对象。示例代码:
interface ViewFactory {
fun createView(): View
}
class TextViewFactory : ViewFactory {
override fun createView(): View = TextView(context)
}3. 建造者模式(Builder)
定义:分步骤构建复杂对象,隐藏创建细节。
应用场景:
AlertDialog.Builder。Request对象)。示例代码:
class AlertDialogBuilder {
private var title: String = ""
private var message: String = ""
fun setTitle(title: String): AlertDialogBuilder {
this.title = title
return this
}
fun setMessage(message: String): AlertDialogBuilder {
this.message = message
return this
}
fun build(): AlertDialog {
return AlertDialog(title, message)
}
}4. 适配器模式(Adapter)
定义:将一个接口转换为另一个接口,使不兼容的类可以协作。
应用场景:
RecyclerView.Adapter将数据转换为UI组件。示例代码:
class MyAdapter(private val dataList: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// 创建ViewHolder
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(dataList[position])
}
override fun getItemCount(): Int = dataList.size
}5. 代理模式(Proxy)
定义:为其他对象提供一种代理以控制对这个对象的访问。
应用场景:
Binder机制(AIDL实现跨进程通信)。示例代码:
class ImageLoaderProxy(private val realImageLoader: ImageLoader) : ImageLoader {
override fun loadImage(url: String) {
if (hasPermission()) {
realImageLoader.loadImage(url)
} else {
requestPermission()
}
}
}6. 组合模式(Composite)
定义:将对象组合成树形结构,表示“部分-整体”的层次结构。
应用场景:
View和ViewGroup层级关系。示例代码:
abstract class View {
abstract fun render()
}
class TextView(context: Context) : View() {
override fun render() { /* 渲染文本 */ }
}
class LinearLayout(context: Context) : View() {
private val children = mutableListOf<View>()
fun addView(view: View) { children.add(view) }
override fun render() {
for (child in children) {
child.render()
}
}
}7. 观察者模式(Observer)
定义:定义对象间的一对多依赖关系,当一个对象状态变化时,所有依赖者自动更新。
应用场景:
LiveData与ViewModel的协同工作。BroadcastReceiver)。示例代码:
class NewsSubject {
private val observers = mutableListOf<NewsObserver>()
fun addObserver(observer: NewsObserver) {
observers.add(observer)
}
fun removeObserver(observer: NewsObserver) {
observers.remove(observer)
}
fun notifyObservers() {
for (observer in observers) {
observer.update()
}
}
}
interface NewsObserver {
fun update()
}8. 策略模式(Strategy)
定义:定义一系列算法,将每个算法封装起来并使其可互换。
应用场景:
示例代码:
interface PaymentStrategy {
fun pay(amount: Double)
}
class AlipayStrategy : PaymentStrategy {
override fun pay(amount: Double) { /* 支付宝支付逻辑 */ }
}
class WeChatPayStrategy : PaymentStrategy {
override fun pay(amount: Double) { /* 微信支付逻辑 */ }
}
class PaymentContext(private var strategy: PaymentStrategy) {
fun executePayment(amount: Double) {
strategy.pay(amount)
}
}9. 责任链模式(Chain of Responsibility)
定义:将请求的发送者和接收者解耦,使多个对象都有机会处理请求。
应用场景:
onTouchEvent)。示例代码:
abstract class Handler {
private var successor: Handler? = null
fun setSuccessor(successor: Handler) {
this.successor = successor
}
abstract fun handleRequest(request: String)
}
class ConcreteHandler1 : Handler() {
override fun handleRequest(request: String) {
if (/* 条件满足 */) {
// 处理请求
} else {
successor?.handleRequest(request)
}
}
}1. MVC(Model-View-Controller)
2. MVP(Model-View-Presenter)
3. MVVM(Model-View-ViewModel)
DataBindingUtil)与View通信。Lifecycle 组件的原理和使用。 Navigation 组件的作用和优势。 WorkManager 的使用场景。
以下是根据你提供的提纲,针对 中高级至资深 Android 开发者岗位 的面试准备内容进行全面补充和深化。内容涵盖 技术原理、代码示例、系统级机制分析、设计思想与可读性优化,旨在帮助你构建一篇既具备技术深度又易于理解的高质量文档。
传统 IPC(如管道、Socket、共享内存)在 Android 上存在以下问题:
而 Binder 的优势:
特性 | 说明 |
|---|---|
高效 | 一次数据拷贝(通过 mmap 映射),传统 IPC 至少两次 |
安全 | 内核维护 UID/PID 权限验证,支持实名调用 |
面向对象 | 支持跨进程调用远程对象方法(类似 RPC) |
统一模型 | 所有系统服务(AMS、WMS、PMS)均基于 Binder |
关键点:Binder 是 Android IPC 的“基石”,它结合了 内核驱动 + 用户态代理 的设计,在效率、安全、抽象层次上达到了最佳平衡。
整个流程涉及四个核心角色:
[Client] [ServiceManager] [Server] [Binder Driver]
| | | |
|---- get(service) --->| | |
|<--- return handle ----| | |
| | |
|-------- transact() -------------------> (IPC) |
| |---- copy data ->|
| |<- wake up thread
| |--- onTransact() |
|<-------- reply() <----------------------- |Server 启动时注册服务
// 示例:ActivityManagerService 注册
ServiceManager.addService(Context.ACTIVITY_SERVICE, activityManager);ServiceManagerbinder_node 并加入全局哈希表Client 查询服务
IBinder binder = ServiceManager.getService(Context.ACTIVITY_SERVICE);
IActivityManager am = IActivityManager.Stub.asInterface(binder);Client 发起调用
am.startActivity(...); // 实际是 Proxy 调用 transact()Binder 驱动处理
binder_node,唤醒其等待线程Server 处理请求
onTransact() 被调用,反序列化参数,执行逻辑reply.write() 写回注意:Binder 使用 内存映射 (mmap) 实现零拷贝优化,仅需一次数据拷贝(从发送方用户空间 → 内核空间 → 接收方用户空间)。
定义 IUserManager.aidl:
// IUserManager.aidl
package com.example.aidl;
interface IUserManager {
User getUser(int id);
void addUser(in User user);
}生成的 Java 文件结构(由 AIDL 编译器生成):
public interface IUserManager extends android.os.IInterface {
public User getUser(int id) throws RemoteException;
public void addUser(User user) throws RemoteException;
public static abstract class Stub extends android.os.Binder implements IUserManager {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.IUserManager";
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) {
case TRANSACTION_getUser: {
data.enforceInterface(DESCRIPTOR);
int _id = data.readInt();
User _result = this.getUser(_id);
reply.writeNoException();
reply.writeTypedObject(_result, 0);
return true;
}
case TRANSACTION_addUser: {
data.enforceInterface(DESCRIPTOR);
User _arg0;
if (data.readInt() != 0) {
_arg0 = User.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addUser(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
static class Proxy implements IUserManager {
private IBinder mRemote;
public Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public User getUser(int id) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
User _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
_reply.readException();
_result = _reply.readTypedObject(User.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
}transact() 发起 IPC深入理解:AIDL 本质是 代理模式 + Binder 驱动 + 序列化 的组合拳,屏蔽了底层 IPC 细节。
Android 的 MessageQueue 底层依赖 Linux 的 epoll 多路复用机制实现阻塞等待。
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static int epoll_fd = -1;
static int wake_fd = -1;
void NativeMessageQueue::pollOnce(int timeoutMillis) {
// 调用 epoll_wait 等待事件
struct epoll_event event;
int result = epoll_wait(epoll_fd, &event, 1, timeoutMillis);
if (result > 0 && event.data.fd == wake_fd) {
// 被唤醒,说明有新消息插入
awoken();
}
}queue.next() 获取下一条消息nativePollOnce()epoll_wait() 阻塞等待,直到: enqueueMessage() 插入消息 → 触发 wake() → 写入 wake_pipe → epoll 返回MessageQueue.enqueueMessage() 插入紧急消息(msg.target != null)且当前是等待状态时,会调用 nativeWake() 向 pipe 写一个字节,触发 epoll 返回类比:就像一个快递站,没人来取件就打盹;一旦有新包裹送达,立刻响铃叫醒。
public static void loop() {
final Looper me = myLooper();
for (;;) { // 死循环
Message msg = queue.next(); // 可能阻塞
if (msg == null) return; // 退出循环
msg.target.dispatchMessage(msg); // 分发给 Handler
msg.recycle();
}
}queue.next() 会调用 nativePollOnce(timeout) 进入 休眠状态(底层 epoll_wait),不消耗 CPU当主线程正在处理某个消息(比如 onClick() 中执行耗时操作)超过 5 秒未返回,则系统认为“无响应”。
结论:
loop()的死循环 ≠ 卡顿。它是 Android UI 线程的“心脏”,驱动整个应用的消息流转。
Choreographer 是 Android 4.1 引入的核心类,用于同步屏幕刷新周期,实现 60fps 流畅渲染。
Choreographer.postFrameCallback(new FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
// 在下一个 VSYNC 信号到来时执行
// 可用于性能监控、自定义动画同步
}
});CALLBACK_INPUT:处理输入事件CALLBACK_ANIMATION:运行属性动画CALLBACK_TRAVERSAL:执行 measure/layout/draw⚙️ 所有 View 的
requestLayout()最终都会触发scheduleTraversals()→postCallback(CHOREOGRAPHER_CALLBACK_TRAVERSAL)
理想情况:每个 VSYNC 周期内完成一次完整的
measure → layout → draw,否则就会 掉帧。
从点击桌面图标到 Activity.onCreate() 被调用,经历了以下关键阶段:
[Launcher]
↓ 启动 Intent
[AMS]
↓ startProcessLocked()
[Zygote] fork()
↓ ActivityThread.main()
[ActivityThread] attach() → handleBindApplication() → onCreate()Launcher 发送启动请求
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);AMS 处理启动请求
Zygote 进程 fork 子进程
ZygoteInit.preload()),加快启动速度forkAndSpecialize() 创建新进程新进程执行 ActivityThread.main()
public static void main(String[] args) {
Looper.prepareMainLooper(); // 创建主线程 Looper
ActivityThread thread = new ActivityThread();
thread.attach(false); // 向 AMS 注册
Looper.loop(); // 开启消息循环
}AMS 回调 applicationOnCreate
makeApplication() → attachBaseContext() → onCreate()performLaunchActivity() → new Activity() → onCreate(savedInstanceState)冷启动关键路径:
PackageManager→Launcher→AMS→Zygote→Binder→ActivityThread→Application.onCreate()→Activity.onCreate()
类型 | 超时时间 | 触发场景 |
|---|---|---|
KeyDispatchTimeout | 5s | 主线程未处理完输入事件(如 onClick) |
BroadcastTimeout | 10s(前台)/ 60s(后台) | BroadcastReceiver 执行过长 |
ServiceTimeout | 20s | Service.onStartCommand() 未返回 |
注意:ANR 判断基于 主线程是否在规定时间内响应特定事件,与是否“卡顿”不完全等价。
获取 traces.txt
adb shell cat /data/anr/traces.txt > anr.log查看主线程堆栈
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 obj=0x74f8e000 self=0xb4f00500
| sysTid=28884 nice=0 cgrp=default sched=0/0 handle=0xb6f76534
| state=S schedstat=( 0 0 0 ) utm=4 stm=2 core=0 HZ=100
| stack=0xbe77d000-0xbe77f000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep(Native method)
at com.example.MyActivity.onCreate(MyActivity.java:30)
at android.app.Activity.performCreate(Activity.java:7136)发现主线程在 onCreate 中 sleep,导致 ANR。
检查 CPU 使用率
dumpsys cpuinfo 对比时间戳使用工具辅助
最佳实践:避免在主线程做任何耗时操作,使用
HandlerThread、IntentService或协程进行异步处理。
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int id);
}
// 使用
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService service = retrofit.create(ApiService.class);
Call<User> call = service.getUser(1);
call.enqueue(...);动态代理:retrofit.create(ApiService.class) 返回的是一个代理对象
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[]{service},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 将方法调用封装为 OkHttp Call
ServiceMethod<Object, Object> serviceMethod = loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});注解解析:ServiceMethod 解析 @GET、@Path、@Query 等注解,构建 Request 对象
适配器模式:支持 Call、Observable(RxJava)、LiveData 等返回类型
转换器模式:Converter<F, T> 实现 JSON ↔ Object 转换(如 Gson)
一句话总结:Retrofit 是一个 声明式 REST 客户端,通过 动态代理 + 注解处理器 + 拦截链 将 Java 接口映射为 HTTP 请求。
OkHttp 使用 责任链模式(Interceptor Chain) 处理请求。
[Application Interceptors]
↓
[RetryAndFollowUpInterceptor] // 重试、重定向
↓
[BridgeInterceptor] // 添加 Host、Cookie、gzip 支持
↓
[CacheInterceptor] // 读取/写入缓存
↓
[ConnectInterceptor] // 建立 TCP 连接(RealConnection)
↓
[CallServerInterceptor] // 向服务器写请求、读响应
↓
[Response]class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
Log.d("OkHttp", "Sending request: " + request.url());
Response response = chain.proceed(request); // 继续执行后续拦截器
long endTime = System.nanoTime();
Log.d("OkHttp", "Received response in " + (endTime - startTime) / 1e6 + "ms");
return response;
}
}优势:灵活、可插拔、便于调试和扩展。
Glide.with(context).load(url).into(imageView);url、transformations、placeholder 等ActiveResources(活跃资源)和 MemoryCacheDiskLruCache(基于 LRU)层级 | 作用 | 实现 |
|---|---|---|
Memory Cache | 快速访问已解码的 Bitmap | LruResourceCache |
Active Resources | 正在使用的图片(防止被回收) | 弱引用 HashMap |
Disk Cache | 持久化缓存原始数据或转换后结果 | DiskLruCache |
关键优化:
BitmapPool 复用 Bitmap 内存.thumbnail() 实现渐进式加载// 注册
EventBus.getDefault().register(this);
// 发布
EventBus.getDefault().post(new MessageEvent("Hello"));
// 接收
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
textView.setText(event.message);
}注解处理:编译期扫描 @Subscribe 方法,生成索引类(提高查找效率)
订阅者注册:
Map<Object, List<SubscriberMethod>> subscriptions;key 是订阅者对象,value 是方法列表
事件分发:
post(event) → 查找所有订阅了该 event 类型的方法threadMode 决定执行线程(POSTING、MAIN、IO、ASYNC)线程切换:使用 Handler 或线程池实现
缺点:过度使用会导致“隐式调用”难以追踪,建议局部使用。
特性 | lateinit | by lazy |
|---|---|---|
初始化时机 | 运行时手动赋值 | 第一次访问时初始化 |
类型限制 | 只能用于 var,非空引用类型 | 任意类型(val) |
是否可变 | 可变(var) | 不可变(val) |
线程安全 | 否 | LazyThreadSafetyMode.SYNCHRONIZED 默认安全 |
适用场景 | DI 注入、View Binding | 单例、耗时初始化 |
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding // 在 onCreate 中初始化
val userManager: UserManager by lazy {
UserManager.getInstance(applicationContext) // 第一次用时创建
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}建议:优先使用
by lazy,除非必须用var且延迟初始化。
data class User(val name: String, val age: Int)编译器自动添加:
equals() / hashCode()toString() → "User(name=John, age=25)"copy() → user.copy(age = 30)componentN() → 解构语法 val (name, age) = user用于 数据载体类,减少模板代码。
// 扩展函数
fun String.isValidEmail(): Boolean {
return Patterns.EMAIL_ADDRESS.matcher(this).matches()
}
// 扩展属性
val Context.appName: String
get() = getString(R.string.app_name)提高代码可读性和复用性,类似“工具类”的优雅写法。
特性 | enum | sealed class |
|---|---|---|
实例数量 | 固定 | 子类固定,但实例可变 |
数据携带 | 不能携带不同数据 | 每个子类可携带不同参数 |
继承范围 | 同文件 | 同模块(Kotlin 1.1+) |
// 枚举:状态有限且无数据
enum class Status { LOADING, SUCCESS, ERROR }
// 密封类:更灵活的数据表达
sealed class Result<out T> {
object Loading : Result<Nothing>()
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
// 使用
when (result) {
is Result.Loading -> showLoading()
is Result.Success -> updateUI(result.data)
is Result.Error -> showError(result.exception)
}推荐:网络请求结果、UI 状态管理使用
sealed class。
lifecycleScope.launch {
val user = fetchUser() // suspend 函数
textView.text = user.name
}
suspend fun fetchUser(): User {
return withContext(Dispatchers.IO) {
api.getUser() // 耗时操作
}
}suspend 函数不是线程阻塞,而是 协作式挂起Continuation 保存执行上下文resume() 恢复执行// 伪代码:编译器生成的状态机
int label = 0;
Object result = null;
while (true) {
switch (label) {
case 0:
label = 1;
return suspendFun(continuation); // 挂起点
case 1:
result = (User) result; // 恢复后继续执行
return result.name;
}
}优势:用同步写法实现异步逻辑,避免回调地狱。
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() => count++),
child: Text('Count: $count'),
);
}
}声明式 UI:每次
setState()都会重建 Widget 树,由框架决定最小更新范围。
对比项 | 传统 View 系统(命令式) | Jetpack Compose(声明式) |
|---|---|---|
更新方式 | findViewById().setText() | 重新调用 @Composable 函数 |
状态管理 | 手动更新视图 | 状态驱动 UI 重建 |
性能优化 | 手动 diff | 智能重组(Recomposition) |
代码结构 | XML + Java/Kotlin | 纯 Kotlin |
// 命令式
button.setOnClickListener { textView.text = "Clicked!" }
// 声明式
@Composable
fun ClickCounter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}优势:更简洁、更易测试、更好支持动态 UI。
回答模板(STAR 法则):
“我主导了一个电商 App 的重构项目。原项目耦合严重,启动耗时达 3.2s。我们采用 模块化 + 启动器优化 + 冷启动预加载 方案,最终将冷启动时间降至 1.1s,用户留存提升 18%。”
ImageLoader.with(context).load(url).into(iv)参考 Glide 架构,但简化依赖。
支持同步/异步、支持 Kotlin 协程挂起函数。
object UserManager {
private val _isLoggedIn = MutableLiveData<Boolean>()
val isLoggedIn: LiveData<Boolean> = _isLoggedIn
fun login(token: String) {
// 保存 token
SharedPreferences.save("token", token)
_isLoggedIn.value = true
}
fun logout() {
SharedPreferences.clear()
_isLoggedIn.value = false
}
}推荐结合 Jetpack 架构组件,实现跨页面状态同步。
示例回答: “我主要通过官方文档 + GitHub 源码 + 技术博客(如 Medium、掘金)学习。每周会安排固定时间阅读 Android Developers Blog 和 Kotlin Weekly。对于重点技术(如 Compose),我会动手写 Demo 并分享到博客。”
示例回答: “我认为 Android 正在向 声明式 UI(Compose)、跨平台(Kotlin Multiplatform)、模块化与动态化 演进。随着 AI 和大模型的发展,本地智能推理、个性化推荐也会成为新方向。同时,隐私安全和后台限制将持续加强。”
建议提问: