当前位置: 首页 > news >正文

2025 Android 知识体系总结(含面试要点,持续补充,更新中...)

文章目录

  • 一、Java 基础与进阶
    • 1. 面向对象 (OOP)
      • 1.1 面向对象的三大特性
        • 1.封装
        • 2.继承 (Inheritance)
        • 3.多态 (Polymorphism)
        • 4.总结
      • 1.2 接口和抽象类
        • 1.抽象类 (Abstract Class):
        • 2. 接口 (Interface):
        • 3.总结
      • 1.3 内部类
        • 1.分类
        • 2.核心作用与优势
        • 3.使用内部类的注意事项
    • 2. 集合框架
      • 2.1 ArryList和linkedList
        • 1.核心区别:底层数据结构
        • 2. 性能对比 (时间复杂度)
        • 3. 内存占用对比
        • 4. 特有功能
      • 2.2 HashMap 和HashTable
        • 1.核心区别概览
        • 2.深入对比详解
          • 1. 线程安全性与性能
          • 2. 对 Null 键和 Null 值的支持
          • 3. 继承关系与历史
          • 4. 迭代器 (Iterator)
          • 5. 初始容量与扩容机制
          • 6. 哈希值计算
          • 7. 底层数据结构演进 (JDK 8+)
      • 2.3 String,StringBuilder,StringBuffer
        • 1.运行速度
        • 2.线程安全
        • 3. 总结一下
    • 3. 并发与多线程
      • 3.1 线程
      • 3.2 线程同步
      • 3.2 线程池
        • 1.线程池的核心优势
        • 2.核心组件与工作原理
          • 核心组件
          • 工作流程
        • 2.ThreadPoolExecutor 核心参数详解
        • 3.线程池最佳实践
    • 4. JVM 相关
      • 4.1 JVM 的核心作用
        • 1. 跨平台性
        • 2. 字节码执行引擎
        • 3. 自动内存管理
        • 4. 安全性
        • 5. 多线程支持
        • 6. 动态类加载
      • 4.2 JVM 的内部结构(运行时数据区)
        • 1. 程序计数器 (Program Counter Register)
        • 2. Java 虚拟机栈 (Java Virtual Machine Stack)
        • 3. 本地方法栈 (Native Method Stack)
        • 4. Java 堆 (Java Heap)
        • 5. 方法区 (Method Area) / 元空间 (Metaspace)
        • 6. 运行时常量池 (Runtime Constant Pool)
        • 7. 直接内存 (Direct Memory)
    • 5. 泛型与反射
      • 5.1Java 泛型 (Generics)
      • 5.2 Java 反射 (Reflection)
      • 5.3 特性对比总结
    • 6. java 类双亲委托机制
      • 6.1类加载器的层级结构
        • 1. 启动类加载器 (Bootstrap ClassLoader)
        • 2. 扩展类加载器 (Extension ClassLoader)
        • 3. 应用程序类加载器 (Application ClassLoader)
        • 4. 自定义类加载器 (Custom ClassLoader)
      • 6.2 双亲委派的工作流程
      • 6.3 为什么需要双亲委派机制?
        • 1. 保障核心类库的安全性 (Security)
        • 2. 避免类的重复加载 (Avoid Duplicate Loading)
        • 3. 保证类加载的一致性 (Consistency)
  • 二、 Android 核心组件与机制
    • 1. 四大组件
      • 1.1 Activity:
        • 1. 核心作用
        • 2. 生命周期方法
        • 3.使用注意
      • 1.1 Service:
        • 1. 核心作用
        • 2 IntentService
        • 3. 前台 Service
        • 4. JobScheduler 和 WorkManager
      • 1.1 BroadcastReceiver:
      • 1.1 ContentProvider:
    • 2. View 体系与事件分发
    • 3. 异步与多线程
    • 4. 数据存储
    • 5. 网络编程
  • 三、性能优化
    • 1. 内存优化
    • 2. 布局优化
    • 3. 卡顿优化
    • 4. 启动优化
    • 5. APK 瘦身
  • 四、架构与设计模式
    • 1. 常用设计模式
    • 2. 应用架构
    • 3. Jetpack 组件
  • 五、Framework 与原理
    • 1. Binder 机制
    • 2. Handler 消息机制深入
    • 3. 应用启动流程
    • 4. ANR (Application Not Responding)
  • 六、开源框架与新技术
    • 1. 常用框架原理
    • 2. Kotlin
    • 3. Flutter / Compose
  • 七、场景题与开放性问题
    • 1. 项目相关
    • 2. 设计题
    • 3. 开放性问题

一、Java 基础与进阶

1. 面向对象 (OOP)

1.1 面向对象的三大特性

1.封装

概念

  • 将对象的**数据(属性)和操作数据的方法(行为)**捆绑在一起,并对外部隐藏对象内部实现的细节。它通过访问控制符(如 private,
    protected, public)来限制对内部数据的直接访问,通常提供公共的 getter 和 setter 方法来间接操作数据。

作用

  • 数据安全:防止外部代码随意修改对象的内部状态,保证数据的完整性和一致性。
  • 降低耦合:使用者只需知道如何调用公共接口,无需关心内部实现,当内部实现改变时,只要接口不变,外部代码就不需要修改。
  • 提高内聚:将相关的数据和方法组织在一个类中,使类的职责更单一、更清晰。
2.继承 (Inheritance)

概念

  • 继承允许一个类(子类/派生类)基于另一个类(父类/基类)来创建。子类会自动拥有父类的所有非私有(private)成员(属性和方法),并可以添加自己特有的成员,或重写
    (Override) 父类的方法以提供不同的实现

作用

  • 代码复用:避免重复编写相同的代码,子类可以直接使用父类已有的功能。
  • 建立层次关系:通过“is-a”关系对现实世界进行建模,使代码结构更符合人类思维。
  • 扩展功能:在不修改父类代码的前提下,通过子类增加新功能或修改现有功能。
3.多态 (Polymorphism)

概念

  • 多态是指同一个接口或父类引用,可以指向不同子类的对象,并在运行时调用实际对象所属类的方法。简单说,就是“一个接口,多种实现”。多态的核心是动态绑定 (Dynamic Binding) 或后期绑定 (LateBinding),即方法调用的决定是在程序运行时根据对象的实际类型做出的,而不是在编译时。

作用

  • 提高灵活性和可扩展性:代码可以编写得更通用,不依赖于具体的实现类。新增子类时,无需修改使用父类引用的代码。
  • 解耦:调用者与具体实现者分离,降低了模块间的依赖。
4.总结
特性核心思想Android引用举例作用
封装隐藏实现,暴露接口Activity 生命周期、自定义 View、数据模型类安全、解耦、易维护
继承代码复用,建立层次创建 Activity/Fragment、自定义 View、BaseActivity复用、扩展、结构化
多态一个接口,多种实现View 事件分发、Adapter、回调接口、设计模式灵活、可扩展、高内聚低耦合

这三大特性相辅相成,共同构成了 Android 应用健壮、灵活、可维护的代码基础。熟练掌握并灵活运用它们,是成为一名优秀 Android 开发者的必经之路。

1.2 接口和抽象类

1.抽象类 (Abstract Class):
  • 概念 一个被 abstract 关键字修饰的类,它可以包含抽象方法(无实现)和具体方法(有实现),也可以包含成员变量(实例变量、静态变量等)。
  • 设计意图:“是什么” (IS-A)。它代表一个不完整的、通用的基类,用于捕捉子类的共同特征(属性和行为),并为子类提供部分默认实现。抽象类强调的是类的继承关系和代码复用。
  • 举例:Animal(动物)可以是一个抽象类,它定义了所有动物共有的属性(如 name)和行为(如 sleep() 的默认实现),以及必须由子类具体实现的抽象行为(如 makeSound())。
  • 抽象类的核心作用
    1.代码复用:为子类提供公共的属性和方法实现,避免重复代码。
    2.强制规范:通过抽象方法强制要求子类实现特定功能。
    3.定义模板:结合具体方法和抽象方法,可以实现模板方法模式,定义算法的骨架,将某些步骤延迟到子类中实现。
2. 接口 (Interface):
  • 概念:一个被 interface 关键字修饰的完全抽象的“契约”或“协议”。在 Java 8 之前,它只能包含常量和抽象方法;Java 8 及以后,可以包含默认方法(default)和静态方法(static)。
  • 设计意图:“能做什么” (HAS-A / CAN-DO)。它定义了一组行为规范,规定了实现该接口的类必须具备哪些能力或方法,而不关心这些类的内部状态或具体实现细节。接口强调的是功能的定义和解耦。
  • 举例:Flyable(可飞行的)、Runnable(可运行的)、Comparable(可比较的)。一个 Bird 类和一个 Airplane 类可以毫无继承关系,但它们都可以实现 Flyable 接口,表明它们都“能飞”。
  • 接口的核心作用
    1.实现“多继承”:这是接口最独特的优势。Java 类不支持多继承,但一个类可以实现多个接口,从而获得多种能力。
    2.定义契约/规范:明确地规定了实现类必须提供的方法,是团队协作和模块化开发的基础。
    3.解耦与依赖倒置:客户端代码依赖于接口,而不是具体的实现类,大大降低了模块间的耦合度,提高了系统的灵活性和可维护性。
    4.支持多态性:可以通过接口类型的引用来调用不同实现类的对象,实现运行时的动态绑定。
    5.便于测试与扩展:易于编写 Mock 对象进行单元测试;添加新功能时,可以定义新接口,而不影响现有代码。
3.总结
接口抽象类
实现子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现子类使用extends关键字继承抽象类。子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现
构造器接口不能有构造器抽象类可以有构造器
与正常J类的区别接口是完全不同的类型除了你不能实例化抽象类之外,它和普通Java类没有任何区别
访问修饰符接口方法默认修饰符是public,不可以使用其它修饰符抽象方法可以有public、protected和default这些修饰符
main方法方法 接口没有main方法,因此我们不能运行它抽象方法可以有main方法并且我们可以运行它
继承与实现可以实现多个接口只能继承一个抽象类
速度接口是稍微有点慢的,它需要时间去寻找在类中实现的方法速度较快
添加新方法接口中添加方法,那么你必须改变实现该接口的类抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码

1.3 内部类

1.分类

Java 内部类是一个功能强大且灵活的特性,它通过提供更细粒度的封装和组织方式,帮助开发者构建更优雅、更健壮的代码。理解四种内部类的区别和适用场景是关键:

  • 成员内部类:需要紧密访问外部类实例成员时使用。
  • 静态内部类:逻辑相关但不需要访问外部类实例成员时使用,是避免内存泄漏的首选。
  • 局部内部类:在单个方法内需要定义一个较复杂的、可能被多次实例化的类时使用。
  • 匿名内部类:用于快速实现接口或继承类,创建一次性使用的对象,尤其在事件监听和回调中应用广泛
2.核心作用与优势
  • 增强封装性 (Enhanced Encapsulation)
    可以将内部类设为 private,完全对外部世界隐藏。外部类可以控制哪些内部类对外暴露
    内部类可以自由访问外部类的私有成员,使得它们能紧密协作,同时保持外部类成员对其他外部类的封装性。
  • 实现“多重继承”的效果
    Java 不支持类的多继承,但内部类可以独立地继承一个类或实现多个接口,而外部类可以继承另一个类。这在一定程度上模拟了多继承。
  • 解决命名冲突
    当外部类继承的父类和实现的接口中有同名方法时,可以通过在内部类中实现接口,从而避免冲突。
  • 实现回调和事件处理 (Callback & Event Handling)
    匿名内部类是实现事件监听器和回调机制的首选方式,代码简洁且能直接访问外部类状态。
  • 代码组织与逻辑内聚 (Code Organization & Cohesion)
    将逻辑上紧密相关的类放在一个外部类中,使代码结构更清晰,减少顶层类的数量,提高可读性和可维护性。
  • 设计模式支持
    方便实现如工厂模式、策略模式、观察者模式等。例如,静态内部类常用于单例模式;成员内部类可用于实现观察者。
3.使用内部类的注意事项
  • 内存泄漏风险 (Memory Leak)
    如果内部类对象的生命周期比外部类长(例如,内部类对象被传递给一个长生命周期的对象,如静态变量、线程池、Handler 等),就会导致外部类对象无法被垃圾回收,造成内存泄漏。可以通过 WeakReference 来持有外部类的弱引用
  • 序列化问题
    非静态内部类默认持有外部类引用,如果尝试序列化非静态内部类,也会尝试序列化其外部类。如果外部类没有实现 Serializable,则会抛出异常。通常,内部类很少需要被序列化
  • 代码复杂性
    过度使用内部类,尤其是嵌套多层的内部类,会使代码结构变得复杂,难以理解和调试。应遵循“简单优于复杂”的原则

2. 集合框架

2.1 ArryList和linkedList

1.核心区别:底层数据结构

ArrayList

  • 底层结构:基于动态数组(Resizable Array)实现。
  • 内存布局:元素在内存中连续存储,就像一个可以自动扩容的数组。
  • 扩容机制:当元素数量超过当前数组容量时,会创建一个更大的新数组(通常是原容量的1.5倍),并将所有旧元素复制过去。

LinkedList

  • 底层结构:基于双向链表(Doubly Linked List)实现。
  • 内存布局:每个元素(称为节点
    Node)包含三部分:数据本身、指向前一个节点的引用(prev)和指向后一个节点的引用(next)。节点在内存中分散存储,通过引用来连接。
  • 扩容机制:无需扩容。添加新元素时,只需创建一个新节点并调整相邻节点的引用即可。
2. 性能对比 (时间复杂度)
操作ArrayListLinkedList说明
随机访问 (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 的节点分散,缓存命中率低
3. 内存占用对比

ArrayList

  • 内存开销较小:主要存储元素数据本身。虽然数组可能有未使用的预留空间(为了减少扩容频率),但总体开销相对紧凑。
  • 内存布局连续:有利于缓存性能。

LinkedList

  • 内存开销较大:每个节点除了存储元素数据,还需要额外存储两个引用(prev 和 next),这在存储大量小对象时,内存开销会显著增加。
  • 内存布局分散:不利于缓存性能。
4. 特有功能

LinkedList

除了实现 List 接口,还实现了 Deque (Double-ended Queue) 接口,因此,它提供了一系列针对头尾操作的高效方法,使其可以方便地用作栈(Stack)、队列(Queue)或双端队列(Deque)。

  • addFirst(E e) / addLast(E e)
  • getFirst() / getLast()
  • removeFirst() / removeLast()
  • offerFirst(E e) / offerLast(E e) (JDK 1.6+,空集合时返回 null 而非异常)
  • peekFirst() / peekLast()
  • pollFirst() / pollLast()

ArrayList

没有针对头尾操作的特殊优化方法,所有位置的操作都通过通用的 add(index, E e) 和 remove(int index) 等方法实现。

2.2 HashMap 和HashTable

1.核心区别概览
特性HashMapHashtable
线程安全❌ 非线程安全。在多线程环境下使用可能导致数据不一致或死循环。✅ 线程安全。所有公共方法都用 synchronized 关键字修饰,同一时间只允许一个线程访问。
性能⚡ 性能高。因为没有同步开销,在单线程环境下速度远超 Hashtable。🐢 性能低。由于每个方法都加锁,导致并发性能极差。
Null 值支持✅ 允许 null。允许一个 null 键和任意多个 null 值。❌ 不允许 null。键或值为 null 时会抛出 NullPointerException。
出现时间JDK 1.2 引入。JDK 1.0 就已存在,是早期的遗留类。
继承关系继承自 AbstractMap 类。继承自已废弃的 Dictionary 类。
推荐替代方案在需要线程安全时,推荐使用 ConcurrentHashMap。无。官方不推荐在新代码中使用。
2.深入对比详解
1. 线程安全性与性能

Hashtable

  • 实现方式:通过在每个公共方法(如 put(), get(), remove())上添加 synchronized 关键字来实现线程安全。
  • 缺点:这是一种全局锁机制。当一个线程在访问 Hashtable 时,会锁住整个对象,其他所有线程都必须等待,导致并发性能极低。
  • 适用场景:仅适用于对性能要求不高、且需要简单线程安全的极少数遗留场景。

HashMap

  • 实现方式:没有任何同步机制,追求极致的单线程性能。
  • 多线程风险
    • 数据不一致:多个线程同时写入可能导致数据丢失或覆盖。
    • 死循环(JDK 1.7 及以前):在并发扩容时,链表可能形成环形结构,导致 get() 操作陷入无限循环。
    • 快速失败 (Fail-Fast):其迭代器是 fail-fast 的。在迭代过程中,如果其他线程修改了 HashMap 的结构(如添加、删除元素),会抛出 ConcurrentModificationException 异常,这是一种安全预警机制。
  • 解决方案
    • 外部同步:使用 Collections.synchronizedMap(new HashMap<...>()) 包装,但这同样是全局锁,性能与 Hashtable 类似。
    • 推荐方案:直接使用 ConcurrentHashMap。它采用分段锁(JDK 7)或 CAS + synchronized(JDK 8+)等更精细的并发控制策略,在保证线程安全的同时,大幅提升了并发性能。
2. 对 Null 键和 Null 值的支持

HashMap

  • 设计上允许 null 作为键或值。
  • null 键会被特殊处理,存储在哈希表数组的第 0 个位置(table[0])。
  • 允许存在多个 null 值。
  • 这为编程提供了更大的灵活性。

Hashtable

  • 严格禁止 null 键或 null 值。
  • putget 方法内部会进行 null 检查,一旦发现 null,立即抛出 NullPointerException
  • 这种设计源于其早期的实现和对“键必须有效”的严格要求。
3. 继承关系与历史

Hashtable

  • 继承自 Dictionary 类。Dictionary 是一个非常古老的抽象类,在现代 Java 中已被标记为 @Deprecated(不推荐使用)。
  • 是 Java 集合框架出现之前的产物。

HashMap

  • 继承自 AbstractMap 类,这是 Java 集合框架(JCF)中为 Map 实现提供的标准抽象基类。
  • 是作为 Hashtable 的现代、高性能替代品被引入的。
4. 迭代器 (Iterator)

HashMap

  • 使用 Iterator 进行遍历。
  • Iterator 是 fail-fast 的。如上所述,在迭代过程中检测到结构修改会立即失败并抛出异常,有助于快速发现问题。

Hashtable

  • 除了支持 Iterator,还保留了旧的 Enumeration 接口(通过 elements()keys() 方法获取)。
  • Iterator 不是 fail-fast 的,在迭代过程中即使结构被修改,也可能不会抛出异常,导致结果不可预测。
5. 初始容量与扩容机制
特性HashMapHashtable
默认初始容量1611
容量要求必须是 2 的幂。这使得在计算元素存储位置时,可以用位运算 (&) 替代取模运算 (%),效率更高。无特殊要求,可以是任意整数。
扩容方式容量变为原来的 2 倍。容量变为原来的 2 倍 + 1(即 old * 2 + 1)。这种设计旨在使容量尽量为奇数或质数,以期在使用取模运算时减少哈希冲突。
6. 哈希值计算

Hashtable

  • 直接使用对象的 hashCode() 方法返回的值。
  • 计算存储位置:index = (hashCode & 0x7FFFFFFF) % capacity

HashMap

  • hashCode() 的结果进行了一次扰动函数(hash function)处理,目的是让哈希值的高位也参与到低位的计算中,使哈希值分布更均匀,减少冲突。
  • 计算存储位置:index = hash & (capacity - 1)(得益于容量是 2 的幂,位运算效率极高)。
7. 底层数据结构演进 (JDK 8+)

HashMap

  • 在 JDK 8 中进行了重大优化。当链表长度超过阈值(默认 8)且数组长度大于 64 时,链表会转换为红黑树。这使得在极端哈希冲突情况下,查询时间复杂度从 O(n) 降低到 O(log n),性能更稳定。

Hashtable

  • 底层始终是数组 + 链表结构,没有引入红黑树优化。在发生严重哈希冲突时,性能会急剧下降。

2.3 String,StringBuilder,StringBuffer

String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下,便于大家观看,也便于加深自己学习过程中对这些知识点的记忆,如果哪里有误,恳请指正。这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。

1.运行速度

StringBuilder > StringBuffer > String

  • String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。

  // 以下面一段代码为例:  String str="abc";System.out.println(str);str=str+"de";System.out.println(str);
  • 如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了。
  • 因此,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
  • .StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。另外,有时候我们会这样对字符串进行赋值
 String str="abc"+"de";StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");System.out.println(str);System.out.println(stringBuilder.toString());
  • 这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和 String str=“abcde”;是完全一样的,所以会很快,而如果写成下面这种形式那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。
 String str1="abc";String str2="de";String str=str1+str2;
2.线程安全

StringBuilder是线程不安全的,而StringBuffer是线程安全的

  1. 如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。
  2. 因此要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
3. 总结一下
  • String:适用于少量的字符串操作的情况
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

3. 并发与多线程

3.1 线程

  • Join() 线程加入,执行此方法的线程优先使用cpu
  • Yeild() 线程释放资源使所有线程能有相等的机会使用cpu
  • Sleep() 相当于让线程睡眠,交出CPU,让CPU去执行其他的任务(不会释放锁)。
  • Wait()方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。wait是要释放对象锁,进入等待池。既然是释放对象锁,那么肯定是先要获得锁。所以wait必须要写在synchronized代码块中,否则会报异常。
  • notify方法,也需要写在synchronized代码块中,调用对象的这两个方法也需要先获得该对象的锁.notify,notifyAll,
    唤醒等待该对象同步锁的线程,并放入该对象的锁池中.对象的锁池中线程可以去竞争得到对象锁,然后开始执行.如果是通过notify来唤起的线程,那先进入wait的线程会先被唤起来,并非随机唤醒;如果是通过nootifyAll唤起的线程,默认情况是最后进入的会先被唤起来,即LIFO的策略;notify()或者notifyAll()调用时并不会真正释放对象锁,
    必须等到synchronized方法或者语法块执行完才真正释放锁.
  • Interrupt() 处于阻塞状态(wait sleep)的线程调用时会抛出异常(InterruptedException )
  • 守护线程 不会去实现系统的主要功能,主要用于监控、抓取系统资源明细和运行状态 等操作,如垃圾回收线程setDeamon(true) 在start方法前调用,守护线程中不要做

3.2 线程同步

  • synchronized 是 Java 中最基础、最常用的同步机制,它通过获取和释放对象的“监视器锁”(MonitorLock)来实现线程间的互斥访问。
  • volatile 是一个变量修饰符,它不提供任何互斥机制,而是通过内存屏障(MemoryBarrier)来保证 变量的可见性和禁止指令重排序。
特性synchronizedvolatile
作用对象方法、代码块变量
核心机制互斥锁 (Monitor)内存屏障 (Memory Barrier)
原子性保证 (通过互斥实现)不保证 (仅保证单次读/写原子)
可见性保证 (进出同步块时刷新主内存)保证 (强制读写主内存)
有序性保证 (通过互斥和禁止重排序)保证 (通过内存屏障禁止重排序)
线程阻塞会阻塞 (未获取锁的线程进入阻塞状态)不会阻塞 (线程可以继续执行)
性能开销较大 (涉及操作系统,可能上下文切换)较小 (主要是内存屏障开销)
适用场景复杂的原子操作、临界区保护简单的状态标志、一次性安全发布、DCL单例模式

共享变量:如果一个变量在多个线程中都使用到了,那么这个变量就是这几个线程的共享变量。
可见性:一个线程对共享变量的修改,能够及时地到主内存并且让其他的线程看到。
原子性:线程要么执行完整个代码块,要么完全不执行,不会被其他线程打断。

详见 Java中 synchronized 和 volatile 详解

3.2 线程池

在高并发的 Java 应用开发中,频繁地创建和销毁线程会带来巨大的系统开销(如内存分配、CPU 调度),并可能导致系统资源耗尽。线程池(Thread Pool)技术应运而生,它通过预先创建并管理一组可复用的线程,极大地优化了多线程任务的执行效率和资源利用率。

1.线程池的核心优势
  • 降低资源消耗:通过复用已创建的线程,避免了频繁创建和销毁线程所带来的性能开销。
  • 提高响应速度:任务到达时,无需等待线程创建,可立即由池中的空闲线程执行。
  • 提高线程的可管理性:可以统一配置、监控和调优线程资源,避免无限制创建线程导致的系统不稳定(如 OOM)。
  • 控制最大并发数:有效防止因过多线程并发执行而耗尽系统资源。
2.核心组件与工作原理

一个标准的 Java 线程池主要由以下几个部分组成:

核心组件
  • ThreadPoolExecutor:线程池的核心实现类。
  • 核心线程池 (corePoolSize):线程池中保持存活的最小线程数,即使它们处于空闲状态。
  • 最大线程数 (maximumPoolSize):线程池中允许存在的最大线程数。
  • 任务队列 (workQueue):用于存放待执行任务的阻塞队列。
  • 线程工厂 (threadFactory):用于创建新线程的工厂。
  • 拒绝策略 (handler):当线程池和任务队列都满时,用于处理新提交任务的策略。
工作流程

当一个新任务被提交到线程池时,其处理流程如下:

  1. 判断核心线程数:如果当前运行的线程数 小于 corePoolSize,则立即创建一个新线程来执行该任务,无论是否有空闲线程。
  2. 加入任务队列:如果当前运行的线程数 大于或等于 corePoolSize,则尝试将任务放入 workQueue 队列中。
  3. 判断最大线程数:如果队列已满,且当前运行的线程数 小于 maximumPoolSize,则创建一个非核心线程来执行该任务。
  4. 执行拒绝策略:如果队列已满,且当前运行的线程数 等于 maximumPoolSize,则触发 RejectedExecutionHandler 拒绝策略来处理该任务。
2.ThreadPoolExecutor 核心参数详解

ThreadPoolExecutor 提供了非常灵活的构造函数,允许开发者精确控制线程池的行为。

public ThreadPoolExecutor(int corePoolSize,           // 核心线程数int maximumPoolSize,        // 最大线程数long keepAliveTime,         // 非核心线程空闲存活时间TimeUnit unit,              // 存活时间单位BlockingQueue<Runnable> workQueue, // 任务队列ThreadFactory threadFactory, // 线程工厂RejectedExecutionHandler handler // 拒绝策略
)
3.线程池最佳实践

CPU 密集型任务

  • 线程数 ≈ CPU 核心数 + 1。
  • 过多的线程会导致频繁的上下文切换,反而降低效率。

I/O 密集型任务

  • 线程数可以设置得更大,因为线程在等待 I/O 时会释放 CPU。
  • 一个常见的公式是:线程数 = CPU 核心数 * (1 + 平均等待时间 / 平均计算时间)。
  • 也可以根据压测结果进行调整。

混合型任务

  • 可以将任务拆分,分别用不同类型的线程池处理。

选择合适的任务队列

  • 优先使用有界队列(如 ArrayBlockingQueue),并设置合理的容量,以防止资源耗尽。
  • 避免在生产环境使用无界队列(LinkedBlockingQueue),除非你能完全掌控任务的提交速率。

4. JVM 相关

Java 虚拟机(JVM,Java Virtual Machine)是 Java 平台的核心和基石,它使得 Java 语言能够实现“一次编写,到处运行”(Write Once, Run Anywhere)的跨平台特性。JVM 并非一个真实的物理机器,而是一个由软件模拟的、运行在操作系统之上的抽象计算机

4.1 JVM 的核心作用

1. 跨平台性
  • 核心价值:JVM 最核心的作用是实现跨平台运行。
  • 机制:开发者只需编写一次代码,编译成字节码后,任何安装了对应操作系统版本 JVM 的机器都能运行该程序。
  • 角色:JVM 充当“翻译官”,将统一的字节码“翻译”成不同平台的本地机器码。
2. 字节码执行引擎
  • 运行引擎:JVM 是 Java 程序的运行引擎。
  • 执行方式
    • 通过解释器逐条执行字节码指令。
    • 通过即时编译器(JIT Compiler)将频繁执行的“热点代码”编译成本地机器码,大幅提升执行效率。
3. 自动内存管理
  • 机制:JVM 提供自动化的内存管理机制,最著名的是垃圾回收(Garbage Collection, GC)。
  • 作用:自动追踪和回收堆中不再被引用的对象所占用的内存,减轻开发者手动管理内存的负担,有效避免内存泄漏(但不能完全杜绝)。
4. 安全性
  • 机制:JVM 通过“沙箱”(Sandbox)机制对运行的代码进行安全检查。
  • 作用:防止恶意代码破坏系统资源或进行未授权操作。
5. 多线程支持
  • 原生支持:JVM 原生支持多线程并发执行。
  • 管理职责:负责管理线程的生命周期、调度和同步,是构建高性能并发应用的基础。
6. 动态类加载
  • 机制:JVM 的类加载器(Class Loader)可以在程序运行时动态地加载和链接所需的类。
  • 作用:为程序的灵活扩展和热部署提供可能。

4.2 JVM 的内部结构(运行时数据区)

JVM 在执行 Java 程序时,会将其管理的内存划分为若干个不同的区域,这些区域各有各的用途。

1. 程序计数器 (Program Counter Register)
  • 线程私有
  • 作用:记录当前线程正在执行的字节码指令的地址。当线程被切换回来时,能知道从哪里继续执行。
  • 特点:是唯一一个在《Java虚拟机规范》中没有规定任何 OutOfMemoryError 情况的区域。
2. Java 虚拟机栈 (Java Virtual Machine Stack)
  • 线程私有,生命周期与线程相同。
  • 作用:存储方法执行时的“栈帧”(Stack Frame)。每个方法被调用时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 异常
    • 线程请求的栈深度大于 JVM 允许的深度,会抛出 StackOverflowError
    • 如果栈可以动态扩展,但在扩展时无法申请到足够的内存,则会抛出 OutOfMemoryError
3. 本地方法栈 (Native Method Stack)
  • 线程私有
  • 作用:与虚拟机栈类似,但它服务于 JVM 调用的 Native 方法(用 C/C++ 编写的本地方法)。
  • 异常:与虚拟机栈相同,可能抛出 StackOverflowErrorOutOfMemoryError
4. Java 堆 (Java Heap)
  • 线程共享,是 JVM 管理的内存中最大的一块。
  • 作用:存放几乎所有对象实例和数组。垃圾回收的主要区域,因此也被称为“GC堆”。
  • 结构:在主流的 JVM 实现(如 HotSpot)中,堆被细分为:
    • 新生代 (Young Generation):新创建的对象首先分配在这里。
      • Eden 区:对象的“出生地”。
      • Survivor 区 (S0, S1):经过垃圾回收后存活的对象会从 Eden 区移动到这里。
    • 老年代 (Old Generation):在新生代中经历了多次垃圾回收后仍然存活的对象会被晋升到这里。
  • 异常:当堆中没有足够空间分配实例,并且堆也无法再扩展时,会抛出 OutOfMemoryError
5. 方法区 (Method Area) / 元空间 (Metaspace)
  • 线程共享
  • 作用:存储已被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
  • 演变
    • 在 JDK 8 之前,方法区的实现被称为“永久代”(Permanent Generation)。
    • 从 JDK 8 开始,“永久代”被移除,取而代之的是“元空间”(Metaspace)。元空间使用的是本地内存(Native Memory),而不是 JVM 的堆内存,理论上只受本机物理内存限制。
  • 异常:当方法区/元空间无法满足新的内存分配需求时,会抛出 OutOfMemoryError
6. 运行时常量池 (Runtime Constant Pool)
  • 方法区的一部分
  • 作用:存放编译期生成的各种字面量(如字符串 "Hello")和符号引用(如类和接口的全限定名、字段和方法的名称及描述符)。在 JDK 8 之后,字符串常量池被移到了堆中。
  • 异常:当常量池无法再申请到内存时,会抛出 OutOfMemoryError
7. 直接内存 (Direct Memory)
  • 不属于 JVM 运行时数据区,但被频繁使用。
  • 作用:通过 java.nio 包中的 DirectByteBuffer 对象可以分配直接内存。这部分内存不受 JVM 堆大小限制,但受本机总物理内存限制。它在进行 I/O 操作(如 NIO)时能显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
  • 异常:分配失败时会抛出 OutOfMemoryError

类加载机制和双亲委派模型。

5. 泛型与反射

5.1Java 泛型 (Generics)

泛型的核心思想是参数化类型,即在定义类、接口或方法时,不指定具体的类型,而是使用一个“类型参数”(如 , , <K, V>)来代替。在使用时,再传入具体的类型。

  1. 主要目的
    类型安全 (Type Safety):编译器可以在编译时检查类型,避免在运行时出现 ClassCastException。例如,List 只能存放 String 类型的对象,如果尝试放入 Integer,编译器会直接报错。
    消除强制类型转换:在使用泛型之前,从集合中取出对象时,通常需要进行强制类型转换(如 (String) list.get(0))。泛型消除了这种繁琐且易错的操作。
    提高代码复用性:可以编写适用于多种数据类型的通用算法和数据结构。

  2. 核心机制:类型擦除 (Type Erasure)这是理解泛型与反射关系的关键。
    定义:为了保持与早期 Java 版本的二进制兼容性,Java 编译器在编译泛型代码时,会将所有的泛型类型参数信息擦除,替换为其限定类型(通常是 Object)或第一个边界。
    编译期 vs 运行期:
    编译期:编译器利用泛型信息进行严格的类型检查。
    运行期:JVM 看不到泛型信息。对于 JVM 而言,ArrayList 和 ArrayList 是同一个类 ArrayList。

  3. 类型擦除带来的限制
    不能创建泛型类型的实例:new T() 是非法的。
    不能创建泛型数组:new T[10] 是非法的。
    不能使用 instanceof 检查具体的泛型类型:obj instanceof List 是非法的,只能写 obj instanceof List。

5.2 Java 反射 (Reflection)

反射是 Java 在运行时动态获取类、接口、字段、方法等信息,并能动态调用对象方法、修改字段值的能力。它打破了编译时的类型检查限制。

  1. 主要用途
    框架开发:Spring, Hibernate 等框架大量使用反射来实现依赖注入、ORM 映射等功能。
    动态代理:创建代理对象,在不修改源码的情况下增强方法功能。
    调试与测试工具:访问私有成员进行单元测试。
    通用工具类:编写能处理任意类型对象的通用序列化/反序列化、拷贝等工具。
  2. 核心 API
    Class:代表一个类的运行时类型信息。
    Field:代表类的成员变量。
    Method:代表类的方法。
    Constructor:代表类的构造器。
  3. 反射与类型擦除的“矛盾”
    由于类型擦除,通过标准的反射 API 获取到的类型通常是原始类型(Raw Type),而不是具体的泛型类型。

5.3 特性对比总结

特性泛型 (Generics)反射 (Reflection)
主要作用时期编译期 (Compile-time)运行期 (Runtime)
核心目的提供编译时类型安全,消除强制转换,提高代码复用性在运行时动态获取和操作类、方法、字段等信息
关键机制类型擦除 (Type Erasure)Class, Field, Method, Constructor 等 API
相互关系类型擦除使得标准反射 API 无法直接获取泛型实例的具体类型可通过 Type 接口(如 ParameterizedType)获取在声明处(字段、方法、父类)的泛型信息,从而部分“突破”类型擦除的限制

简单来说

  • 泛型是给编译器用的,用于在写代码时保证安全;
  • 反射是给程序在运行时用的,用于动态地了解和操作对象。
  • 虽然泛型信息在运行时大部分被擦除,但 Java 通过 java.lang.reflect.Type 体系保留了关键的声明信息,使得反射在特定场景下依然能够与泛型协同工作,这为构建高度灵活的框架和工具提供了可能。

6. java 类双亲委托机制

双亲委派机制的核心思想是:当一个类加载器收到加载某个类的请求时,它不会立即尝试自己去加载,而是先将这个请求“委托”给它的父类加载器去处理。只有当父类加载器反馈自己无法完成这个加载请求时,子类加载器才会尝试自己去加载

6.1类加载器的层级结构

在 Java 中,类加载器以树状的父子层级结构组织,主要包含以下类加载器:

1. 启动类加载器 (Bootstrap ClassLoader)
  • 地位:位于最顶层,是 JVM 的一部分。
  • 实现:由 C++ 语言实现,是 JVM 自身的一部分,非 java.lang.ClassLoader 的子类。在 Java 代码中获取其引用时结果为 null
  • 职责:加载 Java 最核心的类库(如 <JAVA_HOME>/jre/lib 下的 rt.jarresources.jarcharsets.jar 等)。例如,java.lang.Objectjava.lang.String 等类由其加载。
2. 扩展类加载器 (Extension ClassLoader)
  • 地位:Bootstrap ClassLoader 的子加载器。
  • 实现:由 Java 语言编写,是 java.lang.ClassLoader 的子类。
  • 职责:加载 <JAVA_HOME>/jre/lib/ext 目录或系统变量 java.ext.dirs 指定路径下的 JAR 包。
3. 应用程序类加载器 (Application ClassLoader)
  • 地位:Extension ClassLoader 的子加载器,也称为“系统类加载器”。
  • 实现:由 Java 语言编写。
  • 职责:加载用户类路径(ClassPath)指定的类库(如开发者编写的代码及第三方 JAR 包)。开发者可直接在代码中使用该加载器。
4. 自定义类加载器 (Custom ClassLoader)
  • 地位:通常为 Application ClassLoader 的子加载器。
  • 实现:通过继承 java.lang.ClassLoader 类实现。
  • 职责:满足特定需求,例如:
    • 加载网络上的类。
    • 实现热部署。
    • 为不同应用模块提供隔离的类加载环境(如 Tomcat 为每个 Web 应用创建独立类加载器)。

6.2 双亲委派的工作流程

当类加载器(如应用程序类加载器)收到加载 com.example.MyClass 的请求时,流程如下:

  1. 向上委托:应用程序类加载器将请求委托给父加载器(扩展类加载器)。
  2. 继续向上:扩展类加载器将请求委托给父加载器(启动类加载器)。
  3. 顶层尝试:启动类加载器在 jre/lib 核心类库中查找 com.example.MyClass(失败,因用户类不存在于此)。
  4. 向下传递:请求返回扩展类加载器,在其负责的 jre/lib/ext 目录下查找(失败)。
  5. 子加载器出手:请求回到应用程序类加载器,在 ClassPath 路径下查找并加载(成功则返回,失败则抛出 ClassNotFoundException)。

概括自底向上检查,自顶向下加载

6.3 为什么需要双亲委派机制?

双亲委派机制带来以下关键好处:

1. 保障核心类库的安全性 (Security)
  • 问题:若无此机制,恶意开发者可在 ClassPath 下定义 java.lang.String 并植入恶意代码。
  • 解决:加载请求委托至启动类加载器,优先加载官方核心类,防止 API 被篡改。
2. 避免类的重复加载 (Avoid Duplicate Loading)
  • 机制:父加载器已加载的类会直接返回,避免子加载器重复加载。
  • 意义:保证 JVM 中类的唯一性,节省内存,避免类型转换异常。
3. 保证类加载的一致性 (Consistency)
  • 机制:层级结构确保同一类由同一“权威”加载器加载。
  • 意义:维护类定义的一致性,避免因不同加载器导致冲突。

二、 Android 核心组件与机制

1. 四大组件

1.1 Activity:

1. 核心作用
  • 用户交互的入口,每个界面对应一个 Activity
  • 管理界面生命周期(创建、暂停、销毁等)
  • 通过 Intent 实现页面跳转和数据传递

Activity Result API(回传值)
步骤 1:在第一个 Activity 中注册回调使用 ActivityResultLauncher 启动第二个 Activity,并处理返回结果:

public class FirstActivity extends AppCompatActivity {// 1. 定义 Activity Result Launcherprivate 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();}}});@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_first);// 2. 启动第二个 ActivityfindViewById(R.id.button).setOnClickListener(v -> {Intent intent = new Intent(this, SecondActivity.class);someActivityResultLauncher.launch(intent);});}
}

步骤 2:在第二个 Activity 中设置回传值
通过 setResult() 返回数据,并结束当前 Activity:

public class SecondActivity extends AppCompatActivity {@Overrideprotected 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. 设置结果并结束当前ActivitysetResult(Activity.RESULT_OK, resultIntent);finish();});}
}

关键点
ActivityResultLauncher:替代 startActivityForResult(),更安全且避免内存泄漏。
RESULT_OK:表示操作成功,也可用 RESULT_CANCELED 表示取消。
setResult():必须在 finish() 前调用。

2. 生命周期方法
方法触发场景
onCreate()Activity 首次创建时调用
onStart()Activity 对用户可见时调用
onResume()Activity 获得焦点(可交互)时调用
onPause()Activity 失去焦点(如弹出对话框)时调用
onStop()Activity 不可见时调用
onDestroy()Activity 被销毁时调用
  • activityA 中打开activityB

A onPause()—> B onCreate() ---->B onStart() —> B o’nResume() —> A onstop()

  • 再点返回activityA的生命周期函数调用

B onPause() —>A onRestart() —> A onStart() —>A onResume() —>
B onStop —> B onDestroy()

  • A 中打开B如果B是透明的activity,则不会执行a的onstop的restart以及start方法(同dialog)

A onPause()—> B onCreate() ---->B onStart() —> B o’nResume()

  • 再点返回A的生命周期函数调用

B onPause() —>A onResume() —> B onStop —> B onDestroy()

3.使用注意
  • 内存泄漏:未在 onDestroy() 中解绑监听器或释放资源
  • 配置变更:屏幕旋转会导致 Activity 重建,需通过 onSaveInstanceState() 保存数据

1.1 Service:

1. 核心作用
  • 在后台执行长时间任务(如播放音乐、下载文件),不依赖用户界面
  • 分为 启动型服务(Started Service) 和 绑定型服务(Bound Service)

启动服务和绑定服务对比

特性startServicebindService
启动目的长期后台运行(如音乐播放、数据同步)与调用者交互(如 Activity 调用 Service 方法)
生命周期控制独立于调用者,需手动调用 stopService()stopSelf() 停止依赖调用者,调用者解绑或销毁时自动解绑,无绑定时销毁
回调方法onCreate()onStartCommand()onDestroy()onCreate()onBind()onUnbind()onDestroy()
重复调用行为多次调用 startService() 仅触发 onStartCommand(),不重复创建 Service多次调用 bindService() 仅首次触发 onCreate()onBind()
调用者与 Service 关系无直接交互,通过 Intent 传递数据通过 ServiceConnection 获取 IBinder,直接调用 Service 方法
2 IntentService

IntentService 的原理
IntentService 是 Android 提供的一种特殊 Service,其核心原理基于 HandlerThread + 工作队列,通过以下机制实现后台任务处理

  • 内部维护 HandlerThread
    IntentService 在 onCreate() 方法中初始化一个 HandlerThread(本质是带有消息循环的子线程),并为其创建 Looper 和 Handler(即 ServiceHandler)。所有任务通过 ServiceHandler 的 handleMessage() 方法处理,确保任务在子线程中执行,避免阻塞主线程。
  • 任务队列与串行执行
    客户端通过 startService(Intent) 发送任务请求,IntentService 将每个请求封装为 Message,按顺序放入 HandlerThread 的消息队列。任务按 先进先出(FIFO) 原则串行执行,前一个任务完成后才会处理下一个,避免并发问题。
  • 自动停止服务
    每个任务处理完成后,IntentService 会调用 stopSelf(msg.arg1)(传入最后一次处理的消息 ID),确保所有任务完成后服务自动销毁,无需手动调用 stopService()。

IntentService 的适用场景

  • 后台串行耗时任务
    适合需要 按顺序执行 的多个后台任务(如批量上传图片、下载多个文件),避免并发导致的资源竞争或数据混乱。

  • 避免阻塞主线程
    替代在普通 Service 中手动创建线程的繁琐操作,自动将任务切换到子线程,防止 ANR(Application Not Responding)

对比其他方案

方案适用场景优势劣势
IntentService串行后台任务、自动生命周期管理简单易用,自动销毁无法并行,效率较低
HandlerThread自定义线程管理灵活控制线程优先级需手动处理任务队列和销毁
WorkManager延迟或周期性任务支持后台限制和电量优化适合非实时任务
Kotlin 协程复杂异步逻辑代码简洁,支持并发学习成本较高
3. 前台 Service

前台 Service 是 Android 中一种优先级更高的后台服务,通过在通知栏显示持续存在的通知来告知用户其正在运行,从而避免被系统轻易回收(尤其在低内存或后台限制严格的情况下)

前台service的作用

  • 提升后台存活能力
    在 Android 8.0(Oreo)及以上版本,系统对后台服务有严格限制(如后台执行时间限制、进程容易被杀死),前台 Service 通过通知栏的“常驻通知”向系统声明其重要性,降低被回收的概率,适合执行需要长期运行的任务

  • 满足用户感知需求
    系统要求前台 Service 必须显示通知,确保用户明确知晓后台正在进行的操作(如“正在导航”“正在录音”),避免隐私或功耗问题。

创建前台service

  • 继承 Service 类
    创建自定义 Service 类,重写 onStartCommand() 和 onBind()(若需绑定)。

  • 启动前台 Service
    在 onStartCommand() 中调用 startForeground(),传入通知 ID和通知对象。

  • 定义通知渠道(Android 8.0+ 必需)
    使用 NotificationChannel 为通知分类,提升用户体验。

  • 处理停止逻辑
    在任务完成时调用 stopForeground(true) 移除通知,并停止 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;@Overridepublic void onCreate() {super.onCreate();// 初始化逻辑(如加载资源)}@Overridepublic 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();}// 任务完成后停止 ServicestopSelf();}).start();return START_STICKY; // 服务被杀死后自动重启}@Overridepublic 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();}@Overridepublic 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 的通知不能被隐藏,否则系统会抛出 IllegalStateException。
    通知需包含图标和标题,且在 Android 10+ 上需适配深色模式。
  • Android 8.0+ 兼容性
    必须调用 createNotificationChannel() 创建通知渠道,否则通知无法显示。
  • 权限声明
    在 AndroidManifest.xml 中声明 Service 和通知权限:
<service android:name=".ForegroundService" />
<!-- 若需后台定位等权限,需额外声明 -->
  • 省电模式优化
    在 Android 9+ 上,前台 Service 仍可能受省电策略限制,建议结合 WorkManager 或 JobScheduler 使用。
4. JobScheduler 和 WorkManager
  • JobScheduler 是系统原生的高性能调度工具,但兼容性受限。
  • WorkManager 是跨版本的“全能型”解决方案,覆盖了 JobScheduler 的功能并扩展了持久化、任务链等特性。
  • 推荐:新项目优先使用 WorkManager;仅针对 Android 5.0+ 且对性能敏感的任务可考虑 JobScheduler。

核心区别分析

维度JobSchedulerWorkManager
API 兼容性仅支持 Android 5.0+(API 21+)支持 Android 4.0+(API 14+)
底层实现系统原生调度服务根据设备 API 动态选择:
- API 23+:JobScheduler
- API 14-22:AlarmManager + BroadcastReceiver
任务可靠性依赖系统调度,可能因资源限制延迟执行持久化存储 + 自动重试,确保任务最终执行
功能扩展性基础条件约束(网络、充电、空闲)支持任务链、输入/输出数据传递、优先级设置
开发复杂度需手动定义 JobInfoJobService通过 Worker 类简化任务定义,API 更简洁
适用场景高性能节能调度,对电池寿命敏感的任务跨版本兼容、持久化任务、复杂任务流程

代码示例

  1. JobScheduler 实现
// 定义 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 {@Overridepublic boolean onStartJob(JobParameters params) {// 执行耗时任务new Thread(() -> {// 任务逻辑jobFinished(params, false); // 通知系统任务完成}).start();return true;}@Overridepublic boolean onStopJob(JobParameters params) {// 任务取消逻辑return true;}
}
  1. WorkManager 实现
// 定义 Worker
public class MyWorker extends Worker {public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {super(context, workerParams);}@NonNull@Overridepublic 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()
);

1.1 BroadcastReceiver:

静态注册和动态注册的区别及优缺点。
普通广播、有序广播、粘性广播的区别。
LocalBroadcastManager 的作用和优势。

1.1 ContentProvider:

作用和使用场景(跨应用数据共享)。
如何自定义 ContentProvider?
URI 的匹配规则。

2. View 体系与事件分发

View 绘制流程: measure(), layout(), draw() 三个阶段的详细过程。
事件分发机制: dispatchTouchEvent(), onInterceptTouchEvent(), onTouchEvent() 三个方法的作用、返回值含义及调用顺序。如何解决滑动冲突?
自定义 View:
onMeasure() 中如何正确处理 wrap_content 和 match_parent?
onDraw() 中如何进行绘制?Canvas 和 Paint 的基本用法。
自定义属性 (attrs.xml, declare-styleable) 的定义和使用。
常用控件: RecyclerView 的缓存机制(四级缓存)、ViewHolder 模式、LayoutManager、ItemDecoration、ItemAnimator。ViewPager2 与 ViewPager 的区别。

3. 异步与多线程

Android 为什么不能在子线程更新 UI?
Handler 机制:
Message, MessageQueue, Looper, Handler 的工作原理。
主线程的 Looper 何时创建?为什么不会阻塞主线程?
子线程如何创建 Looper?
Handler 导致内存泄漏的原因及解决方案(静态内部类 + 弱引用)。
AsyncTask 的原理、缺陷(API 30 已废弃)及替代方案。
RxJava 的核心概念(Observable, Observer, Scheduler, Operator)及其在项目中的应用。
Kotlin 协程 (Coroutine) 的基本概念(suspend, CoroutineScope, launch, async)和优势。

4. 数据存储

SharedPreferences 的原理、适用场景及 apply() 与 commit() 的区别。
SQLite 数据库的基本操作(增删改查),SQLiteOpenHelper 的使用。
Room 持久化库的使用和优势。
文件存储(内部存储、外部存储)的路径和权限。

5. 网络编程

HTTP 协议基础(GET/POST 区别、状态码、Header)。
OkHttp 的核心架构(拦截器链 Interceptor)和使用。
Retrofit 的原理(动态代理、注解解析)和使用。
JSON 解析库(Gson, Moshi)的使用。

三、性能优化

1. 内存优化

如何检测和分析内存泄漏?(工具:Android Profiler, LeakCanary)
如何避免内存抖动(频繁创建和回收对象)?
对象池 (Pools) 的使用场景。
Bitmap 的高效加载和内存管理(采样率压缩、复用、及时回收)。

2. 布局优化

如何减少布局层级?(使用 ConstraintLayout, , , )
什么是过度绘制 (Overdraw)?如何检测和避免?
onDraw() 中避免创建对象和执行耗时操作。

3. 卡顿优化

卡顿的根本原因是什么?(主线程执行耗时任务)
如何监控应用的帧率 (FPS)?
工具:Systrace, TraceView (已废弃,推荐使用 CPU Profiler)。

4. 启动优化

应用启动流程(Application, Activity)。
优化方案:异步初始化、延迟初始化、启动预加载、闪屏页优化。

5. APK 瘦身

代码混淆 (ProGuard/R8) 的作用。
资源压缩(移除无用资源、使用 WebP 格式、资源混淆)。
So 库的优化(按 ABI 打包)。
Lint 工具的使用。

四、架构与设计模式

考察工程化思维和代码质量。

1. 常用设计模式

单例模式(双重检查锁、静态内部类、枚举)及在 Android 中的应用。
观察者模式(LiveData, RxJava, 自定义 Listener)。
适配器模式(RecyclerView.Adapter)。
建造者模式 (AlertDialog.Builder)。
工厂模式。
策略模式。

2. 应用架构

MVC, MVP, MVVM 三种架构模式的区别、优缺点及适用场景。
MVVM 架构的核心组件(ViewModel, LiveData, DataBinding/ViewBinding)及其工作原理。
ViewModel 的生命周期如何比 Activity/Fragment 更长?它是如何做到的?
LiveData 如何感知生命周期?它的“粘性”问题如何解决?
Repository 模式的概念和作用。

3. Jetpack 组件

Lifecycle 组件的原理和使用。
Navigation 组件的作用和优势。
WorkManager 的使用场景。

五、Framework 与原理

针对中高级和资深岗位。

1. Binder 机制

为什么 Android 要使用 Binder 进行进程间通信 (IPC)?
Binder 通信的大致流程(ServiceManager, Client, Server, Binder Driver)。
AIDL 的使用和原理。

2. Handler 消息机制深入

MessageQueue 是如何实现“等待”和“唤醒”的?(epoll 机制)
Looper.loop() 为什么是死循环却不会导致 ANR?
View 绘制深入
Choreographer 的作用(协调输入、动画、绘制)。
VSYNC 信号的作用。

3. 应用启动流程

从点击桌面图标到 Activity 的 onCreate() 被调用,系统经历了哪些过程?(Launcher, AMS, Zygote, ActivityThread)

4. ANR (Application Not Responding)

什么情况下会触发 ANR?(主线程 5s 无响应、BroadcastReceiver 10s 无响应、Service 20s 无响应)
如何分析和定位 ANR 问题?(查看 /data/anr/traces.txt)

六、开源框架与新技术

考察学习能力和技术广度。

1. 常用框架原理

Retrofit 是如何将接口方法转换为网络请求的?(动态代理、注解处理器)
OkHttp 的拦截器链是如何工作的?
Glide 的图片加载流程和缓存机制(内存缓存、磁盘缓存)。
EventBus 的工作原理(注解、订阅者索引、Poster)。

2. Kotlin

lateinit 和 by lazy 的区别。
data class 的作用和自动生成的方法。
扩展函数和扩展属性。
密封类 (sealed class) 和枚举的区别。
协程 (Coroutine) 的基本使用和挂起函数 (suspend) 的原理。

3. Flutter / Compose

(如果简历有提及)Flutter 的 Widget、State、Context 概念。
Jetpack Compose 的声明式 UI 与传统命令式 UI 的区别。

七、场景题与开放性问题

考察解决问题的思路和沟通能力。

1. 项目相关

请介绍一下你做过的最有挑战性的项目,你遇到了什么难点,是如何解决的?
你在项目中是如何进行性能优化的?
你如何保证代码的质量和可维护性?

2. 设计题

如何设计一个图片加载框架?
如何设计一个网络请求框架?
如何实现一个全局的用户登录状态管理?

3. 开放性问题

你平时是如何学习新技术的?
你如何看待 Android 的未来发展?
你对我们公司/团队有什么想了解的?


文章转载自:

http://SlMTYxQE.xtyyg.cn
http://jHcKBNex.xtyyg.cn
http://A2nj8K0w.xtyyg.cn
http://9KjTmuXF.xtyyg.cn
http://zg7eXywY.xtyyg.cn
http://A4ufiwkQ.xtyyg.cn
http://HRQxx6wK.xtyyg.cn
http://527yw3ND.xtyyg.cn
http://ljX2HpBD.xtyyg.cn
http://msoj6PPO.xtyyg.cn
http://OpVjfDlP.xtyyg.cn
http://jXMniXaV.xtyyg.cn
http://u6JDSiGi.xtyyg.cn
http://2jgjuKl1.xtyyg.cn
http://wecyrQ9B.xtyyg.cn
http://plCNYhbk.xtyyg.cn
http://5vQFkogl.xtyyg.cn
http://N9rnnOhP.xtyyg.cn
http://VFP2HqpA.xtyyg.cn
http://F4FSCqxq.xtyyg.cn
http://vuuiLTvg.xtyyg.cn
http://6jTikbfo.xtyyg.cn
http://StIflXsw.xtyyg.cn
http://BUvmxkST.xtyyg.cn
http://1i32eeGi.xtyyg.cn
http://cDFTLNLs.xtyyg.cn
http://pmejsNGg.xtyyg.cn
http://iiLlkjKo.xtyyg.cn
http://a5W0p78P.xtyyg.cn
http://8M6k3f3G.xtyyg.cn
http://www.dtcms.com/a/386970.html

相关文章:

  • elementui中表单先上传但不请求接口,点击按钮后在请求接口的方式上传文件,及校验
  • el-input自动填充与设置input背景色无效
  • java设计模式-工厂模式(文件上传)
  • Keras+Flask手写数字识别Web应用
  • PPTist+cpolar:开源演示文稿的远程创作方案
  • Chapter8—组合模式
  • vmware的ub系统长时间不动会黑屏
  • 从0到1打造一个能上传任意GeoJSON的交互式Web地图
  • 深入理解数据结构之复杂度
  • Silicon EFR32xG22 CMU
  • 运维面试笔记(持续补充版)
  • 托福阅读35-1
  • qt QCandlestickSet详解
  • 在Linux和Windows系统下使用Qt监测U盘的插拔事件
  • 文字识别接口的应用场景-发票识别接口-OCR API
  • 鸿蒙NEXT ArkWeb同层渲染:原生与Web的完美融合
  • 基于springboot的4s店汽车销售服务系统
  • ARM芯片的调试访问端口 DAP(Debug Access Port)
  • 减少推导式中的重复计算:赋值表达式(:=)的优雅应用 (Effective Python 第29条)
  • 空压机远程控制与数据采集的御控物联网解决方案
  • 瑞萨MCU RA4M1 FLASH锁死问题记录
  • Kubernetes 调度器(Scheduler)
  • Java设计模型-责任链模式
  • Linux 服务器安全优化:firewalld SSH 限制 白名单与 SCP 服务禁用流程
  • bisheng 智能体
  • 学完Python之后我写了一个免费看电影的软件
  • 【ROS2】Concept(Advanced )
  • Apifox自动化测试场景设计
  • 知识复用缺乏跨角色适配该如何改善
  • XML 与 YML 全方位对比:从语法到应用场景