Android 基础---第一部

0 阅读1小时+

第一章:Android 系统架构(20 题)

1.1 Android系统架构分为哪几层?

答案:

Android系统架构采用分层架构设计,从下到上分为五层,每层都有明确的职责。

架构层次:

  1. Linux内核层(Linux Kernel)

    • 作用:提供底层系统服务
    • 内容:进程管理、内存管理、设备驱动、网络协议栈
    • 特点:基于Linux内核,提供硬件抽象层
  2. 硬件抽象层(HAL)

    • 作用:为上层提供统一的硬件接口
    • 内容:音频、相机、传感器等硬件驱动接口
    • 特点:屏蔽硬件差异,便于移植
  3. 系统运行库层(Native Libraries)

    • 作用:提供核心系统功能
    • 内容
      • C/C++库:SQLite、OpenGL ES、WebKit等
      • Android运行时(ART):虚拟机、核心库
    • 特点:使用C/C++实现,性能高效
  4. 应用框架层(Application Framework)

    • 作用:为应用开发提供API
    • 内容
      • 四大组件:Activity、Service、BroadcastReceiver、ContentProvider
      • 系统服务:WindowManager、ActivityManager、PackageManager等
      • 资源管理:资源访问、主题样式等
    • 特点:Java/Kotlin API,面向应用开发
  5. 应用层(Applications)

    • 作用:用户可见的应用
    • 内容:系统应用(电话、短信、设置等)和第三方应用
    • 特点:基于应用框架层开发

架构图:

┌─────────────────────────────────┐
│    应用层(Applications)        │
├─────────────────────────────────┤
│   应用框架层(Framework)        │
├─────────────────────────────────┤
│   系统运行库层(Libraries)      │
├─────────────────────────────────┤
│   硬件抽象层(HAL)              │
├─────────────────────────────────┤
│   Linux内核层(Kernel)          │
└─────────────────────────────────┘

各层关系:

  • 上层依赖下层:应用层依赖框架层,框架层依赖运行库层
  • 接口抽象:每层通过接口与下层交互,降低耦合
  • 功能封装:下层功能被上层封装,提供更高级的API

1.2 Linux内核层的作用是什么?

答案:

Linux内核层是Android系统的基础层,提供底层系统服务和硬件抽象。

主要作用:

  1. 进程管理

    • 进程创建、调度、销毁
    • 进程间通信(IPC)
    • 内存管理
  2. 设备驱动

    • 硬件设备驱动(显示、音频、传感器等)
    • 设备文件系统管理
    • 硬件资源分配
  3. 网络协议栈

    • TCP/IP协议实现
    • 网络数据包处理
    • 网络接口管理
  4. 文件系统

    • 文件系统管理(ext4、F2FS等)
    • 文件读写操作
    • 存储设备管理
  5. 安全机制

    • 权限管理(基于Linux权限模型)
    • SELinux安全策略
    • 进程隔离

关键特性:

  • 基于Linux:使用Linux内核,继承Linux的稳定性和安全性
  • 硬件抽象:屏蔽硬件差异,便于在不同设备上运行
  • 性能基础:为上层提供高效的底层服务

1.3 系统运行库层包含哪些内容?

答案:

系统运行库层包含C/C++库Android运行时(ART),为应用框架层提供核心功能支持。

主要内容:

1. C/C++核心库

  • SQLite:轻量级数据库引擎
  • OpenGL ES:3D图形渲染库
  • WebKit:Web浏览器引擎
  • Media Framework:音视频编解码库
  • Surface Manager:窗口管理
  • Libc:C标准库(Bionic)

2. Android运行时(ART)

  • ART虚拟机:执行DEX字节码
  • 核心库(Core Libraries):Java核心API
  • JNI支持:Java Native Interface

3. 运行时特性

  • AOT编译:应用安装时编译为机器码
  • JIT编译:运行时即时编译
  • 垃圾回收:自动内存管理
  • 多线程支持:并发执行

作用:

  • 为应用框架层提供底层功能支持
  • 提供高性能的系统服务
  • 实现Java/Kotlin代码的执行环境

1.4 应用框架层的作用是什么?

答案:

应用框架层为应用开发提供API,是Android开发的核心层。

主要作用:

  1. 四大组件管理

    • Activity:界面管理
    • Service:后台服务
    • BroadcastReceiver:广播接收
    • ContentProvider:数据共享
  2. 系统服务

    • ActivityManager:Activity生命周期管理
    • WindowManager:窗口管理
    • PackageManager:应用包管理
    • LocationManager:位置服务
    • NotificationManager:通知管理
  3. 资源管理

    • 资源访问(drawable、layout、string等)
    • 主题样式管理
    • 多语言支持
  4. UI框架

    • View系统
    • 布局管理
    • 事件处理

特点:

  • Java/Kotlin API:面向应用开发
  • 功能丰富:提供完整的应用开发能力
  • 易于使用:封装底层复杂性

1.5 应用层的特点是什么?

答案:

应用层是用户直接交互的应用软件层

特点:

  1. 用户可见

    • 用户直接使用的应用
    • 提供用户界面和交互
  2. 基于框架层

    • 使用应用框架层提供的API
    • 遵循Android开发规范
  3. 应用类型

    • 系统应用:电话、短信、设置、相机等
    • 第三方应用:开发者开发的应用
  4. 独立运行

    • 每个应用运行在独立的进程中
    • 应用间通过系统机制通信

开发特点:

  • 使用Java或Kotlin开发
  • 基于Android SDK
  • 遵循Android设计规范

1.6 各层之间的关系是什么?

答案:

Android系统各层之间是依赖关系,上层依赖下层提供的服务。

关系说明:

  1. 依赖关系

    应用层 → 应用框架层 → 系统运行库层 → Linux内核层
    
    • 上层调用下层提供的接口
    • 下层为上层提供服务
  2. 接口抽象

    • 每层通过接口与下层交互
    • 降低层间耦合
    • 便于维护和扩展
  3. 功能封装

    • 下层功能被上层封装
    • 提供更高级、更易用的API
    • 隐藏实现细节

示例:

  • 应用层调用应用框架层的Activity API
  • 应用框架层调用系统运行库层的SQLite库
  • 系统运行库层调用Linux内核层的文件系统

1.7 系统架构的设计思想是什么?

答案:

Android系统架构的设计思想包括分层设计模块化接口抽象等。

设计思想:

  1. 分层设计

    • 每层职责明确
    • 层间通过接口交互
    • 便于维护和扩展
  2. 模块化

    • 功能模块化
    • 组件可替换
    • 降低耦合
  3. 接口抽象

    • 定义清晰的接口
    • 隐藏实现细节
    • 便于测试
  4. 安全性

    • 进程隔离
    • 权限管理
    • 沙箱机制
  5. 性能优化

    • 底层使用C/C++实现
    • 虚拟机优化
    • 资源管理

1.8 Dalvik虚拟机和ART虚拟机的区别是什么?

答案:

Dalvik和ART是Android的两种虚拟机实现,ART是Dalvik的改进版本

主要区别:

特性DalvikART
编译方式JIT(运行时编译)AOT(安装时编译)+ JIT
启动速度较快较慢(首次)
运行速度较慢较快
存储占用较小较大
安装时间慢(首次安装)
内存占用较小较大

Dalvik特点:

  • JIT编译:运行时将DEX字节码编译为机器码
  • 启动快:安装时不需要编译
  • 运行慢:每次运行都需要编译

ART特点:

  • AOT编译:安装时将DEX字节码编译为机器码
  • 启动慢:首次安装需要编译
  • 运行快:直接执行机器码,无需运行时编译
  • 混合模式:Android 7.0+支持AOT+JIT混合编译

演进历史:

  • Android 4.4及以前:使用Dalvik
  • Android 5.0+:使用ART(AOT编译)
  • Android 7.0+:ART支持混合编译(AOT+JIT)

1.9 ART的AOT编译和JIT编译是什么?

答案:

AOT(Ahead-Of-Time)和JIT(Just-In-Time)是两种不同的编译方式。

AOT编译(提前编译):

  • 时机:应用安装时
  • 过程:将DEX字节码编译为机器码
  • 存储:机器码存储在设备上
  • 优点
    • 运行时性能好(直接执行机器码)
    • 减少运行时编译开销
  • 缺点
    • 安装时间长
    • 存储占用大
    • 首次启动可能较慢

JIT编译(即时编译):

  • 时机:应用运行时
  • 过程:运行时将热点代码编译为机器码
  • 存储:不存储机器码
  • 优点
    • 安装快
    • 存储占用小
    • 启动快
  • 缺点
    • 运行时需要编译,影响性能
    • 首次执行较慢

混合编译(Android 7.0+):

  • 策略:结合AOT和JIT的优势
  • 机制
    • 安装时:不编译,快速安装
    • 运行时:JIT编译热点代码
    • 后台:AOT编译常用代码
  • 优势:平衡安装速度、启动速度和运行性能

1.10 为什么Android使用虚拟机而不是直接编译为机器码?

答案:

Android使用虚拟机主要出于跨平台兼容性安全性开发效率的考虑。

主要原因:

  1. 跨平台兼容性

    • Android运行在不同架构的硬件上(ARM、x86等)
    • 虚拟机可以屏蔽硬件差异
    • 一份DEX字节码可以在不同设备上运行
  2. 安全性

    • 虚拟机提供沙箱环境
    • 应用隔离,提高安全性
    • 权限控制和资源访问限制
  3. 开发效率

    • 使用Java/Kotlin开发,开发效率高
    • 不需要为不同架构编译不同版本
    • 代码复用性好
  4. 动态特性

    • 支持动态加载
    • 支持热更新(通过技术手段)
    • 运行时优化

对比:

  • 直接编译为机器码:性能最好,但需要为不同架构编译,开发复杂
  • 使用虚拟机:性能稍差,但跨平台、安全性好、开发效率高

1.11 ART相比Dalvik的优势是什么?

答案:

ART相比Dalvik的主要优势是性能提升更好的内存管理

主要优势:

  1. 性能提升

    • AOT编译后直接执行机器码,运行速度快
    • 减少运行时编译开销
    • 更好的代码优化
  2. 内存管理

    • 改进的垃圾回收器
    • 更高效的内存分配
    • 减少GC暂停时间
  3. 启动优化

    • 混合编译模式优化启动速度
    • 预编译常用代码
    • 减少首次启动时间
  4. 64位支持

    • 更好的64位支持
    • 提升大内存应用性能
  5. 调试支持

    • 更好的调试工具支持
    • 更详细的性能分析

性能对比:

  • 运行速度:ART比Dalvik快约2-3倍
  • 启动速度:混合模式下接近Dalvik
  • 内存占用:ART略高,但可接受

1.12 AOT编译和JIT编译的优缺点是什么?

答案:

AOT和JIT各有优缺点,Android采用混合模式平衡两者。

AOT编译优缺点:

优点:

  • 运行时性能好(直接执行机器码)
  • 减少运行时编译开销
  • 更好的代码优化机会

缺点:

  • 安装时间长
  • 存储占用大(机器码比字节码大)
  • 首次启动可能较慢

JIT编译优缺点:

优点:

  • 安装快(不需要编译)
  • 存储占用小
  • 启动快

缺点:

  • 运行时需要编译,影响性能
  • 首次执行较慢
  • 编译开销在运行时

混合编译的优势:

  • 平衡安装速度、启动速度和运行性能
  • 根据使用情况动态优化
  • 兼顾用户体验和性能

1.13 混合编译模式是什么?

答案:

混合编译模式是Android 7.0+引入的AOT+JIT混合编译策略,结合两者的优势。

工作机制:

  1. 安装阶段

    • 不进行AOT编译,快速安装
    • 保持DEX字节码格式
  2. 运行阶段

    • JIT编译热点代码(频繁执行的代码)
    • 将编译结果缓存
  3. 后台优化

    • 设备空闲时,AOT编译常用代码
    • 提升后续运行性能
  4. 自适应优化

    • 根据应用使用情况调整编译策略
    • 优化常用代码路径

优势:

  • 快速安装:安装时不需要编译
  • 快速启动:启动时使用JIT,启动快
  • 高性能运行:常用代码已AOT编译,运行快
  • 智能优化:根据使用情况自动优化

1.14 Android的四大组件是什么?

答案:

Android的四大组件是ActivityServiceBroadcastReceiverContentProvider,是Android应用开发的核心。

四大组件:

  1. Activity(活动)

    • 作用:用户界面组件
    • 特点:每个Activity代表一个屏幕
    • 用途:展示UI,处理用户交互
  2. Service(服务)

    • 作用:后台服务组件
    • 特点:无界面,在后台运行
    • 用途:执行长时间运行的任务
  3. BroadcastReceiver(广播接收器)

    • 作用:接收系统或应用广播
    • 特点:响应系统事件
    • 用途:监听系统事件,执行相应操作
  4. ContentProvider(内容提供者)

    • 作用:数据共享组件
    • 特点:提供统一的数据访问接口
    • 用途:应用间数据共享

共同特点:

  • 都需要在AndroidManifest.xml中注册
  • 都有生命周期
  • 都通过Intent启动或通信

1.15 系统服务(System Services)的作用是什么?

答案:

系统服务是Android系统提供的核心服务,为应用提供系统级功能。

主要系统服务:

  1. ActivityManager

    • 管理Activity生命周期
    • 任务栈管理
    • 进程管理
  2. WindowManager

    • 窗口管理
    • 窗口层级管理
    • 输入事件分发
  3. PackageManager

    • 应用包管理
    • 权限管理
    • 应用信息查询
  4. LocationManager

    • 位置服务
    • GPS定位
    • 网络定位
  5. NotificationManager

    • 通知管理
    • 通知显示
    • 通知权限
  6. AlarmManager

    • 定时任务
    • 系统唤醒
    • 后台任务调度

特点:

  • 系统级服务,应用通过API访问
  • 单例模式,全局唯一
  • 提供系统核心功能

1.16 四大组件的特点是什么?

答案:

四大组件各有特点,共同构成Android应用的基础架构。

Activity特点:

  • 有用户界面
  • 生命周期管理
  • 任务栈管理
  • 通过Intent启动

Service特点:

  • 无用户界面
  • 后台运行
  • 支持绑定和启动两种模式
  • 可以跨进程

BroadcastReceiver特点:

  • 无界面
  • 响应广播事件
  • 生命周期短暂
  • 可以静态或动态注册

ContentProvider特点:

  • 数据共享接口
  • 统一数据访问
  • 支持跨进程访问
  • 基于URI访问

共同特点:

  • 都需要注册
  • 都有生命周期
  • 都通过Intent通信
  • 都运行在主进程

1.17 四大组件的生命周期如何管理?

答案:

四大组件的生命周期由系统服务管理,每个组件都有特定的生命周期方法。

生命周期管理:

  1. Activity生命周期

    • 由ActivityManager管理
    • 生命周期方法:onCreate、onStart、onResume、onPause、onStop、onDestroy
    • 受用户操作和系统资源影响
  2. Service生命周期

    • 由ActivityManager管理
    • 启动模式:onCreate、onStartCommand、onDestroy
    • 绑定模式:onCreate、onBind、onUnbind、onDestroy
  3. BroadcastReceiver生命周期

    • 由ActivityManager管理
    • 生命周期短暂:onReceive执行完即结束
    • 静态注册:系统管理
    • 动态注册:需要手动注销
  4. ContentProvider生命周期

    • 由ActivityManager管理
    • 生命周期方法:onCreate(应用启动时创建)
    • 生命周期与应用进程绑定

管理机制:

  • 系统服务统一管理
  • 根据系统状态调整
  • 资源不足时可能被回收

1.18 Android应用进程的启动流程是什么?

答案:

Android应用进程的启动流程涉及Zygote进程ActivityManagerService等多个系统组件。

启动流程:

  1. 用户启动应用

    • 点击应用图标
    • 或通过Intent启动
  2. ActivityManagerService处理

    • 检查应用是否已运行
    • 如果未运行,创建新进程
  3. Zygote进程fork

    • 从Zygote进程fork新进程
    • 继承Zygote的预加载资源
  4. 进程初始化

    • 创建应用进程
    • 加载应用代码
    • 初始化运行时环境
  5. Application创建

    • 创建Application实例
    • 调用onCreate()
  6. Activity创建

    • 创建Activity实例
    • 调用生命周期方法
    • 显示界面

关键点:

  • Zygote预加载常用类,加快启动速度
  • 进程隔离,每个应用独立进程
  • 系统统一管理进程生命周期

1.19 Zygote进程的作用是什么?

答案:

Zygote进程是Android系统的进程孵化器,负责创建应用进程。

主要作用:

  1. 进程创建

    • 通过fork()创建新进程
    • 新进程继承Zygote的预加载资源
  2. 预加载资源

    • 预加载常用类库
    • 预加载系统资源
    • 减少应用启动时间
  3. 资源共享

    • 多个应用进程共享Zygote的代码段
    • 节省内存占用
  4. 快速启动

    • 避免每个应用都重新加载类库
    • 提升应用启动速度

工作流程:

Zygote进程启动
  ↓
预加载常用类库和资源
  ↓
等待fork请求
  ↓
收到请求后fork新进程
  ↓
新进程继承预加载资源
  ↓
加载应用特定代码
  ↓
应用进程启动完成

优势:

  • 快速启动应用
  • 节省内存(代码段共享)
  • 统一管理进程创建

1.20 应用进程和系统进程的区别是什么?

答案:

应用进程和系统进程在权限优先级生命周期上有重要区别。

主要区别:

特性应用进程系统进程
权限受限权限系统权限
优先级较低较高
生命周期可被回收常驻内存
资源访问受限完整访问
进程名包名system_server等

应用进程特点:

  • 运行第三方应用
  • 权限受限
  • 系统资源不足时可能被回收
  • 进程名通常是应用包名

系统进程特点:

  • 运行系统服务
  • 拥有系统权限
  • 常驻内存,不易被回收
  • 进程名如system_server、zygote等

进程优先级:

  1. 前台进程(Foreground)
  2. 可见进程(Visible)
  3. 服务进程(Service)
  4. 后台进程(Background)
  5. 空进程(Empty)

系统进程通常具有更高的优先级,确保系统稳定运行。


第二章:Activity(35 题)

2.1 Activity是什么?它的生命周期是什么?

答案:

Activity是Android的用户界面组件,代表应用中的一个屏幕,负责展示UI和处理用户交互。

Activity的定义:

  • 一个Activity代表一个用户界面
  • 每个Activity都是独立的组件
  • 通过Activity栈管理多个Activity

生命周期方法:

  1. onCreate()

    • Activity创建时调用
    • 初始化工作
    • 设置布局
  2. onStart()

    • Activity可见但不可交互
    • 在onCreate()之后调用
  3. onResume()

    • Activity可见且可交互
    • 用户可以与Activity交互
  4. onPause()

    • Activity失去焦点
    • 部分可见或不可见
  5. onStop()

    • Activity完全不可见
    • 在onPause()之后调用
  6. onDestroy()

    • Activity销毁时调用
    • 清理资源
  7. onRestart()

    • Activity从停止状态重新启动
    • 在onStop()之后,onStart()之前

生命周期流程图:

启动:onCreate() → onStart() → onResume()
暂停:onPause() → onStop()
恢复:onRestart() → onStart() → onResume()
销毁:onPause() → onStop() → onDestroy()

2.2 Activity的生命周期方法有哪些?分别在什么时候调用?

答案:

Activity有7个生命周期方法,在不同场景下按顺序调用。

生命周期方法:

  1. onCreate(Bundle savedInstanceState)

    • 调用时机:Activity首次创建时
    • 作用:初始化Activity,设置布局
    • 特点:只会调用一次(除非Activity被销毁重建)
  2. onStart()

    • 调用时机:Activity变为可见时
    • 作用:准备显示Activity
    • 特点:可能在onResume()之前或onRestart()之后调用
  3. onResume()

    • 调用时机:Activity获得焦点,可以交互时
    • 作用:恢复Activity的交互功能
    • 特点:Activity处于前台,用户可见可交互
  4. onPause()

    • 调用时机:Activity失去焦点时
    • 作用:暂停Activity,释放资源
    • 特点:另一个Activity显示时调用
  5. onStop()

    • 调用时机:Activity完全不可见时
    • 作用:停止Activity
    • 特点:Activity被其他Activity完全覆盖时调用
  6. onRestart()

    • 调用时机:Activity从停止状态重新启动时
    • 作用:重新启动Activity
    • 特点:在onStop()之后,onStart()之前调用
  7. onDestroy()

    • 调用时机:Activity被销毁时
    • 作用:清理资源,释放内存
    • 特点:Activity生命周期结束

调用场景:

正常启动:

onCreate() → onStart() → onResume()

返回后台:

onPause() → onStop()

重新显示:

onRestart() → onStart() → onResume()

销毁:

onPause() → onStop() → onDestroy()

2.3 onCreate()、onStart()、onResume()的区别是什么?

答案:

这三个方法在Activity启动时依次调用,但职责不同

onCreate():

  • 调用时机:Activity首次创建时
  • 职责:初始化工作,设置布局
  • 特点:只调用一次
  • 使用场景
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initData();
    }
    

onStart():

  • 调用时机:Activity变为可见时
  • 职责:准备显示Activity
  • 特点:可能多次调用(onRestart()后会再次调用)
  • 使用场景
    @Override
    protected void onStart() {
        super.onStart();
        // 注册广播接收器
        // 启动位置服务等
    }
    

onResume():

  • 调用时机:Activity获得焦点,可以交互时
  • 职责:恢复交互功能
  • 特点:Activity处于前台
  • 使用场景
    @Override
    protected void onResume() {
        super.onResume();
        // 恢复动画
        // 恢复数据刷新等
    }
    

区别总结:

  • onCreate():初始化,只调用一次
  • onStart():可见,可能多次调用
  • onResume():可交互,Activity在前台

2.4 onPause()、onStop()、onDestroy()的区别是什么?

答案:

这三个方法在Activity退出时依次调用,但时机和职责不同

onPause():

  • 调用时机:Activity失去焦点时
  • 职责:暂停Activity,快速释放资源
  • 特点:另一个Activity显示时立即调用
  • 使用场景
    @Override
    protected void onPause() {
        super.onPause();
        // 暂停动画
        // 保存临时数据
        // 释放相机等资源
    }
    

onStop():

  • 调用时机:Activity完全不可见时
  • 职责:停止Activity
  • 特点:在onPause()之后调用
  • 使用场景
    @Override
    protected void onStop() {
        super.onStop();
        // 停止网络请求
        // 注销广播接收器
    }
    

onDestroy():

  • 调用时机:Activity被销毁时
  • 职责:清理所有资源
  • 特点:Activity生命周期结束
  • 使用场景
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 清理资源
        // 取消所有任务
    }
    

区别总结:

  • onPause():失去焦点,快速处理
  • onStop():完全不可见,停止操作
  • onDestroy():销毁,清理资源

2.5 Activity的生命周期流程图是什么?

答案:

Activity的生命周期流程可以通过状态图表示。

完整生命周期流程:

启动流程:
┌─────────┐
│ 不存在  │
└────┬────┘
     │ onCreate()
     ↓
┌─────────┐
│ Created │
└────┬────┘
     │ onStart()
     ↓
┌─────────┐
│ Started │
└────┬────┘
     │ onResume()
     ↓
┌──────────┐
│ Resumed  │ ← Activity运行中
└────┬─────┘
     │ onPause()
     ↓
┌─────────┐
│ Paused  │
└────┬────┘
     │ onStop()
     ↓
┌─────────┐
│ Stopped │
└────┬────┘
     │ onRestart() 或 onDestroy()
     ↓
┌──────────┐      ┌──────────┐
│ Restart  │      │ Destroyed│
└────┬─────┘      └──────────┘
     │ onStart()
     ↓
┌─────────┐
│ Started │
└─────────┘

关键状态:

  • Created:Activity已创建,但不可见
  • Started:Activity可见,但不可交互
  • Resumed:Activity可见且可交互(运行中)
  • Paused:Activity部分可见或失去焦点
  • Stopped:Activity完全不可见
  • Destroyed:Activity已销毁

2.6 Activity的生命周期和进程生命周期的关系是什么?

答案:

Activity的生命周期受进程生命周期影响,但两者不完全一致。

关系说明:

  1. 进程存活时

    • Activity可以正常经历完整生命周期
    • 系统资源充足时,Activity生命周期正常
  2. 进程被回收时

    • Activity可能被系统回收
    • 系统会调用onSaveInstanceState()保存状态
    • 进程重启后,Activity会重建并恢复状态
  3. 内存不足时

    • 系统优先回收后台Activity
    • 前台Activity通常不会被回收
    • 回收顺序:空进程 → 后台进程 → 服务进程

进程优先级影响:

  • 前台进程:Activity生命周期正常
  • 可见进程:可能调用onPause(),但不会onDestroy()
  • 后台进程:可能被回收,调用onSaveInstanceState()后onDestroy()

状态保存:

  • 系统会在Activity可能被回收前调用onSaveInstanceState()
  • 保存的数据在onCreate()或onRestoreInstanceState()中恢复

2.7 Activity的启动模式有哪些?它们的区别是什么?

答案:

Activity有四种启动模式,控制Activity在任务栈中的行为。

启动模式:

  1. standard(标准模式)

    • 特点:每次启动都创建新实例
    • 任务栈:在启动它的Activity所在栈中
    • 使用场景:大多数情况
  2. singleTop(栈顶复用)

    • 特点:如果Activity在栈顶,复用该实例
    • 任务栈:在启动它的Activity所在栈中
    • 使用场景:防止重复启动(如通知点击)
  3. singleTask(栈内复用)

    • 特点:如果Activity在栈中存在,复用并清除其上所有Activity
    • 任务栈:在指定的任务栈中(通过taskAffinity)
    • 使用场景:主界面、登录页
  4. singleInstance(单例模式)

    • 特点:Activity独占一个任务栈
    • 任务栈:独立的任务栈
    • 使用场景:系统Launcher、来电界面

区别对比:

启动模式实例数量任务栈使用场景
standard多个当前栈默认
singleTop栈顶复用当前栈防止重复
singleTask栈内复用指定栈主界面
singleInstance单例独立栈系统应用

2.8 standard启动模式的特点是什么?

答案:

standard是默认启动模式,每次启动都创建新的Activity实例。

特点:

  1. 每次创建新实例

    • 每次调用startActivity()都创建新实例
    • 不检查是否已存在
  2. 任务栈行为

    • 在启动它的Activity所在任务栈中
    • 遵循后进先出(LIFO)原则
  3. 生命周期

    • 每次启动都调用onCreate()
    • 正常经历完整生命周期

示例:

// Activity A启动Activity B(standard模式)
// 任务栈:A → B
// 再次启动B
// 任务栈:A → B → B(新实例)

使用场景:

  • 大多数Activity使用此模式
  • 需要多个实例的场景
  • 不需要特殊栈管理的场景

注意事项:

  • 可能创建多个实例,占用内存
  • 返回键会依次关闭,可能体验不佳

2.9 singleTop启动模式的特点是什么?

答案:

singleTop是栈顶复用模式,如果Activity在栈顶则复用,否则创建新实例。

特点:

  1. 栈顶复用

    • 如果Activity在栈顶,复用该实例
    • 调用onNewIntent()而不是onCreate()
    • 如果不在栈顶,创建新实例
  2. 任务栈行为

    • 在启动它的Activity所在任务栈中
    • 复用时不改变栈结构
  3. 生命周期

    • 复用:onNewIntent() → onResume()
    • 新建:正常生命周期

示例:

// 任务栈:A → B(B是singleTop)
// 在B中启动B
// 结果:A → B(复用B,调用onNewIntent())

// 任务栈:A → B → C
// 在C中启动B(B是singleTop)
// 结果:A → B → C → B(创建新实例)

使用场景:

  • 防止重复启动(如通知点击)
  • 搜索页面
  • 详情页面

2.10 singleTask启动模式的特点是什么?

答案:

singleTask是栈内复用模式,如果Activity在栈中存在,复用并清除其上所有Activity。

特点:

  1. 栈内复用

    • 检查任务栈中是否存在该Activity
    • 如果存在,复用并清除其上所有Activity
    • 如果不存在,创建新实例
  2. 任务栈行为

    • 在指定的任务栈中(通过taskAffinity)
    • 如果没有指定,在启动它的Activity所在栈中
  3. 生命周期

    • 复用:onNewIntent() → onResume()
    • 新建:正常生命周期
    • 清除:其上Activity调用onDestroy()

示例:

// 任务栈:A → B → C(B是singleTask)
// 在C中启动B
// 结果:A → B(复用B,清除C,调用onNewIntent())

使用场景:

  • 主界面(MainActivity)
  • 登录页面
  • 需要作为栈底Activity的场景

2.11 singleInstance启动模式的特点是什么?

答案:

singleInstance是单例模式,Activity独占一个任务栈,系统中只有一个实例。

特点:

  1. 独占任务栈

    • Activity独占一个任务栈
    • 该栈中只有这一个Activity
  2. 单例

    • 系统中只有一个实例
    • 其他Activity启动它时,复用该实例
  3. 任务栈行为

    • 独立的任务栈
    • 不受其他Activity影响

示例:

// 任务栈1:A → B
// 启动C(singleInstance)
// 任务栈1:A → B
// 任务栈2:C(独立栈)

// 从任务栈1启动C
// 结果:切换到任务栈2,复用C

使用场景:

  • 系统Launcher
  • 来电界面
  • 需要完全独立的Activity

注意事项:

  • 使用较少
  • 可能影响用户体验(切换任务栈)

2.12 如何选择合适的启动模式?

答案:

根据Activity的使用场景业务需求选择合适的启动模式。

选择标准:

  1. standard(默认)

    • 大多数Activity
    • 需要多个实例的场景
    • 不需要特殊管理的场景
  2. singleTop

    • 防止重复启动
    • 通知点击跳转
    • 搜索、详情页面
  3. singleTask

    • 主界面(MainActivity)
    • 登录页面
    • 需要作为栈底的Activity
  4. singleInstance

    • 系统级应用
    • 需要完全独立的Activity
    • 使用较少

选择建议:

  • 默认使用standard
  • 防止重复启动用singleTop
  • 主界面用singleTask
  • 谨慎使用singleInstance

2.13 启动模式的Task栈管理是什么?

答案:

Task(任务)是Activity的集合,按照后进先出(LIFO)原则管理。

Task栈管理:

  1. Task概念

    • Task是Activity的集合
    • 用户完成一个任务的所有Activity
    • 通过返回键可以返回上一个Activity
  2. 栈管理规则

    • 后进先出(LIFO)
    • 新Activity压入栈顶
    • 返回键弹出栈顶Activity
  3. 不同启动模式的影响

    • standard:正常入栈
    • singleTop:栈顶复用,不入栈
    • singleTask:清除其上Activity,可能切换Task
    • singleInstance:独立Task

Task切换:

  • 通过taskAffinity指定Task
  • singleTask可以指定不同的Task
  • 系统Launcher是一个特殊的Task

2.14 singleTask的TaskAffinity是什么?

答案:

TaskAffinity是Activity的任务 affinity,用于指定Activity所属的任务栈。

TaskAffinity说明:

  1. 默认值

    • 默认是应用的包名
    • 同一应用的Activity默认在同一Task
  2. 自定义TaskAffinity

    <activity
        android:name=".MainActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.example.main" />
    
  3. singleTask的行为

    • 如果指定了taskAffinity,会在指定的Task中查找
    • 如果Task不存在,创建新Task
    • 如果Task存在且Activity存在,复用

使用场景:

  • 需要将Activity放在特定Task中
  • 跨应用的Activity共享Task
  • 复杂的导航场景

2.15 如何通过代码设置启动模式?

答案:

可以通过Intent标志在代码中设置启动模式。

设置方法:

  1. FLAG_ACTIVITY_NEW_TASK

    • 在新Task中启动Activity
    • 类似singleTask
  2. FLAG_ACTIVITY_SINGLE_TOP

    • 栈顶复用
    • 类似singleTop
  3. FLAG_ACTIVITY_CLEAR_TOP

    • 清除其上Activity
    • 类似singleTask

代码示例:

Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
                Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

注意事项:

  • 代码设置会覆盖AndroidManifest中的设置
  • 标志可以组合使用
  • 某些标志组合有特定含义

2.16 Activity的onSaveInstanceState()和onRestoreInstanceState()的作用是什么?

答案:

这两个方法用于保存和恢复Activity的状态,防止Activity被系统回收后数据丢失。

onSaveInstanceState():

  • 作用:保存Activity状态
  • 调用时机:Activity可能被系统回收前
  • 保存内容:临时数据、UI状态等
  • 特点:系统自动调用,也可以手动调用

onRestoreInstanceState():

  • 作用:恢复Activity状态
  • 调用时机:Activity重建后,onStart()之后
  • 恢复内容:从Bundle中恢复数据
  • 特点:只在Activity被系统回收重建时调用

使用示例:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("key", "value");
    outState.putInt("count", count);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    String value = savedInstanceState.getString("key");
    int count = savedInstanceState.getInt("count");
}

注意事项:

  • 只保存临时数据,持久数据应使用其他方式
  • Bundle有大小限制(约1MB)
  • 系统会自动保存View的状态(有id的View)

2.17 onSaveInstanceState()什么时候调用?

答案:

onSaveInstanceState()在Activity可能被系统回收时调用。

调用时机:

  1. 系统回收前

    • 内存不足时
    • 配置变更时(如横竖屏切换)
    • 系统需要回收Activity时
  2. 不调用的情况

    • 用户主动退出(按返回键)
    • 调用finish()
    • 正常销毁不会调用
  3. 调用顺序

    • 在onPause()之后
    • 可能在onStop()之前或之后
    • 不保证调用顺序

调用场景:

  • 横竖屏切换
  • 多窗口模式切换
  • 系统内存不足
  • 应用进入后台

2.18 onRestoreInstanceState()什么时候调用?

答案:

onRestoreInstanceState()在Activity被系统回收后重建时调用。

调用时机:

  1. Activity重建后

    • 系统回收Activity后重建
    • 在onStart()之后调用
    • 在onResume()之前调用
  2. 调用条件

    • 必须有保存的状态(Bundle不为null)
    • 只在系统回收重建时调用
    • 用户主动退出不会调用
  3. 调用顺序

    onCreate() → onStart() → onRestoreInstanceState() → onResume()
    

使用建议:

  • 可以在onCreate()中恢复(Bundle可能为null)
  • 也可以在onRestoreInstanceState()中恢复(Bundle一定不为null)
  • 推荐在onRestoreInstanceState()中恢复,逻辑更清晰

2.19 哪些情况下Activity会被销毁并重建?

答案:

Activity在以下情况下会被销毁并重建

销毁重建场景:

  1. 配置变更

    • 横竖屏切换
    • 语言切换
    • 字体大小改变
    • 多窗口模式切换
  2. 系统回收

    • 内存不足
    • 应用进入后台
    • 系统需要释放资源
  3. 进程被杀死

    • 应用进程被系统杀死
    • 进程重启后Activity重建

不重建的情况:

  • 用户主动退出
  • 调用finish()
  • 正常销毁

处理方式:

  • 使用onSaveInstanceState()保存状态
  • 使用ViewModel保存数据
  • 配置android:configChanges避免重建

2.20 如何保存和恢复Activity的状态?

答案:

有多种方式保存和恢复Activity状态。

保存方式:

  1. onSaveInstanceState()

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("key", "value");
    }
    
  2. ViewModel

    ViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    viewModel.setData(data);
    
  3. SharedPreferences

    SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
    prefs.edit().putString("key", "value").apply();
    

恢复方式:

  1. onCreate()或onRestoreInstanceState()

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            String value = savedInstanceState.getString("key");
        }
    }
    
  2. ViewModel

    ViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    String data = viewModel.getData();
    

选择建议:

  • 临时数据:onSaveInstanceState()
  • 配置变更数据:ViewModel
  • 持久数据:SharedPreferences、数据库

2.21 ViewModel和onSaveInstanceState()的区别是什么?

答案:

ViewModel和onSaveInstanceState()都可以保存数据,但使用场景不同

区别对比:

特性ViewModelonSaveInstanceState()
保存时机配置变更时系统回收时
数据大小无限制约1MB限制
数据类型任意对象基本类型、Parcelable
生命周期配置变更后仍存在Activity重建后恢复
使用场景配置变更系统回收

ViewModel特点:

  • 配置变更(如横竖屏)时数据保留
  • 可以保存复杂对象
  • 与Activity生命周期关联

onSaveInstanceState()特点:

  • 系统回收时保存
  • 只能保存基本类型和Parcelable
  • Bundle有大小限制

使用建议:

  • 配置变更数据:使用ViewModel
  • 系统回收数据:使用onSaveInstanceState()
  • 两者结合:ViewModel + onSaveInstanceState()

2.22 Activity之间如何传递数据?

答案:

Activity之间通过Intent传递数据。

传递方式:

  1. 基本数据类型

    Intent intent = new Intent(this, SecondActivity.class);
    intent.putExtra("name", "张三");
    intent.putExtra("age", 25);
    startActivity(intent);
    
  2. 对象数据(Parcelable)

    // 实现Parcelable接口
    public class User implements Parcelable {
        private String name;
        private int age;
        // ... Parcelable实现
    }
    
    Intent intent = new Intent(this, SecondActivity.class);
    intent.putExtra("user", user);
    startActivity(intent);
    
  3. 接收数据

    Intent intent = getIntent();
    String name = intent.getStringExtra("name");
    int age = intent.getIntExtra("age", 0);
    User user = intent.getParcelableExtra("user");
    

注意事项:

  • 数据大小有限制(约1MB)
  • 复杂对象需要实现Parcelable
  • 大量数据考虑使用其他方式(数据库、文件等)

2.23 Intent的作用是什么?有哪些类型?

答案:

Intent是Android的消息传递机制,用于组件间通信。

Intent的作用:

  1. 启动组件

    • 启动Activity
    • 启动Service
    • 发送广播
  2. 传递数据

    • 组件间数据传递
    • 携带参数
  3. 隐式调用

    • 通过Action启动组件
    • 系统选择合适的组件

Intent类型:

  1. 显式Intent

    Intent intent = new Intent(this, SecondActivity.class);
    startActivity(intent);
    
    • 明确指定目标组件
    • 用于应用内调用
  2. 隐式Intent

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("https://www.example.com"));
    startActivity(intent);
    
    • 通过Action、Category、Data匹配
    • 系统选择合适的组件

Intent组成:

  • Component:目标组件
  • Action:动作
  • Category:类别
  • Data:数据URI
  • Extras:额外数据

2.24 startActivityForResult()的使用场景是什么?

答案:

startActivityForResult()用于从目标Activity获取返回结果

使用场景:

  1. 选择数据

    • 选择图片
    • 选择联系人
    • 选择文件
  2. 输入数据

    • 输入用户名
    • 填写表单
    • 确认操作

使用示例:

// 启动Activity并等待结果
Intent intent = new Intent(this, SecondActivity.class);
startActivityForResult(intent, REQUEST_CODE);

// 在目标Activity中返回结果
Intent resultIntent = new Intent();
resultIntent.putExtra("result", "data");
setResult(RESULT_OK, resultIntent);
finish();

// 在源Activity中接收结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
        String result = data.getStringExtra("result");
    }
}

注意事项:

  • 已废弃,推荐使用ActivityResult API
  • requestCode用于区分不同的请求
  • resultCode表示结果状态

2.25 Activity的onActivityResult()如何处理?

答案:

onActivityResult()用于接收从目标Activity返回的结果

处理方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    
    // 检查请求码
    if (requestCode == REQUEST_CODE) {
        // 检查结果码
        if (resultCode == RESULT_OK) {
            // 处理成功结果
            String result = data.getStringExtra("result");
        } else if (resultCode == RESULT_CANCELED) {
            // 处理取消
        }
    }
}

参数说明:

  • requestCode:启动Activity时传入的请求码
  • resultCode:结果码(RESULT_OK、RESULT_CANCELED等)
  • data:返回的Intent,包含数据

注意事项:

  • 已废弃,推荐使用ActivityResult API
  • 需要检查requestCode和resultCode
  • 可能为null,需要判空

2.26 ActivityResult API的使用方法是什么?

答案:

ActivityResult API是新的结果回调机制,替代startActivityForResult()。

使用方法:

  1. 注册ActivityResultLauncher

    private ActivityResultLauncher<Intent> launcher = 
        registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == RESULT_OK) {
                        Intent data = result.getData();
                        String result = data.getStringExtra("result");
                    }
                }
            }
        );
    
  2. 启动Activity

    Intent intent = new Intent(this, SecondActivity.class);
    launcher.launch(intent);
    
  3. Lambda简化

    private ActivityResultLauncher<Intent> launcher = 
        registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == RESULT_OK) {
                    // 处理结果
                }
            }
        );
    

优势:

  • 类型安全
  • 更好的生命周期管理
  • 支持多种Contract(拍照、选择文件等)

2.27 ActivityResult API和startActivityForResult()的区别是什么?

答案:

ActivityResult API是新的推荐方式,相比startActivityForResult()有诸多优势。

主要区别:

特性ActivityResult APIstartActivityForResult()
状态推荐使用已废弃
类型安全
生命周期自动管理手动处理
使用方式注册Launcher重写方法
灵活性

ActivityResult API优势:

  • 类型安全:编译时检查
  • 自动管理:生命周期自动处理
  • 更灵活:支持多种Contract
  • 代码清晰:逻辑更清晰

迁移建议:

  • 新项目使用ActivityResult API
  • 旧项目逐步迁移
  • 两者可以共存

2.28 Activity的启动流程是什么?

答案:

Activity的启动流程涉及多个系统组件的协作。

启动流程:

  1. 应用层调用

    startActivity(intent);
    
  2. ActivityManagerService处理

    • 检查目标Activity
    • 检查权限
    • 创建或复用进程
  3. 进程创建(如需要)

    • 从Zygote fork进程
    • 加载应用代码
    • 创建Application
  4. Activity创建

    • 创建Activity实例
    • 调用onCreate()
    • 创建Window
  5. 界面显示

    • 调用onStart()
    • 调用onResume()
    • 显示界面

关键步骤:

  • ActivityManagerService统一管理
  • 进程隔离,每个应用独立进程
  • 通过Binder IPC通信

2.29 Activity的onCreate()之前做了什么?

答案:

在onCreate()之前,系统进行了大量的初始化工作

onCreate()之前的工作:

  1. 进程创建(如需要)

    • 从Zygote fork进程
    • 加载应用代码
  2. Application创建

    • 创建Application实例
    • 调用Application.onCreate()
  3. Activity实例创建

    • 通过反射创建Activity实例
    • 初始化Activity对象
  4. Context创建

    • 创建Activity的Context
    • 关联Application Context
  5. Window创建

    • 创建PhoneWindow
    • 关联WindowManager
  6. 主题应用

    • 应用主题样式
    • 设置窗口属性

性能影响:

  • 这些操作影响启动速度
  • 优化启动需要减少这些开销

2.30 Activity的onResume()之后做了什么?

答案:

在onResume()之后,Activity进入可交互状态,系统进行界面渲染。

onResume()之后的工作:

  1. 界面渲染

    • View的测量、布局、绘制
    • 显示Activity界面
  2. 输入事件处理

    • 可以接收触摸事件
    • 可以接收键盘输入
  3. 动画开始

    • 启动动画
    • 恢复动画状态
  4. 数据刷新

    • 恢复数据加载
    • 刷新UI

关键点:

  • onResume()之后Activity完全可见可交互
  • 此时可以安全地进行UI操作
  • 界面渲染在onResume()之后进行

2.31 Activity的启动性能如何优化?

答案:

Activity启动性能优化可以从多个方面入手。

优化策略:

  1. 减少onCreate()工作量

    • 延迟初始化
    • 异步加载数据
    • 减少布局复杂度
  2. 优化布局

    • 减少布局层级
    • 使用ViewStub延迟加载
    • 避免过度绘制
  3. 预加载优化

    • 使用启动画面
    • 预加载数据
    • 使用缓存
  4. 代码优化

    • 避免主线程阻塞
    • 减少反射调用
    • 优化资源加载

最佳实践:

  • 使用启动画面提升体验
  • 延迟非关键初始化
  • 使用异步加载数据
  • 优化布局性能

2.32 Activity的内存泄漏如何避免?

答案:

Activity内存泄漏是常见问题,需要注意生命周期和引用关系

常见泄漏场景:

  1. 静态引用

    // 错误:静态引用Activity
    static Activity activity;
    
    // 正确:使用WeakReference
    static WeakReference<Activity> activityRef;
    
  2. Handler泄漏

    // 错误:非静态内部类持有Activity引用
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 持有Activity引用
        }
    };
    
    // 正确:使用静态内部类+WeakReference
    private static class MyHandler extends Handler {
        private WeakReference<Activity> activityRef;
    }
    
  3. 异步任务泄漏

    // 错误:AsyncTask持有Activity引用
    new AsyncTask() {
        // 持有Activity引用
    }.execute();
    
    // 正确:在onDestroy()中取消
    @Override
    protected void onDestroy() {
        super.onDestroy();
        asyncTask.cancel(true);
    }
    

避免方法:

  • 避免静态引用Activity
  • 使用WeakReference
  • 及时取消异步任务
  • 使用LeakCanary检测

2.33 Activity的异常退出如何处理?

答案:

Activity异常退出需要通过异常捕获状态恢复处理。

处理方式:

  1. 全局异常捕获

    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            // 记录异常
            // 保存状态
            // 重启应用或显示错误页面
        }
    });
    
  2. try-catch保护

    try {
        // 可能崩溃的代码
    } catch (Exception e) {
        // 处理异常
        e.printStackTrace();
    }
    
  3. 状态恢复

    • 使用onSaveInstanceState()保存状态
    • 异常退出后可以恢复

最佳实践:

  • 关键操作使用try-catch
  • 记录异常日志
  • 提供友好的错误提示
  • 使用崩溃监控工具(如Firebase Crashlytics)

2.34 Activity的横竖屏切换如何处理?

答案:

横竖屏切换会导致Activity销毁重建,需要保存和恢复状态。

处理方式:

  1. 保存状态

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("key", "value");
    }
    
  2. 恢复状态

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            String value = savedInstanceState.getString("key");
        }
    }
    
  3. 避免重建(不推荐)

    <activity
        android:name=".MainActivity"
        android:configChanges="orientation|screenSize" />
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // 手动处理配置变更
    }
    

推荐方式:

  • 使用ViewModel保存数据
  • 使用onSaveInstanceState()保存UI状态
  • 避免使用configChanges(除非必要)

2.35 Activity的最佳实践有哪些?

答案:

Activity最佳实践包括生命周期管理内存管理性能优化等。

最佳实践:

  1. 生命周期管理

    • 在正确的生命周期方法中执行操作
    • 及时释放资源
    • 避免在onCreate()中执行耗时操作
  2. 内存管理

    • 避免内存泄漏
    • 使用WeakReference
    • 及时取消异步任务
  3. 性能优化

    • 优化启动速度
    • 减少布局复杂度
    • 使用异步加载
  4. 状态管理

    • 使用ViewModel保存数据
    • 使用onSaveInstanceState()保存UI状态
    • 合理使用启动模式
  5. 代码规范

    • 单一职责
    • 避免在Activity中写业务逻辑
    • 使用MVP/MVVM架构

总结:

  • 遵循生命周期
  • 注意内存管理
  • 优化性能
  • 合理架构设计

第三章:Fragment(30 题)

3.1 Fragment是什么?它的生命周期是什么?

答案:

Fragment是Android的UI片段组件,可以嵌入到Activity中,提供模块化的UI和逻辑复用。

Fragment的定义:

  • 一个Fragment代表Activity中的一部分UI
  • 可以在多个Activity中复用
  • 有自己的生命周期,但受Activity生命周期影响

生命周期方法:

  1. onAttach()

    • Fragment与Activity关联时调用
    • 获取Activity引用
  2. onCreate()

    • Fragment创建时调用
    • 初始化Fragment
  3. onCreateView()

    • 创建Fragment的视图
    • 返回根视图
  4. onActivityCreated()

    • Activity的onCreate()完成后调用
    • 可以安全访问Activity
  5. onStart()

    • Fragment可见时调用
  6. onResume()

    • Fragment可交互时调用
  7. onPause()

    • Fragment失去焦点时调用
  8. onStop()

    • Fragment不可见时调用
  9. onDestroyView()

    • 销毁Fragment的视图
  10. onDestroy()

    • Fragment销毁时调用
  11. onDetach()

    • Fragment与Activity分离时调用

生命周期流程图:

onAttach() → onCreate() → onCreateView() → onActivityCreated() 
→ onStart() → onResume() → onPause() → onStop() 
→ onDestroyView() → onDestroy() → onDetach()

3.2 Fragment的生命周期和Activity的生命周期有什么关系?

答案:

Fragment的生命周期受Activity生命周期影响,两者紧密关联。

关系说明:

  1. Fragment生命周期包含在Activity生命周期中

    • Fragment不能独立存在
    • 必须依附于Activity
  2. 生命周期对应关系

    Activity.onCreate() 
      → Fragment.onAttach()
      → Fragment.onCreate()
      → Fragment.onCreateView()
      → Fragment.onActivityCreated()
    
    Activity.onStart()
      → Fragment.onStart()
    
    Activity.onResume()
      → Fragment.onResume()
    
    Activity.onPause()
      → Fragment.onPause()
    
    Activity.onStop()
      → Fragment.onStop()
    
    Activity.onDestroy()
      → Fragment.onDestroyView()
      → Fragment.onDestroy()
      → Fragment.onDetach()
    
  3. 关键点

    • Fragment的onAttach()在Activity.onCreate()之前
    • Fragment的onActivityCreated()在Activity.onCreate()之后
    • Fragment的onDestroy()在Activity.onDestroy()之前

注意事项:

  • Fragment的生命周期方法在对应的Activity生命周期方法中调用
  • Activity销毁时,所有Fragment也会销毁
  • Fragment可以感知Activity的生命周期变化

3.3 Fragment的生命周期方法有哪些?

答案:

Fragment有11个生命周期方法,按顺序调用。

生命周期方法:

  1. onAttach(Context context)

    • Fragment与Activity关联
    • 获取Activity引用
  2. onCreate(Bundle savedInstanceState)

    • Fragment创建
    • 初始化Fragment
  3. onCreateView(LayoutInflater, ViewGroup, Bundle)

    • 创建Fragment的视图
    • 返回根视图
  4. onActivityCreated(Bundle savedInstanceState)

    • Activity的onCreate()完成后调用
    • 可以安全访问Activity
  5. onStart()

    • Fragment可见
  6. onResume()

    • Fragment可交互
  7. onPause()

    • Fragment失去焦点
  8. onStop()

    • Fragment不可见
  9. onDestroyView()

    • 销毁Fragment的视图
  10. onDestroy()

    • Fragment销毁
  11. onDetach()

    • Fragment与Activity分离

调用顺序:

创建:onAttach() → onCreate() → onCreateView() → onActivityCreated() 
→ onStart() → onResume()

销毁:onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()

3.4 Fragment的创建方式有哪些?

答案:

Fragment有两种创建方式:静态创建动态创建

1. 静态创建(XML布局)

在Activity的布局文件中直接声明Fragment:

<fragment
    android:id="@+id/fragment"
    android:name="com.example.MyFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

特点:

  • 在XML中声明
  • 不能动态替换
  • 简单直接

2. 动态创建(代码)

通过代码动态添加Fragment:

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.container, new MyFragment());
transaction.commit();

特点:

  • 灵活,可以动态添加、替换、移除
  • 可以添加到回退栈
  • 更常用

使用场景:

  • 静态创建:固定不变的Fragment
  • 动态创建:需要动态管理的Fragment

3.5 Fragment的静态创建和动态创建的区别是什么?

答案:

静态创建和动态创建在使用方式灵活性适用场景上有区别。

主要区别:

特性静态创建动态创建
声明方式XML布局代码
灵活性
替换不能可以
回退栈不支持支持
使用场景固定Fragment动态Fragment

静态创建:

  • 在XML中声明
  • 简单直接
  • 不能动态替换
  • 适合固定不变的Fragment

动态创建:

  • 通过代码添加
  • 灵活,可以动态管理
  • 可以添加到回退栈
  • 适合需要动态切换的Fragment

推荐:

  • 大多数情况使用动态创建
  • 静态创建仅用于固定不变的Fragment

3.6 Fragment的add()和replace()的区别是什么?

答案:

add()和replace()是添加Fragment的两种方式,行为不同

add()方法:

  • 行为:在容器中添加Fragment,不移除现有Fragment
  • 结果:多个Fragment可能重叠
  • 使用场景:需要同时显示多个Fragment
transaction.add(R.id.container, fragment1);
transaction.add(R.id.container, fragment2);
// 结果:fragment1和fragment2都存在于容器中(可能重叠)

replace()方法:

  • 行为:替换容器中的Fragment,移除现有Fragment
  • 结果:容器中只有一个Fragment
  • 使用场景:切换Fragment
transaction.replace(R.id.container, fragment1);
transaction.replace(R.id.container, fragment2);
// 结果:fragment1被移除,只有fragment2存在

区别总结:

  • add():添加,不移除现有Fragment
  • replace():替换,移除现有Fragment

选择建议:

  • 需要同时显示多个Fragment:使用add()
  • 需要切换Fragment:使用replace()

3.7 Fragment的show()和hide()的作用是什么?

答案:

show()和hide()用于显示和隐藏Fragment,不销毁Fragment。

show()方法:

  • 作用:显示Fragment
  • 特点:Fragment已存在,只是显示
  • 生命周期:不触发生命周期方法
transaction.show(fragment);

hide()方法:

  • 作用:隐藏Fragment
  • 特点:Fragment仍存在,只是隐藏
  • 生命周期:不触发生命周期方法
transaction.hide(fragment);

使用场景:

  • 需要频繁切换Fragment
  • 需要保持Fragment状态
  • 避免重复创建和销毁

优势:

  • 性能好(不创建销毁)
  • 保持Fragment状态
  • 切换快速

注意事项:

  • 使用show/hide时,Fragment的onHiddenChanged()会被调用
  • 需要手动管理Fragment的显示状态

3.8 Fragment的attach()和detach()的作用是什么?

答案:

attach()和detach()用于关联和分离Fragment,会触发部分生命周期方法。

attach()方法:

  • 作用:重新关联Fragment
  • 生命周期:调用onAttach()、onCreateView()、onActivityCreated()等
  • 使用场景:重新显示之前detach的Fragment
transaction.attach(fragment);

detach()方法:

  • 作用:分离Fragment,但保留Fragment实例
  • 生命周期:调用onDestroyView(),但不会调用onDestroy()
  • 使用场景:暂时移除Fragment,但保留状态
transaction.detach(fragment);

与remove()的区别:

  • detach():分离,保留Fragment实例
  • remove():移除,销毁Fragment实例

使用场景:

  • 需要暂时移除Fragment但保留状态:使用detach()
  • 需要完全移除Fragment:使用remove()

3.9 什么时候使用add(),什么时候使用replace()?

答案:

根据业务需求Fragment关系选择使用add()或replace()。

使用add()的场景:

  • 需要同时显示多个Fragment
  • Fragment之间需要叠加显示
  • 使用show/hide切换Fragment

使用replace()的场景:

  • 需要切换Fragment(一个容器只显示一个)
  • 不需要保留之前的Fragment
  • 简单的Fragment切换

示例:

使用add() + show/hide:

// 添加多个Fragment
transaction.add(R.id.container, fragment1);
transaction.add(R.id.container, fragment2);
transaction.commit();

// 切换显示
transaction.hide(fragment1);
transaction.show(fragment2);
transaction.commit();

使用replace():

// 切换Fragment
transaction.replace(R.id.container, fragment1);
transaction.commit();
// 再次切换
transaction.replace(R.id.container, fragment2);
transaction.commit();

推荐:

  • 需要频繁切换:使用add() + show/hide
  • 简单切换:使用replace()

3.10 Fragment和Activity如何通信?

答案:

Fragment和Activity有多种通信方式。

1. 通过接口回调

Fragment定义接口,Activity实现:

// Fragment中
public interface OnFragmentListener {
    void onFragmentAction(String data);
}

private OnFragmentListener listener;

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentListener) {
        listener = (OnFragmentListener) context;
    }
}

// 调用
listener.onFragmentAction("data");

// Activity中实现接口
public class MainActivity extends AppCompatActivity implements OnFragmentListener {
    @Override
    public void onFragmentAction(String data) {
        // 处理数据
    }
}

2. 通过getActivity()

Fragment直接访问Activity:

// Fragment中
Activity activity = getActivity();
if (activity instanceof MainActivity) {
    MainActivity mainActivity = (MainActivity) activity;
    mainActivity.doSomething();
}

3. 通过ViewModel

共享ViewModel:

// Fragment和Activity共享同一个ViewModel
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());
MyViewModel viewModel = viewModelProvider.get(MyViewModel.class);

4. 通过EventBus等事件总线

使用第三方库进行通信。

推荐:

  • 简单通信:接口回调
  • 数据共享:ViewModel
  • 复杂通信:EventBus

3.11 Fragment之间如何通信?

答案:

Fragment之间可以通过Activity中转ViewModel共享事件总线等方式通信。

1. 通过Activity中转

Fragment A → Activity → Fragment B:

// Fragment A
((MainActivity) getActivity()).sendDataToFragmentB("data");

// Activity中
public void sendDataToFragmentB(String data) {
    FragmentB fragmentB = (FragmentB) getSupportFragmentManager()
        .findFragmentById(R.id.fragment_b);
    if (fragmentB != null) {
        fragmentB.receiveData(data);
    }
}

2. 通过ViewModel共享

Fragment A和Fragment B共享ViewModel:

// Fragment A
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());
MyViewModel viewModel = viewModelProvider.get(MyViewModel.class);
viewModel.setData("data");

// Fragment B
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());
MyViewModel viewModel = viewModelProvider.get(MyViewModel.class);
String data = viewModel.getData();

3. 通过事件总线

使用EventBus等:

// Fragment A
EventBus.getDefault().post(new MessageEvent("data"));

// Fragment B
@Subscribe
public void onMessageEvent(MessageEvent event) {
    // 处理事件
}

推荐:

  • 数据共享:ViewModel
  • 事件通信:EventBus
  • 简单通信:Activity中转

3.12 Fragment的setArguments()和getArguments()的作用是什么?

答案:

setArguments()和getArguments()用于传递参数给Fragment,是推荐的方式。

setArguments()方法:

  • 作用:设置Fragment的参数
  • 时机:在Fragment创建后、onCreate()之前
  • 特点:参数保存在Bundle中,Fragment重建时会保留
// 创建Fragment并设置参数
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString("key", "value");
fragment.setArguments(args);

getArguments()方法:

  • 作用:获取Fragment的参数
  • 时机:在onCreate()及之后可以获取
  • 特点:返回Bundle,可能为null
// Fragment中获取参数
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        String value = getArguments().getString("key");
    }
}

优势:

  • Fragment重建时参数会保留
  • 推荐的方式
  • 避免使用构造参数(系统可能重建Fragment)

注意事项:

  • 必须在Fragment创建后、添加到Activity之前调用setArguments()
  • getArguments()可能为null,需要判空

3.13 Fragment的onAttach()和onDetach()的作用是什么?

答案:

onAttach()和onDetach()用于关联和分离Fragment与Activity

onAttach()方法:

  • 调用时机:Fragment与Activity关联时
  • 作用:获取Activity引用,初始化与Activity的关联
  • 参数:Context(通常是Activity)
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentListener) {
        listener = (OnFragmentListener) context;
    }
}

onDetach()方法:

  • 调用时机:Fragment与Activity分离时
  • 作用:清理与Activity的关联,避免内存泄漏
  • 特点:Fragment生命周期最后调用
@Override
public void onDetach() {
    super.onDetach();
    listener = null; // 清理引用,避免内存泄漏
}

注意事项:

  • onAttach()中获取Activity引用
  • onDetach()中清理引用,避免内存泄漏
  • onDetach()后不能再访问Activity

3.14 Fragment的接口回调如何实现?

答案:

接口回调是Fragment与Activity通信的标准方式

实现步骤:

  1. 定义接口
public interface OnFragmentListener {
    void onFragmentAction(String data);
    void onFragmentResult(int result);
}
  1. Fragment中定义接口变量
public class MyFragment extends Fragment {
    private OnFragmentListener listener;
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentListener) {
            listener = (OnFragmentListener) context;
        } else {
            throw new RuntimeException("Activity must implement OnFragmentListener");
        }
    }
    
    private void doSomething() {
        if (listener != null) {
            listener.onFragmentAction("data");
        }
    }
    
    @Override
    public void onDetach() {
        super.onDetach();
        listener = null; // 清理引用
    }
}
  1. Activity实现接口
public class MainActivity extends AppCompatActivity implements OnFragmentListener {
    @Override
    public void onFragmentAction(String data) {
        // 处理Fragment的回调
    }
    
    @Override
    public void onFragmentResult(int result) {
        // 处理结果
    }
}

优势:

  • 类型安全
  • 解耦Fragment和Activity
  • 标准做法

3.15 Fragment的ViewModel如何共享?

答案:

Fragment可以通过ViewModelProvider共享ViewModel,作用域可以是Activity或Fragment。

共享方式:

  1. Activity作用域的ViewModel(Fragment间共享)
// Fragment A和Fragment B共享同一个ViewModel
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());
MyViewModel viewModel = viewModelProvider.get(MyViewModel.class);

// Fragment A设置数据
viewModel.setData("data");

// Fragment B获取数据
String data = viewModel.getData();
  1. Fragment作用域的ViewModel(Fragment独享)
// Fragment独享的ViewModel
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
MyViewModel viewModel = viewModelProvider.get(MyViewModel.class);
  1. 使用ViewModelFactory传递参数
ViewModelProvider.Factory factory = new ViewModelFactory("param");
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity(), factory);
MyViewModel viewModel = viewModelProvider.get(MyViewModel.class);

作用域说明:

  • getActivity():Activity作用域,Fragment间共享
  • this:Fragment作用域,Fragment独享

使用场景:

  • Fragment间共享数据:使用Activity作用域
  • Fragment独享数据:使用Fragment作用域

3.16 FragmentManager和FragmentTransaction的作用是什么?

答案:

FragmentManager和FragmentTransaction用于管理Fragment

FragmentManager:

  • 作用:管理Fragment的添加、移除、查找等
  • 获取方式:getSupportFragmentManager()(v4包)或getFragmentManager()
FragmentManager fragmentManager = getSupportFragmentManager();

主要方法:

  • findFragmentById():通过ID查找Fragment
  • findFragmentByTag():通过Tag查找Fragment
  • popBackStack():弹出回退栈

FragmentTransaction:

  • 作用:执行Fragment的添加、移除、替换等操作
  • 获取方式:fragmentManager.beginTransaction()
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.container, fragment);
transaction.commit();

主要方法:

  • add():添加Fragment
  • remove():移除Fragment
  • replace():替换Fragment
  • show()/hide():显示/隐藏Fragment
  • attach()/detach():关联/分离Fragment
  • commit():提交事务

注意事项:

  • 所有操作必须在commit()之前
  • commit()是异步的,commitNow()是同步的
  • 不能在onSaveInstanceState()之后调用commit()

3.17 Fragment的回退栈(BackStack)是什么?

答案:

回退栈用于管理Fragment的返回操作,类似Activity的任务栈。

回退栈的作用:

  • 记录Fragment的添加顺序
  • 支持返回键返回上一个Fragment
  • 管理Fragment的导航

使用方式:

FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment1);
transaction.addToBackStack(null); // 添加到回退栈
transaction.commit();

// 再次添加
transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment2);
transaction.addToBackStack(null);
transaction.commit();

// 按返回键会依次返回:fragment2 → fragment1

addToBackStack()方法:

  • 参数:名称(可以为null),用于标识
  • 作用:将当前事务添加到回退栈

回退栈管理:

// 弹出回退栈
fragmentManager.popBackStack();

// 弹出到指定名称
fragmentManager.popBackStack("name", FragmentManager.POP_BACK_STACK_INCLUSIVE);

// 清空回退栈
fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

注意事项:

  • 只有添加到回退栈的Fragment才能通过返回键返回
  • 返回时会触发Fragment的生命周期方法

3.18 FragmentTransaction的commit()和commitAllowingStateLoss()的区别是什么?

答案:

commit()和commitAllowingStateLoss()用于提交Fragment事务,但在异常处理上有区别。

commit()方法:

  • 行为:提交事务
  • 异常处理:如果Activity状态已保存,会抛出异常
  • 安全性:更安全,避免状态丢失
transaction.commit();
// 如果Activity状态已保存,可能抛出异常

commitAllowingStateLoss()方法:

  • 行为:提交事务,允许状态丢失
  • 异常处理:即使Activity状态已保存,也不会抛出异常
  • 安全性:可能丢失状态,不推荐使用
transaction.commitAllowingStateLoss();
// 即使Activity状态已保存,也不会抛出异常

使用场景:

  • commit():正常情况使用
  • commitAllowingStateLoss():特殊情况(如onSaveInstanceState()之后),但不推荐

注意事项:

  • 推荐使用commit()
  • commitAllowingStateLoss()可能导致状态丢失
  • 不能在onSaveInstanceState()之后调用commit()

3.19 Fragment的懒加载如何实现?

答案:

懒加载用于延迟加载Fragment的数据,提升性能。

实现方式:

  1. 使用setUserVisibleHint()(已废弃)
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser && isResumed()) {
        loadData();
    }
}
  1. 使用onHiddenChanged()(show/hide方式)
@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (!hidden && isResumed()) {
        loadData();
    }
}
  1. 使用FragmentPagerAdapter + ViewPager(推荐)
public class LazyFragment extends Fragment {
    private boolean isDataLoaded = false;
    private boolean isViewCreated = false;
    
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && !isDataLoaded && isViewCreated) {
            loadData();
            isDataLoaded = true;
        }
    }
    
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        isViewCreated = true;
        if (getUserVisibleHint() && !isDataLoaded) {
            loadData();
            isDataLoaded = true;
        }
    }
    
    private void loadData() {
        // 加载数据
    }
}

推荐:

  • ViewPager中使用setUserVisibleHint()
  • show/hide方式使用onHiddenChanged()

3.20 ViewPager中Fragment的生命周期特点是什么?

答案:

ViewPager中Fragment的生命周期有特殊行为,受ViewPager的预加载机制影响。

生命周期特点:

  1. 预加载机制

    • ViewPager会预加载相邻的Fragment
    • 默认预加载左右各1个Fragment
    • 可以通过setOffscreenPageLimit()设置
  2. 生命周期调用

    • 预加载的Fragment会调用onCreate()、onCreateView()等
    • 但不会调用onStart()、onResume()
    • 只有可见的Fragment才会调用onStart()、onResume()
  3. 生命周期顺序

    预加载:onAttach() → onCreate() → onCreateView() → onActivityCreated()
    可见时:onStart() → onResume()
    不可见:onPause() → onStop()
    

注意事项:

  • 预加载的Fragment已经创建,但不可见
  • 使用setUserVisibleHint()判断是否可见
  • 注意内存占用(预加载会占用内存)

3.21 Fragment的setRetainInstance()的作用是什么?

答案:

setRetainInstance()用于在配置变更时保留Fragment实例(已废弃,Android 3.0+推荐使用ViewModel)。

作用:

  • 配置变更(如横竖屏切换)时,Fragment实例不会被销毁重建
  • 保留Fragment的状态和数据

使用方式:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true); // 保留实例
}

注意事项:

  • 已废弃,不推荐使用
  • 推荐使用ViewModel保存数据
  • 只能用于没有UI的Fragment(setRetainInstance(true)的Fragment不能有View)

替代方案:

  • 使用ViewModel保存数据
  • 使用onSaveInstanceState()保存状态

3.22 Fragment的状态保存如何实现?

答案:

Fragment的状态保存通过**onSaveInstanceState()**实现。

保存状态:

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("key", "value");
    outState.putInt("count", count);
}

恢复状态:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        String value = savedInstanceState.getString("key");
        int count = savedInstanceState.getInt("count");
    }
}

调用时机:

  • 系统回收Fragment时
  • 配置变更时
  • Activity的onSaveInstanceState()中自动调用

注意事项:

  • 只保存临时数据
  • 持久数据使用其他方式(SharedPreferences、数据库等)
  • Bundle有大小限制

3.23 Fragment的onSaveInstanceState()什么时候调用?

答案:

onSaveInstanceState()在Fragment可能被系统回收时调用。

调用时机:

  • Activity的onSaveInstanceState()被调用时
  • 配置变更时(如横竖屏切换)
  • 系统需要回收Fragment时

调用顺序:

Activity.onSaveInstanceState()
  → Fragment.onSaveInstanceState()

注意事项:

  • 系统自动调用
  • 不能保证一定调用(用户主动退出不会调用)
  • 保存的数据在onCreate()的Bundle中恢复

3.24 Fragment的状态恢复如何实现?

答案:

Fragment的状态恢复在**onCreate()onActivityCreated()**中实现。

恢复方式:

  1. 在onCreate()中恢复
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        String value = savedInstanceState.getString("key");
    }
}
  1. 在onActivityCreated()中恢复
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (savedInstanceState != null) {
        String value = savedInstanceState.getString("key");
    }
}

推荐:

  • 在onCreate()中恢复数据
  • 在onActivityCreated()中恢复UI状态

3.25 Fragment的内存泄漏如何避免?

答案:

Fragment内存泄漏的常见原因和避免方法。

常见泄漏场景:

  1. 静态引用
// 错误
static Fragment fragment;

// 正确:使用WeakReference
static WeakReference<Fragment> fragmentRef;
  1. 异步任务持有引用
// 错误:AsyncTask持有Fragment引用
new AsyncTask() {
    // 持有Fragment引用
}.execute();

// 正确:在onDestroy()中取消
@Override
public void onDestroy() {
    super.onDestroy();
    asyncTask.cancel(true);
}
  1. 监听器未注销
// 正确:在onDestroy()中注销
@Override
public void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}
  1. Handler泄漏
// 正确:使用静态内部类+WeakReference
private static class MyHandler extends Handler {
    private WeakReference<Fragment> fragmentRef;
}

避免方法:

  • 避免静态引用Fragment
  • 及时取消异步任务
  • 及时注销监听器
  • 使用WeakReference

3.26 Fragment的异常退出如何处理?

答案:

Fragment异常退出需要通过异常捕获状态恢复处理。

处理方式:

  1. try-catch保护
try {
    // 可能崩溃的代码
} catch (Exception e) {
    e.printStackTrace();
}
  1. 状态恢复
  • 使用onSaveInstanceState()保存状态
  • 异常退出后可以恢复
  1. 全局异常处理
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 处理异常
    }
});

最佳实践:

  • 关键操作使用try-catch
  • 记录异常日志
  • 使用崩溃监控工具

3.27 Fragment的嵌套如何处理?

答案:

Fragment嵌套(Fragment中包含Fragment)需要使用getChildFragmentManager()

嵌套Fragment:

public class ParentFragment extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        
        // 使用getChildFragmentManager()而不是getFragmentManager()
        FragmentManager childFragmentManager = getChildFragmentManager();
        FragmentTransaction transaction = childFragmentManager.beginTransaction();
        transaction.add(R.id.child_container, new ChildFragment());
        transaction.commit();
    }
}

关键点:

  • 使用**getChildFragmentManager()**管理子Fragment
  • 子Fragment的生命周期受父Fragment影响
  • 子Fragment的回退栈独立管理

注意事项:

  • 嵌套层级不宜过深
  • 注意性能影响
  • 正确管理FragmentManager

3.28 Fragment的最佳实践有哪些?

答案:

Fragment最佳实践包括生命周期管理通信方式状态管理等。

最佳实践:

  1. 生命周期管理

    • 在正确的生命周期方法中执行操作
    • 及时释放资源
    • 避免内存泄漏
  2. 通信方式

    • 使用接口回调与Activity通信
    • 使用ViewModel共享数据
    • 避免直接访问Activity
  3. 状态管理

    • 使用onSaveInstanceState()保存状态
    • 使用ViewModel保存数据
    • 合理使用setArguments()
  4. Fragment管理

    • 使用动态创建,灵活管理
    • 合理使用回退栈
    • 注意Fragment的生命周期
  5. 性能优化

    • 实现懒加载
    • 避免过度嵌套
    • 优化布局

总结:

  • 遵循生命周期
  • 注意内存管理
  • 合理通信
  • 优化性能

3.29 Fragment和Activity的选择标准是什么?

答案:

根据使用场景需求选择Fragment或Activity。

使用Activity的场景:

  • 独立的屏幕
  • 需要独立的生命周期
  • 系统级功能(如设置)

使用Fragment的场景:

  • 可复用的UI组件
  • 需要在多个Activity中使用
  • 灵活的UI组合
  • 适配不同屏幕(手机/平板)

选择标准:

  • 复用性:需要复用 → Fragment
  • 独立性:完全独立 → Activity
  • 灵活性:需要灵活组合 → Fragment
  • 简单性:简单场景 → Activity

推荐:

  • 大多数情况使用Fragment
  • 系统级功能使用Activity
  • 根据实际需求选择

3.30 Fragment的测试如何进行?

答案:

Fragment测试可以使用AndroidX Test框架。

测试方式:

  1. 单元测试
@Test
public void testFragmentCreation() {
    MyFragment fragment = new MyFragment();
    assertNotNull(fragment);
}
  1. UI测试(使用FragmentScenario)
@RunWith(AndroidJUnit4.class)
public class MyFragmentTest {
    @Test
    public void testFragment() {
        FragmentScenario<MyFragment> scenario = 
            FragmentScenario.launchInContainer(MyFragment.class);
        
        scenario.onFragment(fragment -> {
            // 测试Fragment
        });
    }
}
  1. 集成测试
  • 测试Fragment与Activity的交互
  • 测试Fragment的生命周期
  • 测试Fragment的状态保存和恢复

测试内容:

  • Fragment的创建和销毁
  • 生命周期方法
  • 状态保存和恢复
  • 与Activity的通信

第四章:Service(25 题)

4.1 Service是什么?它的生命周期是什么?

答案:

Service是Android的后台服务组件,用于执行长时间运行的任务,没有用户界面。

Service的定义:

  • 后台运行,无用户界面
  • 可以执行长时间运行的任务
  • 可以跨进程运行

生命周期方法:

启动模式(startService):

  1. onCreate():Service创建时调用
  2. onStartCommand():每次启动时调用
  3. onDestroy():Service销毁时调用

绑定模式(bindService):

  1. onCreate():Service创建时调用
  2. onBind():绑定Service时调用
  3. onUnbind():解绑Service时调用
  4. onDestroy():Service销毁时调用

生命周期流程图:

启动模式:onCreate() → onStartCommand() → onDestroy()
绑定模式:onCreate() → onBind() → onUnbind() → onDestroy()

4.2 Service和Activity的区别是什么?

答案:

Service和Activity在用途生命周期用户交互上有重要区别。

主要区别:

特性ActivityService
用户界面
用途用户交互后台任务
生命周期受用户操作影响受启动方式影响
可见性可见不可见
启动方式startActivity()startService()/bindService()

Activity特点:

  • 有用户界面
  • 用户可见
  • 用于用户交互
  • 生命周期受用户操作影响

Service特点:

  • 无用户界面
  • 后台运行
  • 用于执行长时间任务
  • 生命周期受启动方式影响

使用场景:

  • Activity:需要用户界面的场景
  • Service:后台任务、音乐播放、文件下载等

4.3 Service的启动方式有哪些?

答案:

Service有两种启动方式:startService()bindService()

1. startService()(启动模式)

Intent intent = new Intent(this, MyService.class);
startService(intent);
  • 特点:Service独立运行,不依赖调用者
  • 生命周期:onCreate() → onStartCommand() → onDestroy()
  • 使用场景:后台任务、音乐播放等

2. bindService()(绑定模式)

Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
  • 特点:Service与调用者绑定,可以通信
  • 生命周期:onCreate() → onBind() → onUnbind() → onDestroy()
  • 使用场景:需要与Service通信的场景

混合模式:

  • 可以同时使用startService()和bindService()
  • Service只有在既没有启动也没有绑定时才会销毁

4.4 startService()和bindService()的区别是什么?

答案:

startService()和bindService()在运行方式生命周期通信方式上有区别。

主要区别:

特性startService()bindService()
运行方式独立运行与调用者绑定
生命周期调用者销毁后仍运行调用者销毁后可能销毁
通信方式不能直接通信可以通过Binder通信
使用场景后台任务需要通信

startService()特点:

  • Service独立运行
  • 调用者销毁后Service仍运行
  • 需要调用stopService()或stopSelf()停止
  • 不能直接通信

bindService()特点:

  • Service与调用者绑定
  • 调用者销毁后Service可能销毁(如果没有startService)
  • 可以通过Binder通信
  • 调用unbindService()解绑

选择建议:

  • 需要后台运行:使用startService()
  • 需要通信:使用bindService()
  • 两者结合:同时使用

4.5 Service的生命周期方法有哪些?

答案:

Service的生命周期方法根据启动方式不同而不同。

启动模式(startService)的生命周期:

  1. onCreate()

    • Service创建时调用
    • 只调用一次
    • 初始化工作
  2. onStartCommand(Intent, int, int)

    • 每次startService()时调用
    • 可以多次调用
    • 处理启动请求
  3. onDestroy()

    • Service销毁时调用
    • 清理资源

绑定模式(bindService)的生命周期:

  1. onCreate()

    • Service创建时调用
    • 只调用一次
  2. onBind(Intent)

    • 绑定Service时调用
    • 返回IBinder用于通信
  3. onUnbind(Intent)

    • 解绑Service时调用
    • 返回true表示可以重新绑定
  4. onDestroy()

    • Service销毁时调用

混合模式:

  • 如果同时使用startService()和bindService()
  • 需要同时调用stopService()和unbindService()才会销毁

4.6 Service的生命周期和启动方式的关系是什么?

答案:

Service的生命周期完全由启动方式决定

关系说明:

  1. startService()方式

    • onCreate() → onStartCommand() → onDestroy()
    • Service独立运行
    • 调用者销毁不影响Service
  2. bindService()方式

    • onCreate() → onBind() → onUnbind() → onDestroy()
    • Service与调用者绑定
    • 所有调用者解绑后可能销毁
  3. 混合方式

    • 同时使用startService()和bindService()
    • 需要同时停止和解绑才会销毁
    • 生命周期方法都会调用

关键点:

  • 启动方式决定生命周期
  • 可以同时使用两种方式
  • Service只有在既没有启动也没有绑定时才会销毁

4.7 前台Service和后台Service的区别是什么?

答案:

前台Service和后台Service在优先级通知显示上有区别。

后台Service:

  • 特点:普通后台服务
  • 优先级:较低,系统可能回收
  • 通知:不需要显示通知
  • 使用场景:一般后台任务

前台Service:

  • 特点:高优先级服务
  • 优先级:较高,不易被系统回收
  • 通知:必须显示通知
  • 使用场景:音乐播放、文件下载等

创建前台Service:

@Override
public void onCreate() {
    super.onCreate();
    
    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setContentTitle("前台服务")
        .setContentText("服务正在运行")
        .setSmallIcon(R.drawable.icon)
        .build();
    
    startForeground(1, notification);
}

区别总结:

  • 后台Service:低优先级,不需要通知
  • 前台Service:高优先级,必须显示通知

4.8 如何创建前台Service?

答案:

创建前台Service需要显示通知并调用startForeground()

创建步骤:

  1. 创建通知渠道(Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    NotificationChannel channel = new NotificationChannel(
        CHANNEL_ID, "服务渠道", NotificationManager.IMPORTANCE_LOW);
    NotificationManager manager = getSystemService(NotificationManager.class);
    manager.createNotificationChannel(channel);
}
  1. 创建通知
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("前台服务")
    .setContentText("服务正在运行")
    .setSmallIcon(R.drawable.icon)
    .build();
  1. 启动前台服务
@Override
public void onCreate() {
    super.onCreate();
    startForeground(1, notification);
}
  1. 在AndroidManifest.xml中声明权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

注意事项:

  • Android 8.0+需要创建通知渠道
  • 必须在onCreate()或onStartCommand()中调用startForeground()
  • 必须在5秒内调用,否则会ANR

4.9 前台Service的通知如何管理?

答案:

前台Service的通知需要持续显示,可以更新停止

通知管理:

  1. 更新通知
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("更新标题")
    .setContentText("更新内容")
    .setProgress(100, progress, false)
    .build();
    
NotificationManager manager = getSystemService(NotificationManager.class);
manager.notify(1, notification);
  1. 停止前台服务
stopForeground(true); // true表示移除通知
// 或
stopForeground(false); // false表示保留通知
  1. 取消通知
NotificationManager manager = getSystemService(NotificationManager.class);
manager.cancel(1);

注意事项:

  • 通知必须持续显示
  • 可以更新通知内容
  • 停止前台服务时可以移除或保留通知

4.10 IntentService的特点是什么?

答案:

IntentService是自带工作线程的Service,自动处理异步任务。

特点:

  1. 自动创建工作线程

    • 在后台线程执行任务
    • 不阻塞主线程
  2. 串行执行

    • 任务按顺序执行
    • 一次只执行一个任务
  3. 自动停止

    • 任务执行完成后自动停止
    • 不需要手动调用stopSelf()
  4. 简单易用

    • 只需实现onHandleIntent()方法
    • 自动处理线程和停止

使用示例:

public class MyIntentService extends IntentService {
    public MyIntentService() {
        super("MyIntentService");
    }
    
    @Override
    protected void onHandleIntent(Intent intent) {
        // 在后台线程执行任务
        String data = intent.getStringExtra("data");
        // 处理任务
    }
}

注意事项:

  • 已废弃(Android 8.0+),推荐使用JobIntentService或WorkManager
  • 适合简单的后台任务
  • 任务串行执行,不适合并发

4.11 IntentService的实现原理是什么?

答案:

IntentService通过HandlerThreadHandler实现后台线程执行任务。

实现原理:

  1. 创建HandlerThread
private HandlerThread handlerThread;
private Handler handler;

@Override
public void onCreate() {
    super.onCreate();
    handlerThread = new HandlerThread("IntentService");
    handlerThread.start();
    handler = new Handler(handlerThread.getLooper());
}
  1. 在onStartCommand()中发送消息
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    handler.post(() -> {
        onHandleIntent(intent);
        stopSelf(startId);
    });
    return START_NOT_STICKY;
}
  1. 在onHandleIntent()中处理任务
protected abstract void onHandleIntent(Intent intent);

关键点:

  • 使用HandlerThread创建工作线程
  • 使用Handler发送任务到工作线程
  • 任务执行完成后自动停止

优势:

  • 自动管理线程
  • 串行执行,线程安全
  • 自动停止,无需手动管理

4.12 JobService的作用是什么?

答案:

JobService是系统调度的后台服务,用于执行定时任务和条件任务。

作用:

  • 在满足条件时执行任务(如网络可用、充电时等)
  • 系统统一调度,节省电量
  • 替代后台Service(Android 8.0+限制后台服务)

使用方式:

  1. 继承JobService
public class MyJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        // 在后台线程执行任务
        new Thread(() -> {
            // 执行任务
            jobFinished(params, false);
        }).start();
        return true; // 返回true表示任务在后台线程执行
    }
    
    @Override
    public boolean onStopJob(JobParameters params) {
        // 任务被停止时调用
        return false; // 返回false表示任务不需要重新调度
    }
}
  1. 调度任务
JobScheduler jobScheduler = getSystemService(JobScheduler.class);
JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(this, MyJobService.class))
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
    .setRequiresCharging(true)
    .build();
jobScheduler.schedule(jobInfo);

注意事项:

  • Android 5.0+支持
  • 系统统一调度,执行时间不确定
  • 适合不紧急的后台任务

4.13 WorkManager和Service的区别是什么?

答案:

WorkManager是新的后台任务调度框架,相比Service有诸多优势。

主要区别:

特性ServiceWorkManager
调度方式立即执行系统调度
执行条件支持条件(网络、充电等)
电量优化较差较好
适用场景立即执行的任务可延迟的任务
API级别所有版本Android 4.0+

Service特点:

  • 立即执行
  • 适合需要立即执行的任务
  • 电量消耗较大

WorkManager特点:

  • 系统统一调度
  • 支持执行条件
  • 电量优化好
  • 适合可延迟的任务

使用建议:

  • 立即执行的任务:使用Service
  • 可延迟的任务:使用WorkManager
  • 定时任务:使用WorkManager

4.14 Service和Activity如何通信?

答案:

Service和Activity可以通过BinderBroadcastReceiverMessenger等方式通信。

1. 通过Binder通信(绑定模式)

// Service中
public class MyService extends Service {
    private MyBinder binder = new MyBinder();
    
    public class MyBinder extends Binder {
        MyService getService() {
            return MyService.this;
        }
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    
    public void doSomething() {
        // Service的方法
    }
}

// Activity中
private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        MyService.MyBinder binder = (MyService.MyBinder) service;
        MyService myService = binder.getService();
        myService.doSomething();
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};

bindService(intent, connection, Context.BIND_AUTO_CREATE);

2. 通过BroadcastReceiver通信

// Service中发送广播
Intent broadcast = new Intent("ACTION");
broadcast.putExtra("data", "value");
sendBroadcast(broadcast);

// Activity中接收广播
BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String data = intent.getStringExtra("data");
    }
};
registerReceiver(receiver, new IntentFilter("ACTION"));

3. 通过Messenger通信

// Service中
Messenger messenger = new Messenger(new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // 处理消息
    }
});

@Override
public IBinder onBind(Intent intent) {
    return messenger.getBinder();
}

// Activity中
Messenger serviceMessenger = new Messenger(service);
Message msg = Message.obtain();
serviceMessenger.send(msg);

推荐:

  • 简单通信:Binder
  • 跨进程通信:AIDL或Messenger
  • 事件通知:BroadcastReceiver

4.15 Binder机制是什么?

答案:

Binder是Android的进程间通信(IPC)机制,用于跨进程通信。

Binder的作用:

  • 进程间通信
  • 跨应用通信
  • 系统服务通信

Binder的特点:

  • 高性能(比Socket快)
  • 安全性好(基于Linux内核)
  • Android特有机制

Binder的工作原理:

  1. Client进程通过Binder驱动发送请求
  2. Binder驱动将请求转发到Server进程
  3. Server进程处理请求并返回结果
  4. Binder驱动将结果返回给Client进程

使用方式:

  • 本地通信:直接使用Binder
  • 跨进程通信:使用AIDL生成Binder接口

优势:

  • 性能好
  • 安全性高
  • 系统统一管理

4.16 Binder的通信原理是什么?

答案:

Binder通信基于Linux内核的Binder驱动,实现进程间通信。

通信流程:

  1. Client进程

    • 调用Service的方法
    • 通过Binder驱动发送请求
  2. Binder驱动

    • 接收Client的请求
    • 转发到Server进程
    • 管理进程间通信
  3. Server进程

    • 接收Binder驱动的请求
    • 处理请求
    • 返回结果
  4. 返回结果

    • Server通过Binder驱动返回结果
    • Binder驱动转发给Client
    • Client接收结果

关键组件:

  • Binder驱动:Linux内核模块,管理IPC
  • ServiceManager:系统服务管理器
  • Binder对象:进程间通信的代理

优势:

  • 一次拷贝(比Socket的两次拷贝快)
  • 安全性好(基于Linux内核)
  • 系统统一管理

4.17 AIDL的作用是什么?如何使用?

答案:

AIDL(Android Interface Definition Language)用于定义跨进程通信接口

AIDL的作用:

  • 定义跨进程通信接口
  • 自动生成Binder代码
  • 简化跨进程通信

使用步骤:

  1. 定义AIDL接口
// IMyService.aidl
interface IMyService {
    void doSomething(String data);
    String getResult();
}
  1. 实现Service
public class MyService extends Service {
    private IMyService.Stub binder = new IMyService.Stub() {
        @Override
        public void doSomething(String data) {
            // 实现方法
        }
        
        @Override
        public String getResult() {
            return "result";
        }
    };
    
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}
  1. 在Activity中绑定Service
private IMyService myService;

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        myService = IMyService.Stub.asInterface(service);
        myService.doSomething("data");
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
        myService = null;
    }
};

bindService(intent, connection, Context.BIND_AUTO_CREATE);

注意事项:

  • AIDL文件需要放在aidl目录下
  • 支持的数据类型有限(基本类型、String、Parcelable等)
  • 跨进程调用是异步的

4.18 AIDL的接口如何定义?

答案:

AIDL接口定义需要遵循特定语法

定义规则:

  1. 基本语法
// IMyService.aidl
package com.example;

interface IMyService {
    void method1();
    int method2(String param);
}
  1. 支持的数据类型
  • 基本类型:int、long、char、boolean等
  • String、CharSequence
  • List(元素必须是支持的类型)
  • Map(key和value必须是支持的类型)
  • Parcelable对象
  1. 定义Parcelable对象
// User.aidl
package com.example;
parcelable User;
// User.java实现Parcelable
public class User implements Parcelable {
    // Parcelable实现
}
  1. 方向标识(in/out/inout)
interface IMyService {
    void method(in String input, out String output, inout String inout);
}

注意事项:

  • 包名必须与Java包名一致
  • 支持的类型有限
  • 跨进程调用是异步的

4.19 Messenger的使用方法是什么?

答案:

Messenger是基于Message的跨进程通信方式,比AIDL更简单。

使用方法:

  1. Service中创建Messenger
public class MyService extends Service {
    private Messenger messenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 处理消息
            String data = (String) msg.obj;
            
            // 回复消息
            Message reply = Message.obtain();
            reply.obj = "result";
            try {
                msg.replyTo.send(reply);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    });
    
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}
  1. Activity中绑定Service
private Messenger serviceMessenger;

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        serviceMessenger = new Messenger(service);
        
        // 发送消息
        Message msg = Message.obtain();
        msg.obj = "data";
        msg.replyTo = clientMessenger; // 设置回复Messenger
        try {
            serviceMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
        serviceMessenger = null;
    }
};

// 创建客户端Messenger接收回复
private Messenger clientMessenger = new Messenger(new Handler() {
    @Override
    public void handleMessage(Message msg) {
        String result = (String) msg.obj;
    }
});

特点:

  • 基于Message通信
  • 比AIDL简单
  • 适合简单的跨进程通信

4.20 Messenger和AIDL的区别是什么?

答案:

Messenger和AIDL都可以用于跨进程通信,但使用方式和适用场景不同

主要区别:

特性MessengerAIDL
通信方式基于Message基于接口方法
使用复杂度简单较复杂
适用场景简单通信复杂通信
性能稍慢稍快
双向通信需要两个Messenger支持

Messenger特点:

  • 基于Message通信
  • 使用简单
  • 适合简单的跨进程通信
  • 双向通信需要两个Messenger

AIDL特点:

  • 基于接口方法调用
  • 功能强大
  • 适合复杂的跨进程通信
  • 支持双向通信

选择建议:

  • 简单通信:使用Messenger
  • 复杂通信:使用AIDL
  • 性能要求高:使用AIDL

4.21 Service的最佳实践有哪些?

答案:

Service最佳实践包括生命周期管理内存管理性能优化等。

最佳实践:

  1. 选择合适的启动方式

    • 需要立即执行:使用startService()
    • 需要通信:使用bindService()
    • 可延迟执行:使用WorkManager
  2. 及时释放资源

    • 在onDestroy()中清理资源
    • 取消异步任务
    • 注销监听器
  3. 避免内存泄漏

    • 避免静态引用Context
    • 及时取消异步任务
    • 使用WeakReference
  4. 使用前台Service

    • 重要任务使用前台Service
    • 必须显示通知
    • 提升优先级
  5. 合理使用WorkManager

    • 可延迟的任务使用WorkManager
    • 利用系统调度优化电量

总结:

  • 选择合适的启动方式
  • 注意内存管理
  • 及时释放资源
  • 合理使用前台Service

4.22 Service的内存泄漏如何避免?

答案:

Service内存泄漏的常见原因和避免方法。

常见泄漏场景:

  1. 静态引用Context
// 错误
static Context context;

// 正确:使用Application Context
static Context context = getApplicationContext();
  1. 异步任务持有引用
// 错误:AsyncTask持有Service引用
new AsyncTask() {
    // 持有Service引用
}.execute();

// 正确:在onDestroy()中取消
@Override
public void onDestroy() {
    super.onDestroy();
    asyncTask.cancel(true);
}
  1. 监听器未注销
// 正确:在onDestroy()中注销
@Override
public void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}
  1. Handler泄漏
// 正确:使用静态内部类+WeakReference
private static class MyHandler extends Handler {
    private WeakReference<Service> serviceRef;
}

避免方法:

  • 避免静态引用Context
  • 及时取消异步任务
  • 及时注销监听器
  • 使用WeakReference

4.23 Service的保活机制有哪些?

答案:

Service保活机制用于防止Service被系统回收,但需要注意合规性。

保活机制:

  1. 前台Service

    • 提升优先级
    • 不易被系统回收
    • 必须显示通知
  2. START_STICKY

    • Service被系统杀死后自动重启
    • 在onStartCommand()中返回START_STICKY
  3. 双进程守护

    • 两个进程互相守护
    • 一个进程被杀死,另一个进程重启它
    • 不推荐,可能被系统限制
  4. JobScheduler/WorkManager

    • 系统统一调度
    • 在满足条件时执行
    • 推荐方式

注意事项:

  • Android 8.0+限制后台服务
  • 过度保活可能影响用户体验
  • 推荐使用WorkManager等系统推荐方式

合规建议:

  • 使用前台Service
  • 使用WorkManager
  • 避免过度保活

4.24 Service的异常退出如何处理?

答案:

Service异常退出需要通过异常捕获重启机制处理。

处理方式:

  1. try-catch保护
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    try {
        // 可能崩溃的代码
    } catch (Exception e) {
        e.printStackTrace();
        // 记录异常
    }
    return START_STICKY; // 异常后重启
}
  1. START_STICKY重启
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 处理任务
    return START_STICKY; // Service被杀死后自动重启
}
  1. 全局异常处理
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 处理异常
        // 重启Service
    }
});

最佳实践:

  • 关键操作使用try-catch
  • 记录异常日志
  • 使用START_STICKY自动重启
  • 使用崩溃监控工具

4.25 Service的测试如何进行?

答案:

Service测试可以使用AndroidX Test框架。

测试方式:

  1. 单元测试
@Test
public void testServiceCreation() {
    MyService service = new MyService();
    assertNotNull(service);
}
  1. 集成测试(使用ServiceTestRule)
@RunWith(AndroidJUnit4.class)
public class MyServiceTest {
    @Rule
    public final ServiceTestRule serviceRule = new ServiceTestRule();
    
    @Test
    public void testService() throws TimeoutException {
        Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class);
        IBinder binder = serviceRule.bindService(intent);
        MyService.MyBinder myBinder = (MyService.MyBinder) binder;
        MyService service = myBinder.getService();
        assertNotNull(service);
    }
}
  1. 功能测试
  • 测试Service的启动和停止
  • 测试Service的生命周期
  • 测试Service与Activity的通信

测试内容:

  • Service的创建和销毁
  • 生命周期方法
  • 与Activity的通信
  • 异常处理

第五章:BroadcastReceiver(20 题)

5.1 BroadcastReceiver是什么?它的作用是什么?

答案:

BroadcastReceiver是Android的广播接收器组件,用于接收系统或应用发送的广播消息。

BroadcastReceiver的定义:

  • 用于接收广播消息
  • 无用户界面
  • 生命周期短暂

主要作用:

  1. 接收系统广播

    • 系统事件(开机、网络变化、电量变化等)
    • 系统状态变化通知
  2. 接收应用广播

    • 应用间通信
    • 组件间通信
  3. 响应事件

    • 根据广播执行相应操作
    • 更新UI、启动Service等

使用场景:

  • 监听系统事件
  • 应用间通信
  • 组件间解耦通信

5.2 BroadcastReceiver的注册方式有哪些?

答案:

BroadcastReceiver有两种注册方式:静态注册动态注册

1. 静态注册(AndroidManifest.xml)

在AndroidManifest.xml中声明:

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

特点:

  • 应用安装时注册
  • 应用未运行也能接收广播
  • 适合系统广播

2. 动态注册(代码)

在代码中注册:

BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理广播
    }
};

IntentFilter filter = new IntentFilter();
filter.addAction("ACTION");
registerReceiver(receiver, filter);

特点:

  • 运行时注册
  • 应用运行才能接收
  • 需要手动注销

注销:

unregisterReceiver(receiver);

5.3 静态注册和动态注册的区别是什么?

答案:

静态注册和动态注册在注册时机生命周期使用场景上有区别。

主要区别:

特性静态注册动态注册
注册时机应用安装时运行时
生命周期应用生命周期注册到注销
接收时机应用未运行也能接收应用运行才能接收
注销系统管理手动注销
使用场景系统广播应用广播

静态注册:

  • 在AndroidManifest.xml中声明
  • 应用安装时注册
  • 应用未运行也能接收
  • 适合系统广播(如开机广播)

动态注册:

  • 在代码中注册
  • 运行时注册
  • 应用运行才能接收
  • 需要手动注销,避免内存泄漏
  • 适合应用广播

注意事项:

  • 动态注册必须在合适的时机注销
  • Android 8.0+限制静态注册(部分系统广播除外)

5.4 有序广播和普通广播的区别是什么?

答案:

有序广播和普通广播在接收顺序传播机制上有区别。

普通广播(sendBroadcast):

  • 特点:所有接收者同时接收,无序
  • 传播:不能中断,所有接收者都会收到
  • 使用场景:不需要控制接收顺序的场景
Intent intent = new Intent("ACTION");
sendBroadcast(intent);

有序广播(sendOrderedBroadcast):

  • 特点:按优先级顺序接收
  • 传播:可以中断(abortBroadcast()),后续接收者收不到
  • 使用场景:需要控制接收顺序的场景
Intent intent = new Intent("ACTION");
sendOrderedBroadcast(intent, null);

优先级设置:

<intent-filter android:priority="1000">
    <action android:name="ACTION" />
</intent-filter>

区别总结:

  • 普通广播:无序,不能中断
  • 有序广播:有序,可以中断

5.5 粘性广播(Sticky Broadcast)是什么?

答案:

粘性广播是会保留的广播,即使没有接收者,广播也会保留,后续注册的接收者可以收到。

特点:

  • 广播会保留在系统中
  • 后续注册的接收者可以收到之前的广播
  • 需要权限:BROADCAST_STICKY

使用方式:

Intent intent = new Intent("ACTION");
sendStickyBroadcast(intent);

接收粘性广播:

Intent intent = registerReceiver(receiver, filter);
if (intent != null) {
    // 处理粘性广播
}

注意事项:

  • 已废弃(Android 5.0+),不推荐使用
  • 推荐使用其他方式(如SharedPreferences、数据库等)保存状态

5.6 广播的优先级如何设置?

答案:

广播优先级通过IntentFilter的priority属性设置,数值越大优先级越高。

设置方式:

  1. 静态注册中设置
<receiver android:name=".MyReceiver">
    <intent-filter android:priority="1000">
        <action android:name="ACTION" />
    </intent-filter>
</receiver>
  1. 动态注册中设置
IntentFilter filter = new IntentFilter("ACTION");
filter.setPriority(1000);
registerReceiver(receiver, filter);

优先级说明:

  • 数值范围:-1000 到 1000
  • 数值越大,优先级越高
  • 相同优先级按注册顺序接收

注意事项:

  • 只对有序广播有效
  • 系统广播的优先级通常较高
  • 不建议设置过高的优先级

5.7 系统广播有哪些?

答案:

系统广播是Android系统发送的广播,用于通知系统事件。

常见系统广播:

  1. 开机广播

    • android.intent.action.BOOT_COMPLETED
    • 系统启动完成
  2. 网络变化广播

    • android.net.conn.CONNECTIVITY_CHANGE
    • 网络连接状态变化
  3. 电量变化广播

    • android.intent.action.BATTERY_CHANGED
    • 电池电量变化
  4. 时间变化广播

    • android.intent.action.TIME_TICK
    • 每分钟触发一次
  5. 屏幕开关广播

    • android.intent.action.SCREEN_ON
    • android.intent.action.SCREEN_OFF
    • 屏幕开关
  6. 应用安装/卸载广播

    • android.intent.action.PACKAGE_ADDED
    • android.intent.action.PACKAGE_REMOVED

注意事项:

  • Android 8.0+限制部分系统广播的静态注册
  • 需要使用动态注册或替代方案

5.8 自定义广播如何实现?

答案:

自定义广播用于应用内或应用间通信

实现步骤:

  1. 定义广播接收者
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String data = intent.getStringExtra("data");
        // 处理广播
    }
}
  1. 注册广播接收者
// 静态注册(AndroidManifest.xml)
<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="com.example.ACTION" />
    </intent-filter>
</receiver>

// 或动态注册
BroadcastReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter("com.example.ACTION");
registerReceiver(receiver, filter);
  1. 发送广播
Intent intent = new Intent("com.example.ACTION");
intent.putExtra("data", "value");
sendBroadcast(intent);

注意事项:

  • 使用包名作为Action前缀,避免冲突
  • 动态注册需要及时注销
  • 跨应用广播需要设置权限

5.9 本地广播(LocalBroadcastManager)的作用是什么?

答案:

LocalBroadcastManager用于应用内广播,比全局广播更安全、高效。

作用:

  • 只在应用内传播,不跨进程
  • 更安全(其他应用无法接收)
  • 更高效(不需要跨进程通信)

使用方式:

// 注册接收者
LocalBroadcastManager.getInstance(this).registerReceiver(
    receiver,
    new IntentFilter("ACTION")
);

// 发送广播
Intent intent = new Intent("ACTION");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

// 注销接收者
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);

优势:

  • 安全性好(应用内)
  • 性能好(不跨进程)
  • 简单易用

注意事项:

  • 已废弃(AndroidX),推荐使用其他方式(如LiveData、EventBus等)
  • 只适用于应用内通信

5.10 本地广播和全局广播的区别是什么?

答案:

本地广播和全局广播在传播范围安全性性能上有区别。

主要区别:

特性本地广播全局广播
传播范围应用内系统范围
安全性
性能
跨进程不支持支持
使用场景应用内通信跨应用通信

本地广播:

  • 只在应用内传播
  • 更安全
  • 更高效
  • 适合应用内通信

全局广播:

  • 系统范围传播
  • 可以跨应用
  • 需要权限控制
  • 适合跨应用通信

推荐:

  • 应用内通信:使用本地广播或其他方式(LiveData、EventBus)
  • 跨应用通信:使用全局广播(需要权限)

5.11 有序广播的传播机制是什么?

答案:

有序广播按优先级顺序传播,可以中断传播。

传播机制:

  1. 按优先级排序

    • 系统根据优先级对接收者排序
    • 优先级高的先接收
  2. 顺序传播

    • 按顺序依次发送给接收者
    • 前一个接收者处理完后,才发送给下一个
  3. 可以中断

    • 接收者可以调用abortBroadcast()中断
    • 后续接收者收不到广播
  4. 可以修改结果

    • 接收者可以修改广播结果
    • 通过setResultData()等方法

示例:

@Override
public void onReceive(Context context, Intent intent) {
    // 处理广播
    if (shouldAbort) {
        abortBroadcast(); // 中断传播
    }
    setResultData("result"); // 设置结果
}

使用场景:

  • 需要控制接收顺序
  • 需要中断传播
  • 需要传递结果

5.12 BroadcastReceiver的最佳实践有哪些?

答案:

BroadcastReceiver最佳实践包括注册管理性能优化安全性等。

最佳实践:

  1. 及时注销动态注册

    • 在onDestroy()中注销
    • 避免内存泄漏
  2. 避免耗时操作

    • onReceive()在主线程执行
    • 耗时操作使用Service或异步任务
  3. 使用本地广播

    • 应用内通信使用本地广播
    • 更安全、高效
  4. 权限控制

    • 跨应用广播设置权限
    • 保护广播安全
  5. 适配Android 8.0+

    • 使用动态注册或替代方案
    • 避免使用已废弃的API

总结:

  • 及时注销
  • 避免耗时操作
  • 注意安全性
  • 适配新版本

5.13 BroadcastReceiver的内存泄漏如何避免?

答案:

BroadcastReceiver内存泄漏的常见原因和避免方法。

常见泄漏场景:

  1. 动态注册未注销
// 错误:注册后未注销
registerReceiver(receiver, filter);

// 正确:在onDestroy()中注销
@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}
  1. 内部类持有外部类引用
// 错误:内部类持有Activity引用
private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 持有Activity引用
    }
};

// 正确:使用静态内部类+WeakReference
private static class MyReceiver extends BroadcastReceiver {
    private WeakReference<Activity> activityRef;
}
  1. Context泄漏
// 错误:持有Activity Context
Context context = getActivity();

// 正确:使用Application Context
Context context = getApplicationContext();

避免方法:

  • 及时注销动态注册
  • 使用静态内部类
  • 使用Application Context

5.14 Android 8.0对广播的限制是什么?

答案:

Android 8.0(API 26)对静态注册的隐式广播进行了限制。

限制内容:

  • 大部分隐式广播不能静态注册
  • 只能动态注册或使用替代方案
  • 例外:部分系统广播仍可静态注册

受影响的广播:

  • 自定义广播
  • 大部分系统广播(如网络变化广播)

仍可静态注册的广播:

  • 开机广播(BOOT_COMPLETED)
  • 应用安装/卸载广播
  • 部分系统关键广播

原因:

  • 提升系统性能
  • 减少后台应用唤醒
  • 优化电量消耗

解决方案:

  • 使用动态注册
  • 使用JobScheduler/WorkManager
  • 使用其他通信方式

5.15 如何适配Android 8.0的广播限制?

答案:

适配Android 8.0的广播限制需要使用动态注册替代方案

适配方案:

  1. 使用动态注册
// 在Activity或Service中动态注册
BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理广播
    }
};

IntentFilter filter = new IntentFilter();
filter.addAction("ACTION");
registerReceiver(receiver, filter);

// 在onDestroy()中注销
@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}
  1. 使用JobScheduler/WorkManager
// 替代网络变化广播
JobScheduler jobScheduler = getSystemService(JobScheduler.class);
JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(this, MyJobService.class))
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
    .build();
jobScheduler.schedule(jobInfo);
  1. 使用其他通信方式
  • LiveData
  • EventBus
  • 直接调用

推荐:

  • 优先使用动态注册
  • 系统事件使用JobScheduler/WorkManager
  • 应用内通信使用其他方式

5.16 广播的安全问题如何避免?

答案:

广播安全问题主要包括未授权接收数据泄露

安全问题:

  1. 未授权接收

    • 其他应用可能接收广播
    • 敏感信息泄露
  2. 数据泄露

    • 广播数据可能被拦截
    • 需要加密敏感数据

解决方案:

  1. 使用权限
<!-- 发送广播时设置权限 -->
<uses-permission android:name="com.example.PERMISSION" />

<!-- 接收者声明权限 -->
<receiver android:name=".MyReceiver"
    android:permission="com.example.PERMISSION">
    <intent-filter>
        <action android:name="ACTION" />
    </intent-filter>
</receiver>
  1. 使用本地广播
// 应用内通信使用本地广播
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
  1. 加密敏感数据
// 加密广播数据
String encryptedData = encrypt(data);
intent.putExtra("data", encryptedData);
  1. 使用显式Intent
// 指定接收者
Intent intent = new Intent(this, MyReceiver.class);
sendBroadcast(intent);

推荐:

  • 设置权限保护
  • 应用内通信使用本地广播
  • 加密敏感数据

5.17 广播的发送方式有哪些?

答案:

广播有三种发送方式:普通广播有序广播粘性广播

1. 普通广播(sendBroadcast)

Intent intent = new Intent("ACTION");
sendBroadcast(intent);
  • 所有接收者同时接收
  • 无序
  • 不能中断

2. 有序广播(sendOrderedBroadcast)

Intent intent = new Intent("ACTION");
sendOrderedBroadcast(intent, null);
  • 按优先级顺序接收
  • 可以中断
  • 可以传递结果

3. 粘性广播(sendStickyBroadcast)

Intent intent = new Intent("ACTION");
sendStickyBroadcast(intent);
  • 广播会保留
  • 后续注册的接收者可以收到
  • 已废弃,不推荐使用

选择建议:

  • 大多数情况:使用普通广播
  • 需要控制顺序:使用有序广播
  • 避免使用粘性广播

5.18 广播的接收顺序如何控制?

答案:

广播接收顺序通过优先级广播类型控制。

控制方式:

  1. 设置优先级(有序广播)
<intent-filter android:priority="1000">
    <action android:name="ACTION" />
</intent-filter>
IntentFilter filter = new IntentFilter("ACTION");
filter.setPriority(1000);
registerReceiver(receiver, filter);
  1. 使用有序广播
Intent intent = new Intent("ACTION");
sendOrderedBroadcast(intent, null);
  1. 中断传播
@Override
public void onReceive(Context context, Intent intent) {
    if (shouldAbort) {
        abortBroadcast(); // 中断,后续接收者收不到
    }
}

优先级说明:

  • 数值范围:-1000 到 1000
  • 数值越大,优先级越高
  • 相同优先级按注册顺序

注意事项:

  • 只对有序广播有效
  • 系统广播优先级通常较高
  • 不建议设置过高的优先级

5.19 广播的权限如何设置?

答案:

广播权限用于控制谁可以发送或接收广播

设置方式:

  1. 发送广播时设置权限
Intent intent = new Intent("ACTION");
sendBroadcast(intent, "com.example.PERMISSION");
  1. 接收者声明权限
<receiver android:name=".MyReceiver"
    android:permission="com.example.PERMISSION">
    <intent-filter>
        <action android:name="ACTION" />
    </intent-filter>
</receiver>
  1. 定义权限
<permission
    android:name="com.example.PERMISSION"
    android:protectionLevel="normal" />
  1. 声明使用权限
<uses-permission android:name="com.example.PERMISSION" />

权限级别:

  • normal:自动授予
  • dangerous:需要用户授权
  • signature:相同签名自动授予

使用场景:

  • 保护敏感广播
  • 控制广播接收者
  • 提升安全性

5.20 广播的性能优化有哪些?

答案:

广播性能优化可以从注册方式处理逻辑使用场景等方面优化。

优化策略:

  1. 使用本地广播

    • 应用内通信使用本地广播
    • 避免跨进程开销
  2. 避免耗时操作

    • onReceive()在主线程执行
    • 耗时操作使用Service或异步任务
  3. 及时注销

    • 动态注册及时注销
    • 避免不必要的接收
  4. 减少广播频率

    • 避免频繁发送广播
    • 合并多个广播
  5. 使用替代方案

    • 应用内通信使用LiveData、EventBus等
    • 系统事件使用JobScheduler/WorkManager

最佳实践:

  • 应用内通信:使用本地广播或其他方式
  • 避免耗时操作
  • 及时注销
  • 减少广播频率