diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 3f99c3b..35c3102 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "docs/adb调试命令/adb常用命令.md", + "file": "docs/cursor/cursor.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "adb常用命令" + "title": "cursor" } } ] @@ -185,41 +185,41 @@ }, "active": "9a5781991bf94030", "lastOpenFiles": [ - "docs/cursor/cursor使用技巧.md", - "docs/adb调试命令/linux常用命令.md", - "docs/adb调试命令/常用linux命令.md", - "docs/adb调试命令/adb常用命令.md", - "docs/Obsidian笔记体系/Daily/2026-01-14.md", - "docs/Obsidian笔记体系/Daily/2026-01-13.md", - "docs/Obsidian笔记体系/Daily/2024-06-02.md", - "docs/cursor/cursor.md", - "docs/Obsidian/高频命令.md", - "docs/Obsidian笔记体系/Projects/知你-调测/知你--调测.md", - "docs/Obsidian笔记体系/Projects/saars开发/数据库配置.md", - "docs/Obsidian笔记体系/Areas/03-Window系统/SurfaceFlinger交互流程.md", - "docs/Obsidian笔记体系/Areas/03-Window系统/窗口类型与层级.md", - "docs/Obsidian笔记体系/Areas/03-Window系统/触摸事件传递.md", - "docs/dify/密码输入错误次数超过限制时的机制.md", - "docs/dify/管理员账户.md", - "docs/Obsidian/2026-01-05 个人文档管理.md", - "未命名.base", - "个人笔记体系.md", - "使用说明.md", - "docs/Obsidian笔记体系/README.md", - "docs/gerrit/gerrit每日工作流程.md", - "docs/Obsidian/资源网站.md", - "docs/Obsidian/重要笔记.md", - "docs/dify/作为安卓高级开发工程师,除了项目管理,你完全可以在技术专项、团队效能和个人成长三大领域构建更懂你的专属助手.md", - "docs/dify/使用dify,可以生成一个专项事务助手吗,比如公司正规化事务助手.md", - "docs/git/git常用命令.md", - "docs/Obsidian笔记体系/Projects/知你-调测", - "docs/gerrit", - "gerrit", - "更新部署脚本/deploy.sh", - "更新部署脚本/deploy.py", - "更新部署脚本/deploy.ps1", - "更新部署脚本/deploy_config.json", - "更新部署脚本", - "新建文件夹" + "docs/Obsidian笔记体系/Daily/2026-01-15.md", + "docs/android面试/面试技巧/薪资谈判.md", + "docs/android面试/面试技巧/项目介绍.md", + "docs/android面试/面试技巧/常见问题.md", + "docs/android面试/面试技巧/自我介绍.md", + "docs/android面试/面试技巧", + "docs/android面试/项目经验/技术选型.md", + "docs/android面试/项目经验/团队协作经验.md", + "docs/android面试/项目经验/问题排查经验.md", + "docs/android面试/项目经验/性能优化实践.md", + "docs/android面试/项目经验/项目架构设计.md", + "docs/android面试/项目经验", + "docs/android面试/系统原理/系统启动流程.md", + "docs/android面试/系统原理/Zygote进程.md", + "docs/android面试/系统原理/进程与线程.md", + "docs/android面试/系统原理/内存管理.md", + "docs/android面试/系统原理/事件分发机制.md", + "docs/android面试/系统原理/View绘制流程.md", + "docs/android面试/系统原理/WMS原理.md", + "docs/android面试/系统原理/AMS原理.md", + "docs/android面试/系统原理/Binder机制.md", + "docs/android面试/系统原理", + "docs/android面试/开源框架/Jetpack组件.md", + "docs/android面试/开源框架/Dagger2原理.md", + "docs/android面试/开源框架/ButterKnife原理.md", + "docs/android面试/开源框架/EventBus原理.md", + "docs/android面试/开源框架/RxJava原理.md", + "docs/android面试/开源框架/Picasso原理.md", + "docs/android面试/开源框架/Glide原理.md", + "docs/android面试/开源框架", + "docs/android面试/算法与数据结构", + "docs/android面试/设计模式", + "docs/android面试/多线程与并发", + "docs/android面试/数据存储", + "docs/android面试/网络编程", + "docs/android面试/性能优化" ] } \ No newline at end of file diff --git a/docs/Obsidian笔记体系/Daily/2026-01-15.md b/docs/Obsidian笔记体系/Daily/2026-01-15.md new file mode 100644 index 0000000..30b39b7 --- /dev/null +++ b/docs/Obsidian笔记体系/Daily/2026-01-15.md @@ -0,0 +1,131 @@ +## 基本信息 +- **日期**: 2026-01-15 +- **星期**: 星期四 +- **天气**: 晴 +- **心情**: 良好 + +## 今日计划 + +### 工作安排 +- [ ] 知你--会员功能 +- [ ] 知你--聊天会话界面跑马灯温馨提示 + +### 学习计划 +- [ ] 学习内容1 +- [ ] 学习内容2 + +### 其他计划 +- [ ] 其他事项1 + +## 工作记录 + +### 已完成 +- ✅ 知你--会员功能开发 + - 时间: HH:MM - HH:MM + - 内容: http://101.43.95.130:8082/c/zhini_im/+/106 + - 收获: + +- ✅ 知你--聊天会话界面跑马灯温馨提示 + - 时间: HH:MM - HH:MM + - 内容: http://101.43.95.130:8082/c/zhini_im/+/107 + - 收获: +### 进行中 +- 🔄 进行中事项1 + - 开始时间: HH:MM + - 当前进度: + - 遇到的问题: + - 下一步计划: + - 继续知你--会员功能开发 + +### 待处理 +- ⏳ 待处理事项1 + - 计划时间: HH:MM + - 优先级: 高/中/低 + +## 学习记录 + +### 技术学习 +- **学习内容**: +- **学习时间**: HH:MM - HH:MM +- **学习方式**: 阅读/实践/视频 +- **关键收获**: +- **相关链接**: [[相关文档]] + +### 源码阅读 +- **阅读模块**: +- **阅读时间**: HH:MM - HH:MM +- **关键理解**: +- **疑问**: +- **相关链接**: [[相关源码]] + +### 问题解决 +- **问题描述**: +- **解决过程**: +- **解决方案**: +- **经验总结**: +- **相关链接**: [[相关文档]] + +## 会议记录 + +### 会议1 +- **时间**: HH:MM - HH:MM +- **主题**: +- **参与人**: +- **关键内容**: +- **行动项**: +- **相关链接**: [[会议记录]] + +## 思考与总结 + +### 今日收获 +1. 收获1 +2. 收获2 +3. 收获3 + +### 今日反思 +- 做得好的地方: +- 需要改进的地方: +- 改进计划: + +### 明日计划 +1. 计划1 +2. 计划2 +3. 计划3 + +## 技术笔记 + +### 技术点1 +- **内容**: +- **代码示例**: +```java +// 代码示例 +``` + +- **关键理解**: +- **相关链接**: [[相关文档]] + +### 技术点2 +- **内容**: +- **关键理解**: + +## 问题与疑问 + +### 问题1 +- **问题描述**: +- **思考**: +- **待解决**: + +### 问题2 +- **问题描述**: +- **思考**: + +## 相关链接 + +- [[相关项目]] +- [[相关文档]] +- [[相关笔记]] + +## 备注 + +- 备注1 +- 备注2 diff --git a/docs/android面试/README.md b/docs/android面试/README.md new file mode 100644 index 0000000..299f7a1 --- /dev/null +++ b/docs/android面试/README.md @@ -0,0 +1,164 @@ +# Android 面试知识体系 + +## 目录结构 + +- [基础知识](#基础知识) +- [核心组件](#核心组件) +- [系统架构](#系统架构) +- [性能优化](#性能优化) +- [网络编程](#网络编程) +- [数据存储](#数据存储) +- [多线程与并发](#多线程与并发) +- [设计模式](#设计模式) +- [算法与数据结构](#算法与数据结构) +- [开源框架](#开源框架) +- [系统原理](#系统原理) +- [项目经验](#项目经验) +- [面试技巧](#面试技巧) + +--- + +## 基础知识 + +- [Java基础](基础知识/Java基础.md) +- [Kotlin基础](基础知识/Kotlin基础.md) +- [Android基础](基础知识/Android基础.md) +- [Android版本特性](基础知识/Android版本特性.md) + +--- + +## 核心组件 + +- [Activity详解](核心组件/Activity详解.md) +- [Service详解](核心组件/Service详解.md) +- [BroadcastReceiver详解](核心组件/BroadcastReceiver详解.md) +- [ContentProvider详解](核心组件/ContentProvider详解.md) +- [Fragment详解](核心组件/Fragment详解.md) +- [Intent详解](核心组件/Intent详解.md) + +--- + +## 系统架构 + +- [MVC架构](系统架构/MVC架构.md) +- [MVP架构](系统架构/MVP架构.md) +- [MVVM架构](系统架构/MVVM架构.md) +- [组件化架构](系统架构/组件化架构.md) +- [模块化设计](系统架构/模块化设计.md) +- [Clean Architecture](系统架构/Clean_Architecture.md) + +--- + +## 性能优化 + +- [启动优化](性能优化/启动优化.md) +- [内存优化](性能优化/内存优化.md) +- [布局优化](性能优化/布局优化.md) +- [网络优化](性能优化/网络优化.md) +- [电量优化](性能优化/电量优化.md) +- [流畅度优化](性能优化/流畅度优化.md) + +--- + +## 网络编程 + +- [HTTP与HTTPS](网络编程/HTTP与HTTPS.md) +- [OkHttp原理](网络编程/OkHttp原理.md) +- [Retrofit原理](网络编程/Retrofit原理.md) +- [WebSocket](网络编程/WebSocket.md) +- [网络请求优化](网络编程/网络请求优化.md) + +--- + +## 数据存储 + +- [SharedPreferences](数据存储/SharedPreferences.md) +- [SQLite数据库](数据存储/SQLite数据库.md) +- [Room数据库](数据存储/Room数据库.md) +- [文件存储](数据存储/文件存储.md) +- [MMKV](数据存储/MMKV.md) + +--- + +## 多线程与并发 + +- [线程基础](多线程与并发/线程基础.md) +- [线程池](多线程与并发/线程池.md) +- [Handler机制](多线程与并发/Handler机制.md) +- [AsyncTask](多线程与并发/AsyncTask.md) +- [协程](多线程与并发/协程.md) +- [并发编程](多线程与并发/并发编程.md) + +--- + +## 设计模式 + +- [单例模式](设计模式/单例模式.md) +- [工厂模式](设计模式/工厂模式.md) +- [观察者模式](设计模式/观察者模式.md) +- [适配器模式](设计模式/适配器模式.md) +- [建造者模式](设计模式/建造者模式.md) +- [策略模式](设计模式/策略模式.md) +- [代理模式](设计模式/代理模式.md) +- [装饰者模式](设计模式/装饰者模式.md) + +--- + +## 算法与数据结构 + +- [数组与链表](算法与数据结构/数组与链表.md) +- [栈与队列](算法与数据结构/栈与队列.md) +- [树与二叉树](算法与数据结构/树与二叉树.md) +- [排序算法](算法与数据结构/排序算法.md) +- [查找算法](算法与数据结构/查找算法.md) +- [动态规划](算法与数据结构/动态规划.md) +- [字符串算法](算法与数据结构/字符串算法.md) + +--- + +## 开源框架 + +- [Glide原理](开源框架/Glide原理.md) +- [Picasso原理](开源框架/Picasso原理.md) +- [RxJava原理](开源框架/RxJava原理.md) +- [EventBus原理](开源框架/EventBus原理.md) +- [ButterKnife原理](开源框架/ButterKnife原理.md) +- [Dagger2原理](开源框架/Dagger2原理.md) +- [Jetpack组件](开源框架/Jetpack组件.md) + +--- + +## 系统原理 + +- [Binder机制](系统原理/Binder机制.md) +- [AMS原理](系统原理/AMS原理.md) +- [WMS原理](系统原理/WMS原理.md) +- [View绘制流程](系统原理/View绘制流程.md) +- [事件分发机制](系统原理/事件分发机制.md) +- [内存管理](系统原理/内存管理.md) +- [进程与线程](系统原理/进程与线程.md) +- [Zygote进程](系统原理/Zygote进程.md) +- [系统启动流程](系统原理/系统启动流程.md) + +--- + +## 项目经验 + +- [项目架构设计](项目经验/项目架构设计.md) +- [性能优化实践](项目经验/性能优化实践.md) +- [问题排查经验](项目经验/问题排查经验.md) +- [团队协作经验](项目经验/团队协作经验.md) +- [技术选型](项目经验/技术选型.md) + +--- + +## 面试技巧 + +- [自我介绍](面试技巧/自我介绍.md) +- [常见问题](面试技巧/常见问题.md) +- [项目介绍](面试技巧/项目介绍.md) +- [薪资谈判](面试技巧/薪资谈判.md) + +--- + +*最后更新:2024年* diff --git a/docs/android面试/基础知识/Android基础.md b/docs/android面试/基础知识/Android基础.md new file mode 100644 index 0000000..8680c00 --- /dev/null +++ b/docs/android面试/基础知识/Android基础.md @@ -0,0 +1,522 @@ +# Android基础 + +## 目录 +- [Android系统架构](#android系统架构) +- [应用组件](#应用组件) +- [应用生命周期](#应用生命周期) +- [资源管理](#资源管理) +- [权限系统](#权限系统) +- [应用签名](#应用签名) +- [打包流程](#打包流程) +- [版本适配](#版本适配) +- [面试常见问题](#面试常见问题) + +--- + +## Android系统架构 + +### 四层架构 + +``` +┌─────────────────────────────────┐ +│ Applications(应用层) │ +│ (系统应用、第三方应用) │ +└─────────────────────────────────┘ + │ +┌─────────────────────────────────┐ +│ Application Framework(应用框架层)│ +│ (Activity Manager, Content Provider等)│ +└─────────────────────────────────┘ + │ +┌─────────────────────────────────┐ +│ Libraries & Android Runtime │ +│ (系统库、Android运行时) │ +└─────────────────────────────────┘ + │ +┌─────────────────────────────────┐ +│ Linux Kernel(Linux内核) │ +│ (驱动、内存管理、进程管理等) │ +└─────────────────────────────────┘ +``` + +### 各层说明 + +#### 1. 应用层(Applications) +- 系统应用:电话、短信、浏览器等 +- 第三方应用:用户安装的应用 + +#### 2. 应用框架层(Application Framework) +- **Activity Manager**:管理 Activity 生命周期 +- **Window Manager**:管理窗口 +- **Content Provider**:数据共享 +- **View System**:UI 组件 +- **Package Manager**:应用管理 +- **Telephony Manager**:电话管理 +- **Resource Manager**:资源管理 +- **Location Manager**:位置管理 +- **Notification Manager**:通知管理 + +#### 3. 系统库和运行时 +- **系统库**:SQLite、WebKit、OpenGL ES 等 +- **Android Runtime**:ART(Android 5.0+)或 Dalvik + +#### 4. Linux 内核 +- 驱动:显示驱动、音频驱动、相机驱动等 +- 内存管理、进程管理、网络协议栈 + +--- + +## 应用组件 + +### 四大组件 + +#### 1. Activity(活动) + +```java +// Activity:用户界面 +public class MainActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } +} +``` + +#### 2. Service(服务) + +```java +// Service:后台服务 +public class MyService extends Service { + @Override + public IBinder onBind(Intent intent) { + return null; + } +} +``` + +#### 3. BroadcastReceiver(广播接收器) + +```java +// BroadcastReceiver:接收广播 +public class MyReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // 处理广播 + } +} +``` + +#### 4. ContentProvider(内容提供者) + +```java +// ContentProvider:数据共享 +public class MyProvider extends ContentProvider { + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + // 查询数据 + return null; + } +} +``` + +--- + +## 应用生命周期 + +### Application 生命周期 + +```java +public class MyApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + // 应用创建时调用 + } + + @Override + public void onTerminate() { + super.onTerminate(); + // 应用终止时调用(通常不会调用) + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + // 内存不足时调用 + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + // 内存紧张时调用 + } +} +``` + +### Activity 生命周期 + +```java +public class MainActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 创建 Activity + } + + @Override + protected void onStart() { + super.onStart(); + // Activity 可见但不可交互 + } + + @Override + protected void onResume() { + super.onResume(); + // Activity 可见且可交互 + } + + @Override + protected void onPause() { + super.onPause(); + // Activity 失去焦点 + } + + @Override + protected void onStop() { + super.onStop(); + // Activity 不可见 + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // Activity 被销毁 + } + + @Override + protected void onRestart() { + super.onRestart(); + // Activity 重新启动 + } +} +``` + +--- + +## 资源管理 + +### 资源类型 + +```xml + +MyApp + + +#3F51B5 + + +16dp + + +... + + +... +``` + +### 资源访问 + +```java +// 代码中访问资源 +String appName = getString(R.string.app_name); +int color = getColor(R.color.primary); +float padding = getResources().getDimension(R.dimen.padding); + +// XML 中访问资源 +android:text="@string/app_name" +android:background="@color/primary" +android:padding="@dimen/padding" +``` + +### 多语言支持 + +```xml + +Hello + + +你好 + + +Hello +``` + +--- + +## 权限系统 + +### 权限类型 + +#### 1. 普通权限(Normal Permissions) + +```xml + + + +``` + +#### 2. 危险权限(Dangerous Permissions) + +```java +// Android 6.0+ 需要运行时申请 +if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.CAMERA}, + REQUEST_CODE); +} + +@Override +public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == REQUEST_CODE) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // 权限已授予 + } else { + // 权限被拒绝 + } + } +} +``` + +### 权限组 + +```java +// 权限组 +// - CALENDAR:日历权限 +// - CAMERA:相机权限 +// - CONTACTS:联系人权限 +// - LOCATION:位置权限 +// - MICROPHONE:麦克风权限 +// - PHONE:电话权限 +// - SENSORS:传感器权限 +// - SMS:短信权限 +// - STORAGE:存储权限 +``` + +--- + +## 应用签名 + +### 签名作用 + +1. **身份标识**:标识应用开发者 +2. **完整性验证**:确保应用未被篡改 +3. **权限控制**:相同签名的应用可以共享数据 + +### 签名流程 + +```bash +# 生成密钥库 +keytool -genkey -v -keystore my-release-key.jks + -keyalg RSA -keysize 2048 -validity 10000 + -alias my-key-alias + +# 签名 APK +jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 + -keystore my-release-key.jks app-release-unsigned.apk my-key-alias + +# 验证签名 +jarsigner -verify -verbose -certs app-release-unsigned.apk +``` + +### Gradle 配置 + +```gradle +android { + signingConfigs { + release { + storeFile file('my-release-key.jks') + storePassword 'password' + keyAlias 'my-key-alias' + keyPassword 'password' + } + } + + buildTypes { + release { + signingConfig signingConfigs.release + } + } +} +``` + +--- + +## 打包流程 + +### APK 构建流程 + +``` +1. 编译 Java/Kotlin 代码 → .class 文件 +2. 转换为 DEX 文件 +3. 打包资源文件 +4. 生成未签名 APK +5. 签名 APK +6. 对齐优化(zipalign) +7. 生成最终 APK +``` + +### 构建工具 + +```gradle +// build.gradle +android { + compileSdkVersion 30 + + defaultConfig { + applicationId "com.example.app" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} +``` + +--- + +## 版本适配 + +### Target SDK 版本 + +```gradle +android { + defaultConfig { + targetSdkVersion 30 // 目标 SDK 版本 + } +} +``` + +### 版本适配要点 + +#### Android 6.0 (API 23) - 运行时权限 + +```java +// 需要运行时申请危险权限 +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(...); +} +``` + +#### Android 7.0 (API 24) - FileProvider + +```xml + + + + +``` + +#### Android 8.0 (API 26) - 通知渠道 + +```java +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, "Channel Name", NotificationManager.IMPORTANCE_DEFAULT); + notificationManager.createNotificationChannel(channel); +} +``` + +#### Android 9.0 (API 28) - 网络安全 + +```xml + + + + + + + + +``` + +#### Android 10 (API 29) - 分区存储 + +```java +// 使用 MediaStore 访问文件 +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // 使用作用域存储 +} +``` + +#### Android 11 (API 30) - 包可见性 + +```xml + + + + +``` + +--- + +## 面试常见问题 + +### Q1: Android 系统架构? + +**答案:** +四层架构: +1. **应用层**:系统应用和第三方应用 +2. **应用框架层**:Activity Manager、Window Manager 等 +3. **系统库和运行时**:系统库、ART +4. **Linux 内核**:驱动、内存管理、进程管理 + +### Q2: 四大组件? + +**答案:** +1. **Activity**:用户界面 +2. **Service**:后台服务 +3. **BroadcastReceiver**:广播接收器 +4. **ContentProvider**:内容提供者 + +### Q3: Activity 生命周期? + +**答案:** +onCreate → onStart → onResume → onPause → onStop → onDestroy +onRestart(从 onStop 恢复时调用) + +### Q4: 权限系统? + +**答案:** +- **普通权限**:安装时自动授予 +- **危险权限**:Android 6.0+ 需要运行时申请 +- **特殊权限**:需要特殊处理 + +### Q5: 版本适配要点? + +**答案:** +- Android 6.0:运行时权限 +- Android 7.0:FileProvider +- Android 8.0:通知渠道 +- Android 9.0:网络安全 +- Android 10:分区存储 +- Android 11:包可见性 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/基础知识/Android版本特性.md b/docs/android面试/基础知识/Android版本特性.md new file mode 100644 index 0000000..a7a8464 --- /dev/null +++ b/docs/android面试/基础知识/Android版本特性.md @@ -0,0 +1,14 @@ +# Android版本特性 + +## 待补充内容 + +- Android 5.0 (Lollipop) +- Android 6.0 (Marshmallow) +- Android 7.0 (Nougat) +- Android 8.0 (Oreo) +- Android 9.0 (Pie) +- Android 10 +- Android 11 +- Android 12 +- Android 13 +- Android 14 diff --git a/docs/android面试/基础知识/Java基础.md b/docs/android面试/基础知识/Java基础.md new file mode 100644 index 0000000..3ec4651 --- /dev/null +++ b/docs/android面试/基础知识/Java基础.md @@ -0,0 +1,543 @@ +# Java基础 + +## 目录 +- [面向对象编程](#面向对象编程) +- [集合框架](#集合框架) +- [异常处理](#异常处理) +- [泛型](#泛型) +- [反射](#反射) +- [注解](#注解) +- [多线程基础](#多线程基础) +- [IO流](#io流) +- [JVM基础](#jvm基础) +- [面试常见问题](#面试常见问题) + +--- + +## 面向对象编程 + +### 三大特性 + +#### 1. 封装 + +```java +// 封装:隐藏内部实现,提供公共接口 +public class User { + private String name; // 私有属性 + private int age; + + // 公共方法访问私有属性 + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} +``` + +#### 2. 继承 + +```java +// 继承:子类继承父类的属性和方法 +public class Animal { + protected String name; + + public void eat() { + System.out.println("Animal is eating"); + } +} + +public class Dog extends Animal { + public void bark() { + System.out.println("Dog is barking"); + } + + @Override + public void eat() { + System.out.println("Dog is eating"); + } +} +``` + +#### 3. 多态 + +```java +// 多态:同一接口,不同实现 +Animal animal1 = new Dog(); +Animal animal2 = new Cat(); +animal1.eat(); // 调用 Dog 的 eat 方法 +animal2.eat(); // 调用 Cat 的 eat 方法 +``` + +### 抽象类和接口 + +```java +// 抽象类:可以有抽象方法和具体方法 +public abstract class Animal { + public abstract void makeSound(); + + public void sleep() { + System.out.println("Animal is sleeping"); + } +} + +// 接口:只有抽象方法(Java 8+ 可以有默认方法) +public interface Flyable { + void fly(); + + default void land() { + System.out.println("Landing"); + } +} +``` + +--- + +## 集合框架 + +### List + +```java +// ArrayList:动态数组 +List list = new ArrayList<>(); +list.add("A"); +list.add("B"); +list.get(0); // "A" + +// LinkedList:双向链表 +List linkedList = new LinkedList<>(); +linkedList.add("A"); +linkedList.addFirst("B"); +``` + +### Set + +```java +// HashSet:无序,不重复 +Set set = new HashSet<>(); +set.add("A"); +set.add("B"); +set.add("A"); // 重复,不会添加 + +// TreeSet:有序,不重复 +Set treeSet = new TreeSet<>(); +treeSet.add("C"); +treeSet.add("A"); +treeSet.add("B"); +// 输出:A, B, C +``` + +### Map + +```java +// HashMap:键值对,无序 +Map map = new HashMap<>(); +map.put("A", 1); +map.put("B", 2); +map.get("A"); // 1 + +// TreeMap:键值对,有序 +Map treeMap = new TreeMap<>(); +treeMap.put("C", 3); +treeMap.put("A", 1); +treeMap.put("B", 2); +// 按键排序:A, B, C +``` + +### 集合遍历 + +```java +// for-each 循环 +for (String item : list) { + System.out.println(item); +} + +// Iterator +Iterator iterator = list.iterator(); +while (iterator.hasNext()) { + String item = iterator.next(); + System.out.println(item); +} + +// Stream API(Java 8+) +list.stream() + .filter(s -> s.startsWith("A")) + .forEach(System.out::println); +``` + +--- + +## 异常处理 + +### 异常类型 + +```java +// Throwable +// ├── Error(错误,不可恢复) +// └── Exception(异常,可恢复) +// ├── RuntimeException(运行时异常) +// └── CheckedException(检查异常) + +// RuntimeException:不需要捕获 +int[] arr = new int[5]; +int value = arr[10]; // ArrayIndexOutOfBoundsException + +// CheckedException:必须捕获或声明 +try { + FileInputStream fis = new FileInputStream("file.txt"); +} catch (FileNotFoundException e) { + e.printStackTrace(); +} +``` + +### 异常处理 + +```java +// try-catch-finally +try { + // 可能抛出异常的代码 + int result = 10 / 0; +} catch (ArithmeticException e) { + // 处理异常 + System.out.println("除零错误"); +} catch (Exception e) { + // 处理其他异常 + e.printStackTrace(); +} finally { + // 无论是否异常都会执行 + System.out.println("清理资源"); +} + +// try-with-resources(Java 7+) +try (FileInputStream fis = new FileInputStream("file.txt")) { + // 使用资源 +} catch (IOException e) { + e.printStackTrace(); +} // 自动关闭资源 +``` + +### 自定义异常 + +```java +// 自定义异常 +public class CustomException extends Exception { + public CustomException(String message) { + super(message); + } +} + +// 使用 +public void method() throws CustomException { + throw new CustomException("自定义异常"); +} +``` + +--- + +## 泛型 + +### 泛型类 + +```java +// 泛型类 +public class Box { + private T item; + + public void setItem(T item) { + this.item = item; + } + + public T getItem() { + return item; + } +} + +// 使用 +Box stringBox = new Box<>(); +stringBox.setItem("Hello"); +String value = stringBox.getItem(); +``` + +### 泛型方法 + +```java +// 泛型方法 +public T getValue(T value) { + return value; +} + +// 使用 +String str = getValue("Hello"); +Integer num = getValue(123); +``` + +### 通配符 + +```java +// 上界通配符:? extends T +List list1; // 可以是 Number 及其子类 + +// 下界通配符:? super T +List list2; // 可以是 Integer 及其父类 + +// 无界通配符:? +List list3; // 可以是任何类型 +``` + +--- + +## 反射 + +### 获取 Class 对象 + +```java +// 方式1:通过类名 +Class clazz = String.class; + +// 方式2:通过对象 +String str = "Hello"; +Class clazz2 = str.getClass(); + +// 方式3:通过类名字符串 +Class clazz3 = Class.forName("java.lang.String"); +``` + +### 反射操作 + +```java +// 创建对象 +Class clazz = User.class; +User user = (User) clazz.newInstance(); + +// 获取字段 +Field field = clazz.getDeclaredField("name"); +field.setAccessible(true); +field.set(user, "John"); + +// 获取方法 +Method method = clazz.getMethod("getName"); +String name = (String) method.invoke(user); + +// 获取构造函数 +Constructor constructor = clazz.getConstructor(String.class); +User user2 = (User) constructor.newInstance("John"); +``` + +--- + +## 注解 + +### 内置注解 + +```java +// @Override:重写方法 +@Override +public void method() { } + +// @Deprecated:已废弃 +@Deprecated +public void oldMethod() { } + +// @SuppressWarnings:抑制警告 +@SuppressWarnings("unchecked") +public void method() { } +``` + +### 自定义注解 + +```java +// 定义注解 +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MyAnnotation { + String value() default ""; + int count() default 0; +} + +// 使用注解 +@MyAnnotation(value = "test", count = 5) +public void method() { } + +// 读取注解 +Method method = MyClass.class.getMethod("method"); +MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); +String value = annotation.value(); +``` + +--- + +## 多线程基础 + +### 创建线程 + +```java +// 方式1:继承 Thread +public class MyThread extends Thread { + @Override + public void run() { + System.out.println("Thread running"); + } +} +MyThread thread = new MyThread(); +thread.start(); + +// 方式2:实现 Runnable +public class MyRunnable implements Runnable { + @Override + public void run() { + System.out.println("Thread running"); + } +} +Thread thread2 = new Thread(new MyRunnable()); +thread2.start(); + +// 方式3:Lambda 表达式 +Thread thread3 = new Thread(() -> { + System.out.println("Thread running"); +}); +thread3.start(); +``` + +### 线程同步 + +```java +// synchronized 关键字 +public class Counter { + private int count = 0; + + public synchronized void increment() { + count++; + } +} + +// Lock +private Lock lock = new ReentrantLock(); + +public void method() { + lock.lock(); + try { + // 临界区代码 + } finally { + lock.unlock(); + } +} +``` + +--- + +## IO流 + +### 字节流 + +```java +// FileInputStream / FileOutputStream +try (FileInputStream fis = new FileInputStream("input.txt"); + FileOutputStream fos = new FileOutputStream("output.txt")) { + int data; + while ((data = fis.read()) != -1) { + fos.write(data); + } +} +``` + +### 字符流 + +```java +// FileReader / FileWriter +try (FileReader fr = new FileReader("input.txt"); + FileWriter fw = new FileWriter("output.txt")) { + int data; + while ((data = fr.read()) != -1) { + fw.write(data); + } +} +``` + +### 缓冲流 + +```java +// BufferedInputStream / BufferedOutputStream +try (BufferedInputStream bis = new BufferedInputStream( + new FileInputStream("input.txt")); + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream("output.txt"))) { + int data; + while ((data = bis.read()) != -1) { + bos.write(data); + } +} +``` + +--- + +## JVM基础 + +### 内存区域 + +```java +// JVM 内存区域 +// 1. 程序计数器:当前执行指令的地址 +// 2. 虚拟机栈:方法执行时的栈帧 +// 3. 本地方法栈:Native 方法执行 +// 4. 堆:对象实例 +// 5. 方法区:类信息、常量、静态变量 +``` + +### 垃圾回收 + +```java +// GC 算法 +// 1. 标记-清除 +// 2. 标记-复制 +// 3. 标记-整理 +// 4. 分代收集 + +// GC 类型 +// - Minor GC:新生代回收 +// - Major GC:老年代回收 +// - Full GC:整个堆回收 +``` + +--- + +## 面试常见问题 + +### Q1: Java 三大特性? + +**答案:** +1. **封装**:隐藏内部实现,提供公共接口 +2. **继承**:子类继承父类的属性和方法 +3. **多态**:同一接口,不同实现 + +### Q2: ArrayList 和 LinkedList 的区别? + +**答案:** +- **ArrayList**:基于数组,随机访问快,插入删除慢 +- **LinkedList**:基于链表,随机访问慢,插入删除快 + +### Q3: HashMap 的实现原理? + +**答案:** +- 基于数组和链表(Java 8+ 使用红黑树) +- 通过 hashCode 计算数组索引 +- 解决冲突使用链表或红黑树 + +### Q4: 异常处理的机制? + +**答案:** +- **try-catch-finally**:捕获和处理异常 +- **throws**:声明可能抛出的异常 +- **throw**:抛出异常 + +### Q5: 泛型的作用? + +**答案:** +1. 类型安全:编译时检查类型 +2. 消除强制转换 +3. 提高代码可读性 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/基础知识/Kotlin基础.md b/docs/android面试/基础知识/Kotlin基础.md new file mode 100644 index 0000000..d35b845 --- /dev/null +++ b/docs/android面试/基础知识/Kotlin基础.md @@ -0,0 +1,451 @@ +# Kotlin基础 + +## 目录 +- [基本语法](#基本语法) +- [空安全](#空安全) +- [扩展函数](#扩展函数) +- [数据类](#数据类) +- [密封类](#密封类) +- [协程基础](#协程基础) +- [委托](#委托) +- [高阶函数](#高阶函数) +- [Lambda表达式](#lambda表达式) +- [面试常见问题](#面试常见问题) + +--- + +## 基本语法 + +### 变量声明 + +```kotlin +// 可变变量 +var name: String = "John" +name = "Jane" + +// 不可变变量 +val age: Int = 25 +// age = 26 // 编译错误 + +// 类型推断 +var count = 10 // 自动推断为 Int +var message = "Hello" // 自动推断为 String +``` + +### 函数定义 + +```kotlin +// 普通函数 +fun add(a: Int, b: Int): Int { + return a + b +} + +// 单表达式函数 +fun multiply(a: Int, b: Int) = a * b + +// 默认参数 +fun greet(name: String = "Guest") { + println("Hello, $name") +} + +// 命名参数 +greet(name = "John") +``` + +### 控制流 + +```kotlin +// if 表达式 +val max = if (a > b) a else b + +// when 表达式(类似 switch) +when (x) { + 1 -> println("One") + 2 -> println("Two") + else -> println("Other") +} + +// for 循环 +for (i in 1..10) { + println(i) +} + +for (item in list) { + println(item) +} + +// while 循环 +while (condition) { + // 代码 +} +``` + +--- + +## 空安全 + +### 可空类型 + +```kotlin +// 可空类型 +var name: String? = null +name = "John" + +// 安全调用 +val length = name?.length // 如果 name 为 null,返回 null + +// Elvis 操作符 +val length2 = name?.length ?: 0 // 如果 name 为 null,返回 0 + +// 非空断言 +val length3 = name!!.length // 如果 name 为 null,抛出异常 +``` + +### 空安全操作 + +```kotlin +// 安全调用链 +val length = user?.address?.street?.length + +// let 函数 +name?.let { + println(it.length) +} + +// 类型检查 +if (obj is String) { + println(obj.length) // 自动转换为 String +} +``` + +--- + +## 扩展函数 + +### 扩展函数定义 + +```kotlin +// 为 String 添加扩展函数 +fun String.removeSpaces(): String { + return this.replace(" ", "") +} + +// 使用 +val text = "Hello World" +val result = text.removeSpaces() // "HelloWorld" + +// 为 View 添加扩展函数 +fun View.show() { + this.visibility = View.VISIBLE +} + +fun View.hide() { + this.visibility = View.GONE +} +``` + +### 扩展属性 + +```kotlin +// 扩展属性 +val String.lastChar: Char + get() = this[this.length - 1] + +// 使用 +val text = "Hello" +val last = text.lastChar // 'o' +``` + +--- + +## 数据类 + +### 数据类定义 + +```kotlin +// 数据类:自动生成 equals、hashCode、toString、copy 等方法 +data class User( + val id: Int, + val name: String, + val email: String +) + +// 使用 +val user = User(1, "John", "john@example.com") +println(user) // User(id=1, name=John, email=john@example.com) + +// 复制 +val user2 = user.copy(name = "Jane") + +// 解构 +val (id, name, email) = user +``` + +### 数据类限制 + +```kotlin +// 数据类限制 +// 1. 主构造函数至少有一个参数 +// 2. 主构造函数参数必须标记为 val 或 var +// 3. 数据类不能是抽象、开放、密封或内部类 +``` + +--- + +## 密封类 + +### 密封类定义 + +```kotlin +// 密封类:限制子类只能在同一个文件中 +sealed class Result { + data class Success(val data: T) : Result() + data class Error(val message: String) : Result() + object Loading : Result() +} + +// 使用 +fun handleResult(result: Result) { + when (result) { + is Result.Success -> println(result.data) + is Result.Error -> println(result.message) + is Result.Loading -> println("Loading...") + } +} +``` + +--- + +## 协程基础 + +### 协程创建 + +```kotlin +// 启动协程 +GlobalScope.launch { + delay(1000) + println("Hello from coroutine") +} + +// 使用 runBlocking +runBlocking { + delay(1000) + println("Hello") +} + +// 使用 CoroutineScope +val scope = CoroutineScope(Dispatchers.Main) +scope.launch { + delay(1000) + println("Hello") +} +``` + +### 协程作用域 + +```kotlin +// CoroutineScope +class MyActivity : AppCompatActivity(), CoroutineScope by MainScope() { + override fun onDestroy() { + cancel() // 取消所有协程 + super.onDestroy() + } +} + +// viewModelScope(ViewModel) +class MyViewModel : ViewModel() { + fun loadData() { + viewModelScope.launch { + // 协程代码 + } + } +} + +// lifecycleScope(LifecycleOwner) +lifecycleScope.launch { + // 协程代码 +} +``` + +### 协程上下文 + +```kotlin +// Dispatchers +launch(Dispatchers.Main) { + // 主线程 +} + +launch(Dispatchers.IO) { + // IO 线程 +} + +launch(Dispatchers.Default) { + // 默认线程池 +} +``` + +--- + +## 委托 + +### 类委托 + +```kotlin +// 类委托:将接口实现委托给另一个对象 +interface Base { + fun print() +} + +class BaseImpl(val x: Int) : Base { + override fun print() { + println(x) + } +} + +class Derived(b: Base) : Base by b + +// 使用 +val b = BaseImpl(10) +val derived = Derived(b) +derived.print() // 10 +``` + +### 属性委托 + +```kotlin +// lazy 委托:延迟初始化 +val lazyValue: String by lazy { + println("Computed!") + "Hello" +} + +// 使用 +println(lazyValue) // 第一次访问时计算 +println(lazyValue) // 直接返回缓存值 + +// observable 委托:监听属性变化 +var name: String by Delegates.observable("Initial") { prop, old, new -> + println("$old -> $new") +} +``` + +--- + +## 高阶函数 + +### 高阶函数定义 + +```kotlin +// 高阶函数:接受函数作为参数或返回函数 +fun operation(x: Int, y: Int, op: (Int, Int) -> Int): Int { + return op(x, y) +} + +// 使用 +val result = operation(5, 3) { a, b -> a + b } // 8 +val result2 = operation(5, 3) { a, b -> a * b } // 15 +``` + +### 常用高阶函数 + +```kotlin +val list = listOf(1, 2, 3, 4, 5) + +// map:转换 +val doubled = list.map { it * 2 } // [2, 4, 6, 8, 10] + +// filter:过滤 +val evens = list.filter { it % 2 == 0 } // [2, 4] + +// reduce:累积 +val sum = list.reduce { acc, i -> acc + i } // 15 + +// forEach:遍历 +list.forEach { println(it) } +``` + +--- + +## Lambda表达式 + +### Lambda 语法 + +```kotlin +// Lambda 表达式 +val sum = { x: Int, y: Int -> x + y } +println(sum(1, 2)) // 3 + +// 简化语法 +val list = listOf(1, 2, 3) +list.forEach { println(it) } + +// it:单个参数的隐式名称 +list.map { it * 2 } +``` + +### Lambda 与集合 + +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) + +// map +val squares = numbers.map { it * it } // [1, 4, 9, 16, 25] + +// filter +val evens = numbers.filter { it % 2 == 0 } // [2, 4] + +// find +val firstEven = numbers.find { it % 2 == 0 } // 2 + +// any / all +val hasEven = numbers.any { it % 2 == 0 } // true +val allEven = numbers.all { it % 2 == 0 } // false +``` + +--- + +## 面试常见问题 + +### Q1: Kotlin 和 Java 的区别? + +**答案:** +1. **空安全**:Kotlin 有可空类型,编译时检查 +2. **简洁性**:Kotlin 代码更简洁 +3. **扩展函数**:可以为类添加新方法 +4. **数据类**:自动生成常用方法 +5. **协程**:轻量级线程 + +### Q2: Kotlin 空安全机制? + +**答案:** +- 可空类型:`String?` 表示可能为 null +- 安全调用:`?.` 操作符 +- Elvis 操作符:`?:` 提供默认值 +- 非空断言:`!!` 强制非空 + +### Q3: 扩展函数的作用? + +**答案:** +- 为现有类添加新方法 +- 不修改原类代码 +- 提高代码复用性 +- 使代码更易读 + +### Q4: 数据类的特点? + +**答案:** +- 自动生成 `equals`、`hashCode`、`toString`、`copy` 方法 +- 支持解构声明 +- 简化数据模型定义 + +### Q5: 协程的优势? + +**答案:** +1. 轻量级:比线程更轻量 +2. 挂起恢复:可以挂起和恢复 +3. 结构化并发:自动管理生命周期 +4. 简化异步代码 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/多线程与并发/AsyncTask.md b/docs/android面试/多线程与并发/AsyncTask.md new file mode 100644 index 0000000..e718496 --- /dev/null +++ b/docs/android面试/多线程与并发/AsyncTask.md @@ -0,0 +1,296 @@ +# AsyncTask + +## 目录 +- [AsyncTask原理](#asynctask原理) +- [AsyncTask生命周期](#asynctask生命周期) +- [AsyncTask执行流程](#asynctask执行流程) +- [AsyncTask问题](#asynctask问题) +- [AsyncTask替代方案](#asynctask替代方案) +- [AsyncTask最佳实践](#asynctask最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## AsyncTask原理 + +### AsyncTask 简介 + +```java +// AsyncTask:异步任务类 +// 在后台线程执行任务,在主线程更新 UI +// 已废弃(Android 11+),推荐使用协程或 ExecutorService +``` + +### AsyncTask 结构 + +```java +public abstract class AsyncTask { + // Params:参数类型 + // Progress:进度类型 + // Result:结果类型 + + // 在后台线程执行 + protected abstract Result doInBackground(Params... params); + + // 在主线程执行(任务开始前) + protected void onPreExecute() { } + + // 在主线程执行(进度更新) + protected void onProgressUpdate(Progress... values) { } + + // 在主线程执行(任务完成后) + protected void onPostExecute(Result result) { } +} +``` + +--- + +## AsyncTask生命周期 + +### 生命周期方法 + +```java +public class MyAsyncTask extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + // 主线程:任务开始前 + } + + @Override + protected String doInBackground(String... params) { + // 后台线程:执行任务 + for (int i = 0; i < 100; i++) { + publishProgress(i); // 更新进度 + } + return "Result"; + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + // 主线程:进度更新 + int progress = values[0]; + progressBar.setProgress(progress); + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + // 主线程:任务完成 + textView.setText(result); + } + + @Override + protected void onCancelled() { + super.onCancelled(); + // 主线程:任务取消 + } +} +``` + +--- + +## AsyncTask执行流程 + +### 执行流程 + +``` +1. execute() → 创建任务 +2. onPreExecute() → 主线程执行 +3. doInBackground() → 后台线程执行 +4. publishProgress() → 更新进度 +5. onProgressUpdate() → 主线程更新 UI +6. onPostExecute() → 主线程执行(任务完成) +``` + +### 执行示例 + +```java +// 创建并执行 +MyAsyncTask task = new MyAsyncTask(); +task.execute("param1", "param2"); + +// 取消任务 +task.cancel(true); +``` + +--- + +## AsyncTask问题 + +### 1. 内存泄漏 + +```java +// ❌ 问题:AsyncTask 持有 Activity 引用 +public class MainActivity extends AppCompatActivity { + private MyAsyncTask task; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + task = new MyAsyncTask(); + task.execute(); + } + + // 如果 Activity 销毁,但 AsyncTask 还在执行 + // Activity 无法被回收 +} +``` + +### 2. 配置变更问题 + +```java +// ❌ 问题:屏幕旋转时 Activity 重建 +// AsyncTask 持有旧 Activity 引用 +// 可能导致崩溃或内存泄漏 +``` + +### 3. 串行执行 + +```java +// AsyncTask 默认串行执行 +// 多个 AsyncTask 按顺序执行 +// 可能影响性能 +``` + +### 4. 已废弃 + +```java +// Android 11+ 已废弃 AsyncTask +// 推荐使用协程或 ExecutorService +``` + +--- + +## AsyncTask替代方案 + +### 方案1:协程(推荐) + +```kotlin +// 使用协程 +lifecycleScope.launch { + val result = withContext(Dispatchers.IO) { + // 后台执行 + loadData() + } + // 主线程更新 UI + updateUI(result) +} +``` + +### 方案2:ExecutorService + +```java +// 使用线程池 +ExecutorService executor = Executors.newCachedThreadPool(); +executor.execute(() -> { + // 后台执行 + String result = loadData(); + + // 主线程更新 UI + runOnUiThread(() -> { + updateUI(result); + }); +}); +``` + +### 方案3:RxJava + +```java +// 使用 RxJava +Observable.fromCallable(() -> loadData()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + updateUI(result); + }); +``` + +--- + +## AsyncTask最佳实践 + +### 1. 使用静态内部类 + +```java +// ✅ 使用静态内部类避免内存泄漏 +private static class MyAsyncTask extends AsyncTask { + private WeakReference activity; + + MyAsyncTask(MainActivity activity) { + this.activity = new WeakReference<>(activity); + } + + @Override + protected String doInBackground(String... params) { + // 执行任务 + return null; + } + + @Override + protected void onPostExecute(String result) { + MainActivity act = activity.get(); + if (act != null && !act.isFinishing()) { + // 更新 UI + } + } +} +``` + +### 2. 及时取消 + +```java +@Override +protected void onDestroy() { + super.onDestroy(); + if (task != null) { + task.cancel(true); + } +} +``` + +### 3. 使用替代方案 + +```java +// 推荐使用协程或 ExecutorService +// AsyncTask 已废弃 +``` + +--- + +## 面试常见问题 + +### Q1: AsyncTask 的原理? + +**答案:** +- 在后台线程执行任务(doInBackground) +- 在主线程更新 UI(onPostExecute、onProgressUpdate) +- 使用线程池执行任务 + +### Q2: AsyncTask 的问题? + +**答案:** +1. 内存泄漏:持有 Activity 引用 +2. 配置变更:屏幕旋转时可能出问题 +3. 串行执行:默认串行,可能影响性能 +4. 已废弃:Android 11+ 已废弃 + +### Q3: AsyncTask 的替代方案? + +**答案:** +1. **协程**:Kotlin 官方推荐 +2. **ExecutorService**:线程池 +3. **RxJava**:响应式编程 + +### Q4: 如何避免 AsyncTask 内存泄漏? + +**答案:** +1. 使用静态内部类 +2. 使用 WeakReference +3. 及时取消任务 +4. 使用替代方案 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/多线程与并发/Handler机制.md b/docs/android面试/多线程与并发/Handler机制.md new file mode 100644 index 0000000..56ffaf5 --- /dev/null +++ b/docs/android面试/多线程与并发/Handler机制.md @@ -0,0 +1,366 @@ +# Handler机制 + +## 目录 +- [Handler原理](#handler原理) +- [Looper机制](#looper机制) +- [MessageQueue](#messagequeue) +- [Message传递](#message传递) +- [Handler内存泄漏](#handler内存泄漏) +- [Handler最佳实践](#handler最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## Handler原理 + +### Handler 作用 + +```java +// Handler:用于线程间通信 +// 主线程 → 子线程:通过 Handler 发送消息 +// 子线程 → 主线程:通过 Handler 更新 UI +``` + +### Handler 架构 + +``` +┌─────────┐ +│ Handler │ ←─── 发送和处理消息 +└────┬────┘ + │ +┌────▼────┐ +│ Looper │ ←─── 消息循环 +└────┬────┘ + │ +┌────▼────┐ +│MessageQueue│ ←─── 消息队列 +└────┬────┘ + │ +┌────▼────┐ +│ Message │ ←─── 消息对象 +└─────────┘ +``` + +--- + +## Looper机制 + +### Looper 作用 + +```java +// Looper:消息循环器 +// 从 MessageQueue 中取出消息,分发给 Handler 处理 + +// 主线程 Looper +Looper looper = Looper.getMainLooper(); + +// 子线程 Looper +class MyThread extends Thread { + @Override + public void run() { + Looper.prepare(); // 创建 Looper + Looper.loop(); // 开始循环 + } +} +``` + +### Looper 原理 + +```java +// Looper.prepare() +public static void prepare() { + if (sThreadLocal.get() != null) { + throw new RuntimeException("Only one Looper may be created per thread"); + } + sThreadLocal.set(new Looper()); +} + +// Looper.loop() +public static void loop() { + Looper me = myLooper(); + MessageQueue queue = me.mQueue; + for (;;) { + Message msg = queue.next(); // 阻塞等待消息 + if (msg == null) { + return; + } + msg.target.dispatchMessage(msg); // 分发消息 + msg.recycleUnchecked(); + } +} +``` + +--- + +## MessageQueue + +### MessageQueue 原理 + +```java +// MessageQueue:消息队列 +// 使用单链表存储消息 +// 按时间排序,时间早的在前面 + +// 消息入队 +boolean enqueueMessage(Message msg, long when) { + // 插入消息到队列 + // 按时间排序 +} + +// 消息出队 +Message next() { + // 取出消息 + // 如果队列为空,阻塞等待 +} +``` + +### 消息优先级 + +```java +// 消息按时间排序 +// when 值小的先执行 + +// 立即执行 +handler.sendMessage(msg); // when = 0 + +// 延迟执行 +handler.sendMessageDelayed(msg, 1000); // when = SystemClock.uptimeMillis() + 1000 +``` + +--- + +## Message传递 + +### 发送消息 + +```java +// 方式1:sendMessage +Message msg = Message.obtain(); +msg.what = 1; +msg.obj = "Data"; +handler.sendMessage(msg); + +// 方式2:sendMessageDelayed +handler.sendMessageDelayed(msg, 1000); + +// 方式3:post +handler.post(new Runnable() { + @Override + public void run() { + // 执行代码 + } +}); + +// 方式4:postDelayed +handler.postDelayed(runnable, 1000); +``` + +### 处理消息 + +```java +// Handler 处理消息 +public class MyHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case 1: + // 处理消息 + String data = (String) msg.obj; + break; + } + } +} + +// 或使用 Callback +Handler handler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + // 处理消息 + return true; // true 表示已处理,不再调用 handleMessage + } +}); +``` + +### 消息分发流程 + +```java +// dispatchMessage 流程 +public void dispatchMessage(Message msg) { + if (msg.callback != null) { + // 1. 优先执行 Runnable + handleCallback(msg); + } else { + if (mCallback != null) { + // 2. 执行 Callback + if (mCallback.handleMessage(msg)) { + return; + } + } + // 3. 执行 handleMessage + handleMessage(msg); + } +} +``` + +--- + +## Handler内存泄漏 + +### 泄漏原因 + +```java +// ❌ 问题:Handler 持有 Activity 引用 +public class MainActivity extends AppCompatActivity { + private Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + // Handler 持有 Activity 的隐式引用 + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + handler.postDelayed(new Runnable() { + @Override + public void run() { + // 如果 Activity 已销毁,但 Handler 还在处理消息 + } + }, 10000); + } +} +``` + +### 解决方案 + +#### 方案1:静态内部类 + WeakReference + +```java +// ✅ 解决方案1:静态内部类 + WeakReference +public class MainActivity extends AppCompatActivity { + private static class MyHandler extends Handler { + private WeakReference mActivity; + + MyHandler(MainActivity activity) { + mActivity = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + MainActivity activity = mActivity.get(); + if (activity == null || activity.isFinishing()) { + return; + } + // 处理消息 + } + } + + private MyHandler handler; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + handler = new MyHandler(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + handler.removeCallbacksAndMessages(null); + } +} +``` + +#### 方案2:使用主线程 Looper + +```java +// ✅ 解决方案2:使用主线程 Looper +Handler handler = new Handler(Looper.getMainLooper()); +``` + +#### 方案3:及时移除消息 + +```java +@Override +protected void onDestroy() { + super.onDestroy(); + handler.removeCallbacksAndMessages(null); +} +``` + +--- + +## Handler最佳实践 + +### 1. 避免内存泄漏 + +```java +// 使用静态内部类 + WeakReference +// 或使用主线程 Looper +``` + +### 2. 及时移除消息 + +```java +@Override +protected void onDestroy() { + super.onDestroy(); + handler.removeCallbacksAndMessages(null); +} +``` + +### 3. 使用 Message.obtain() + +```java +// ✅ 推荐:复用 Message 对象 +Message msg = Message.obtain(); + +// ❌ 不推荐:创建新对象 +Message msg = new Message(); +``` + +--- + +## 面试常见问题 + +### Q1: Handler 的原理? + +**答案:** +Handler → Looper → MessageQueue → Message +- Handler 发送消息到 MessageQueue +- Looper 从 MessageQueue 取出消息 +- Handler 处理消息 + +### Q2: Looper 的作用? + +**答案:** +- 消息循环器 +- 从 MessageQueue 取出消息 +- 分发给 Handler 处理 + +### Q3: Handler 为什么会导致内存泄漏? + +**答案:** +- Handler 持有 Activity 的隐式引用 +- Message 持有 Handler 的引用 +- MessageQueue 持有 Message 的引用 +- 如果 Handler 还有未处理的消息,Activity 无法被回收 + +### Q4: 如何避免 Handler 内存泄漏? + +**答案:** +1. 使用静态内部类 + WeakReference +2. 使用主线程 Looper +3. 及时移除消息(onDestroy 中) + +### Q5: Handler、Looper、MessageQueue 的关系? + +**答案:** +- **Handler**:发送和处理消息 +- **Looper**:消息循环,从 MessageQueue 取消息 +- **MessageQueue**:消息队列,存储消息 +- 一个线程只有一个 Looper,一个 Looper 只有一个 MessageQueue,可以有多个 Handler + +--- + +*最后更新:2024年* diff --git a/docs/android面试/多线程与并发/协程.md b/docs/android面试/多线程与并发/协程.md new file mode 100644 index 0000000..7a8e9ac --- /dev/null +++ b/docs/android面试/多线程与并发/协程.md @@ -0,0 +1,343 @@ +# 协程 + +## 目录 +- [协程概念](#协程概念) +- [协程与线程](#协程与线程) +- [协程作用域](#协程作用域) +- [协程上下文](#协程上下文) +- [协程取消](#协程取消) +- [协程异常处理](#协程异常处理) +- [协程最佳实践](#协程最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## 协程概念 + +### 什么是协程? + +```kotlin +// 协程:轻量级线程 +// 可以挂起和恢复 +// 比线程更轻量,开销更小 + +// 创建协程 +GlobalScope.launch { + delay(1000) // 挂起1秒 + println("Hello from coroutine") +} +``` + +### 协程特点 + +1. **轻量级**:比线程更轻量,可以创建大量协程 +2. **挂起恢复**:可以挂起和恢复执行 +3. **结构化并发**:自动管理生命周期 +4. **简化异步代码**:代码更简洁易读 + +--- + +## 协程与线程 + +### 协程 vs 线程 + +| 特性 | 协程 | 线程 | +|------|------|------| +| 创建开销 | 小 | 大 | +| 数量 | 可以创建大量 | 受系统限制 | +| 切换开销 | 小 | 大 | +| 阻塞 | 挂起(不阻塞线程) | 阻塞线程 | +| 适用场景 | 异步操作 | CPU 密集型任务 | + +### 协程优势 + +```kotlin +// 协程:挂起不阻塞线程 +suspend fun loadData() { + delay(1000) // 挂起,不阻塞线程 + // 其他协程可以继续执行 +} + +// 线程:阻塞线程 +Thread.sleep(1000) // 阻塞线程 +``` + +--- + +## 协程作用域 + +### 作用域类型 + +#### 1. GlobalScope + +```kotlin +// GlobalScope:全局作用域 +// 生命周期与应用一致 +GlobalScope.launch { + delay(1000) + println("Hello") +} +``` + +#### 2. CoroutineScope + +```kotlin +// CoroutineScope:自定义作用域 +val scope = CoroutineScope(Dispatchers.Main) +scope.launch { + delay(1000) + println("Hello") +} +scope.cancel() // 取消所有协程 +``` + +#### 3. lifecycleScope + +```kotlin +// lifecycleScope:与 LifecycleOwner 绑定 +class MainActivity : AppCompatActivity() { + fun loadData() { + lifecycleScope.launch { + val data = withContext(Dispatchers.IO) { + loadDataFromNetwork() + } + updateUI(data) + } + } +} +``` + +#### 4. viewModelScope + +```kotlin +// viewModelScope:与 ViewModel 绑定 +class MyViewModel : ViewModel() { + fun loadData() { + viewModelScope.launch { + val data = loadDataFromNetwork() + // 处理数据 + } + } +} +``` + +--- + +## 协程上下文 + +### Dispatchers + +```kotlin +// Dispatchers.Main:主线程 +launch(Dispatchers.Main) { + // 更新 UI +} + +// Dispatchers.IO:IO 线程 +launch(Dispatchers.IO) { + // 网络请求、文件操作 +} + +// Dispatchers.Default:默认线程池 +launch(Dispatchers.Default) { + // CPU 密集型任务 +} + +// Dispatchers.Unconfined:不指定线程 +launch(Dispatchers.Unconfined) { + // 在调用线程执行 +} +``` + +### 切换上下文 + +```kotlin +// 切换上下文 +launch(Dispatchers.Main) { + val data = withContext(Dispatchers.IO) { + // 在 IO 线程执行 + loadDataFromNetwork() + } + // 回到主线程 + updateUI(data) +} +``` + +--- + +## 协程取消 + +### 取消协程 + +```kotlin +// 取消协程 +val job = launch { + delay(10000) + println("Done") +} +job.cancel() // 取消协程 + +// 检查是否取消 +launch { + while (isActive) { + // 执行任务 + delay(100) + } +} +``` + +### 取消检查 + +```kotlin +// 检查取消状态 +suspend fun doWork() { + for (i in 1..1000) { + ensureActive() // 检查是否取消 + // 执行任务 + } +} +``` + +### 取消异常 + +```kotlin +// 取消时抛出 CancellationException +try { + delay(10000) +} catch (e: CancellationException) { + // 处理取消 + throw e // 必须重新抛出 +} +``` + +--- + +## 协程异常处理 + +### try-catch + +```kotlin +// 使用 try-catch +launch { + try { + val data = loadData() + } catch (e: Exception) { + // 处理异常 + } +} +``` + +### CoroutineExceptionHandler + +```kotlin +// 使用 CoroutineExceptionHandler +val handler = CoroutineExceptionHandler { _, exception -> + Log.e("Coroutine", "Exception: $exception") +} + +launch(handler) { + throw Exception("Error") +} +``` + +### SupervisorJob + +```kotlin +// SupervisorJob:子协程异常不影响其他协程 +val supervisor = SupervisorJob() +val scope = CoroutineScope(Dispatchers.Main + supervisor) + +scope.launch { + // 子协程1 +} + +scope.launch { + // 子协程2:异常不影响子协程1 + throw Exception("Error") +} +``` + +--- + +## 协程最佳实践 + +### 1. 使用合适的作用域 + +```kotlin +// ✅ 推荐:使用 lifecycleScope 或 viewModelScope +lifecycleScope.launch { + // 协程代码 +} + +// ❌ 不推荐:使用 GlobalScope +GlobalScope.launch { + // 可能导致内存泄漏 +} +``` + +### 2. 及时取消协程 + +```kotlin +// 在 onDestroy 中取消 +override fun onDestroy() { + super.onDestroy() + job.cancel() +} +``` + +### 3. 使用 suspend 函数 + +```kotlin +// ✅ 推荐:使用 suspend 函数 +suspend fun loadData(): String { + return withContext(Dispatchers.IO) { + // 网络请求 + } +} + +// ❌ 不推荐:在协程中直接使用回调 +``` + +--- + +## 面试常见问题 + +### Q1: 什么是协程? + +**答案:** +- 轻量级线程 +- 可以挂起和恢复 +- 比线程更轻量,开销更小 +- 简化异步代码 + +### Q2: 协程和线程的区别? + +**答案:** +- **协程**:轻量级,可以创建大量,挂起不阻塞线程 +- **线程**:重量级,受系统限制,阻塞线程 + +### Q3: 协程作用域? + +**答案:** +1. **GlobalScope**:全局作用域 +2. **CoroutineScope**:自定义作用域 +3. **lifecycleScope**:与 LifecycleOwner 绑定 +4. **viewModelScope**:与 ViewModel 绑定 + +### Q4: 如何取消协程? + +**答案:** +1. 调用 `job.cancel()` +2. 检查 `isActive` +3. 使用 `ensureActive()` + +### Q5: 协程异常处理? + +**答案:** +1. 使用 try-catch +2. 使用 CoroutineExceptionHandler +3. 使用 SupervisorJob + +--- + +*最后更新:2024年* diff --git a/docs/android面试/多线程与并发/并发编程.md b/docs/android面试/多线程与并发/并发编程.md new file mode 100644 index 0000000..eadf4e4 --- /dev/null +++ b/docs/android面试/多线程与并发/并发编程.md @@ -0,0 +1,343 @@ +# 并发编程 + +## 目录 +- [并发概念](#并发概念) +- [同步与异步](#同步与异步) +- [锁机制](#锁机制) +- [原子类](#原子类) +- [并发集合](#并发集合) +- [并发编程最佳实践](#并发编程最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## 并发概念 + +### 并发 vs 并行 + +```java +// 并发:同一时间段内多个任务交替执行 +// 单核 CPU:时间片轮转 + +// 并行:同一时刻多个任务同时执行 +// 多核 CPU:真正同时执行 +``` + +### 并发问题 + +```java +// 问题1:竞态条件 +private int count = 0; + +public void increment() { + count++; // 非原子操作 + // 1. 读取 count + // 2. count + 1 + // 3. 写入 count + // 多线程同时执行可能导致数据错误 +} + +// 问题2:可见性问题 +private boolean flag = false; + +// 线程1 +flag = true; + +// 线程2 +while (!flag) { + // 可能永远循环(可见性问题) +} +``` + +--- + +## 同步与异步 + +### 同步 + +```java +// 同步:按顺序执行,等待结果 +public void syncMethod() { + String result = loadData(); // 等待加载完成 + updateUI(result); +} +``` + +### 异步 + +```java +// 异步:不等待结果,继续执行 +public void asyncMethod() { + loadDataAsync(new Callback() { + @Override + public void onResult(String result) { + updateUI(result); + } + }); + // 继续执行其他代码 +} +``` + +--- + +## 锁机制 + +### synchronized + +```java +// 同步方法 +public synchronized void method() { + // 临界区代码 +} + +// 同步代码块 +public void method() { + synchronized (this) { + // 临界区代码 + } +} + +// 同步静态方法 +public static synchronized void staticMethod() { + // 临界区代码 +} +``` + +### ReentrantLock + +```java +// ReentrantLock:可重入锁 +private Lock lock = new ReentrantLock(); + +public void method() { + lock.lock(); + try { + // 临界区代码 + } finally { + lock.unlock(); + } +} + +// 可中断锁 +lock.lockInterruptibly(); +try { + // 临界区代码 +} finally { + lock.unlock(); +} +``` + +### ReadWriteLock + +```java +// ReadWriteLock:读写锁 +private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); +private Lock readLock = readWriteLock.readLock(); +private Lock writeLock = readWriteLock.writeLock(); + +// 读操作 +public String read() { + readLock.lock(); + try { + return data; + } finally { + readLock.unlock(); + } +} + +// 写操作 +public void write(String value) { + writeLock.lock(); + try { + data = value; + } finally { + writeLock.unlock(); + } +} +``` + +--- + +## 原子类 + +### AtomicInteger + +```java +// AtomicInteger:原子整数 +private AtomicInteger count = new AtomicInteger(0); + +// 原子操作 +count.incrementAndGet(); // 原子递增 +count.getAndIncrement(); // 先获取再递增 +count.addAndGet(10); // 原子加法 +``` + +### AtomicReference + +```java +// AtomicReference:原子引用 +private AtomicReference ref = new AtomicReference<>("initial"); + +// 原子更新 +ref.compareAndSet("initial", "new"); // CAS 操作 +``` + +### CAS 操作 + +```java +// CAS:Compare And Swap +// 比较并交换 +// 如果当前值等于期望值,则更新为新值 + +// 实现 +public boolean compareAndSet(int expect, int update) { + if (value == expect) { + value = update; + return true; + } + return false; +} +``` + +--- + +## 并发集合 + +### ConcurrentHashMap + +```java +// ConcurrentHashMap:线程安全的 HashMap +ConcurrentHashMap map = new ConcurrentHashMap<>(); +map.put("key", "value"); +String value = map.get("key"); + +// 特点: +// - 分段锁机制 +// - 支持并发读写 +// - 性能优于 Hashtable +``` + +### CopyOnWriteArrayList + +```java +// CopyOnWriteArrayList:线程安全的 ArrayList +CopyOnWriteArrayList list = new CopyOnWriteArrayList<>(); +list.add("item"); +String item = list.get(0); + +// 特点: +// - 写时复制 +// - 读操作无锁 +// - 适合读多写少场景 +``` + +### BlockingQueue + +```java +// BlockingQueue:阻塞队列 +BlockingQueue queue = new LinkedBlockingQueue<>(); + +// 生产者 +queue.put("item"); // 阻塞直到有空间 + +// 消费者 +String item = queue.take(); // 阻塞直到有元素 +``` + +--- + +## 并发编程最佳实践 + +### 1. 避免死锁 + +```java +// ❌ 问题:可能死锁 +synchronized (lock1) { + synchronized (lock2) { + // 代码 + } +} + +// 另一个线程 +synchronized (lock2) { + synchronized (lock1) { + // 代码 + } +} + +// ✅ 解决:按相同顺序获取锁 +synchronized (lock1) { + synchronized (lock2) { + // 代码 + } +} +``` + +### 2. 使用并发集合 + +```java +// ✅ 使用并发集合 +ConcurrentHashMap map = new ConcurrentHashMap<>(); + +// ❌ 使用同步包装 +Map map = Collections.synchronizedMap(new HashMap<>()); +``` + +### 3. 最小化锁范围 + +```java +// ✅ 最小化锁范围 +public void method() { + // 不需要同步的代码 + synchronized (this) { + // 需要同步的代码 + } + // 不需要同步的代码 +} + +// ❌ 扩大锁范围 +public synchronized void method() { + // 所有代码都在锁内 +} +``` + +--- + +## 面试常见问题 + +### Q1: 并发和并行的区别? + +**答案:** +- **并发**:同一时间段内多个任务交替执行 +- **并行**:同一时刻多个任务同时执行 + +### Q2: 并发问题有哪些? + +**答案:** +1. **竞态条件**:多线程同时修改共享数据 +2. **可见性问题**:线程修改数据其他线程看不到 +3. **死锁**:多个线程互相等待 + +### Q3: synchronized 和 Lock 的区别? + +**答案:** +- **synchronized**:JVM 层面,自动释放锁 +- **Lock**:API 层面,手动释放锁,更灵活 + +### Q4: CAS 操作? + +**答案:** +- Compare And Swap:比较并交换 +- 原子操作,无锁编程 +- 如果当前值等于期望值,则更新 + +### Q5: 并发集合有哪些? + +**答案:** +1. ConcurrentHashMap:线程安全的 HashMap +2. CopyOnWriteArrayList:线程安全的 ArrayList +3. BlockingQueue:阻塞队列 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/多线程与并发/线程基础.md b/docs/android面试/多线程与并发/线程基础.md new file mode 100644 index 0000000..564746a --- /dev/null +++ b/docs/android面试/多线程与并发/线程基础.md @@ -0,0 +1,345 @@ +# 线程基础 + +## 目录 +- [线程概念](#线程概念) +- [线程创建](#线程创建) +- [线程状态](#线程状态) +- [线程同步](#线程同步) +- [线程通信](#线程通信) +- [线程安全](#线程安全) +- [线程基础最佳实践](#线程基础最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## 线程概念 + +### 进程 vs 线程 + +```java +// 进程:程序执行的基本单位 +// - 有独立的内存空间 +// - 进程间通信复杂 + +// 线程:CPU 调度的基本单位 +// - 共享进程的内存空间 +// - 线程间通信简单 +``` + +### 主线程 vs 子线程 + +```java +// 主线程(UI 线程) +// - 负责 UI 更新 +// - 不能执行耗时操作 +// - 阻塞会导致 ANR + +// 子线程(工作线程) +// - 执行耗时操作 +// - 不能直接更新 UI +// - 需要通过 Handler 更新 UI +``` + +--- + +## 线程创建 + +### 方式1:继承 Thread + +```java +public class MyThread extends Thread { + @Override + public void run() { + // 线程执行代码 + System.out.println("Thread running"); + } +} + +// 使用 +MyThread thread = new MyThread(); +thread.start(); +``` + +### 方式2:实现 Runnable + +```java +public class MyRunnable implements Runnable { + @Override + public void run() { + // 线程执行代码 + System.out.println("Thread running"); + } +} + +// 使用 +Thread thread = new Thread(new MyRunnable()); +thread.start(); +``` + +### 方式3:Lambda 表达式 + +```java +Thread thread = new Thread(() -> { + System.out.println("Thread running"); +}); +thread.start(); +``` + +--- + +## 线程状态 + +### 线程状态转换 + +``` +NEW → RUNNABLE → BLOCKED → WAITING → TIMED_WAITING → TERMINATED +``` + +### 状态说明 + +```java +// NEW:新建状态 +Thread thread = new Thread(); + +// RUNNABLE:可运行状态 +thread.start(); + +// BLOCKED:阻塞状态(等待锁) +synchronized (lock) { + // 其他线程等待锁 +} + +// WAITING:等待状态 +thread.wait(); +LockSupport.park(); + +// TIMED_WAITING:超时等待 +Thread.sleep(1000); +thread.wait(1000); + +// TERMINATED:终止状态 +// 线程执行完毕 +``` + +--- + +## 线程同步 + +### synchronized + +```java +// 同步方法 +public synchronized void method() { + // 临界区代码 +} + +// 同步代码块 +public void method() { + synchronized (this) { + // 临界区代码 + } +} + +// 同步静态方法 +public static synchronized void staticMethod() { + // 临界区代码 +} +``` + +### Lock + +```java +// ReentrantLock +private Lock lock = new ReentrantLock(); + +public void method() { + lock.lock(); + try { + // 临界区代码 + } finally { + lock.unlock(); + } +} + +// ReadWriteLock +private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); +private Lock readLock = readWriteLock.readLock(); +private Lock writeLock = readWriteLock.writeLock(); +``` + +### volatile + +```java +// volatile:保证可见性,不保证原子性 +private volatile boolean flag = false; + +// 适用场景 +// 1. 状态标志 +// 2. 双重检查锁定 +``` + +--- + +## 线程通信 + +### wait/notify + +```java +// wait:等待 +synchronized (lock) { + while (!condition) { + lock.wait(); // 释放锁,等待通知 + } +} + +// notify:通知 +synchronized (lock) { + condition = true; + lock.notify(); // 唤醒一个等待线程 + // lock.notifyAll(); // 唤醒所有等待线程 +} +``` + +### CountDownLatch + +```java +// CountDownLatch:等待多个线程完成 +CountDownLatch latch = new CountDownLatch(3); + +// 线程中 +latch.countDown(); + +// 主线程等待 +latch.await(); +``` + +### CyclicBarrier + +```java +// CyclicBarrier:多个线程等待到齐 +CyclicBarrier barrier = new CyclicBarrier(3); + +// 线程中 +barrier.await(); +``` + +--- + +## 线程安全 + +### 线程安全问题 + +```java +// ❌ 问题:非线程安全 +private int count = 0; + +public void increment() { + count++; // 非原子操作 +} + +// ✅ 解决:使用同步 +private int count = 0; + +public synchronized void increment() { + count++; +} +``` + +### 线程安全集合 + +```java +// ConcurrentHashMap:线程安全的 HashMap +ConcurrentHashMap map = new ConcurrentHashMap<>(); + +// CopyOnWriteArrayList:线程安全的 ArrayList +CopyOnWriteArrayList list = new CopyOnWriteArrayList<>(); + +// BlockingQueue:阻塞队列 +BlockingQueue queue = new LinkedBlockingQueue<>(); +``` + +--- + +## 线程基础最佳实践 + +### 1. 避免在 UI 线程执行耗时操作 + +```java +// ❌ 错误 +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + loadDataFromNetwork(); // 阻塞 UI 线程 +} + +// ✅ 正确 +new Thread(() -> { + loadDataFromNetwork(); + runOnUiThread(() -> { + updateUI(); + }); +}).start(); +``` + +### 2. 使用线程池 + +```java +// 使用线程池管理线程 +ExecutorService executor = Executors.newCachedThreadPool(); +executor.execute(() -> { + // 执行任务 +}); +``` + +### 3. 及时释放资源 + +```java +@Override +protected void onDestroy() { + super.onDestroy(); + // 停止线程 + if (thread != null) { + thread.interrupt(); + } +} +``` + +--- + +## 面试常见问题 + +### Q1: 线程和进程的区别? + +**答案:** +- **进程**:程序执行的基本单位,有独立内存空间 +- **线程**:CPU 调度的基本单位,共享进程内存空间 + +### Q2: 如何创建线程? + +**答案:** +1. 继承 Thread +2. 实现 Runnable +3. 使用 Lambda 表达式 + +### Q3: 线程状态? + +**答案:** +NEW → RUNNABLE → BLOCKED → WAITING → TIMED_WAITING → TERMINATED + +### Q4: 线程同步方式? + +**答案:** +1. synchronized +2. Lock +3. volatile +4. 原子类 + +### Q5: wait 和 sleep 的区别? + +**答案:** +- **wait**:释放锁,需要 notify 唤醒 +- **sleep**:不释放锁,时间到自动唤醒 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/多线程与并发/线程池.md b/docs/android面试/多线程与并发/线程池.md new file mode 100644 index 0000000..0f40c6c --- /dev/null +++ b/docs/android面试/多线程与并发/线程池.md @@ -0,0 +1,364 @@ +# 线程池 + +## 目录 +- [线程池原理](#线程池原理) +- [线程池类型](#线程池类型) +- [线程池参数](#线程池参数) +- [线程池执行流程](#线程池执行流程) +- [线程池监控](#线程池监控) +- [线程池最佳实践](#线程池最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## 线程池原理 + +### 为什么使用线程池? + +```java +// ❌ 问题:频繁创建线程 +for (int i = 0; i < 100; i++) { + new Thread(() -> { + // 执行任务 + }).start(); // 创建和销毁线程开销大 +} + +// ✅ 解决:使用线程池 +ExecutorService executor = Executors.newCachedThreadPool(); +for (int i = 0; i < 100; i++) { + executor.execute(() -> { + // 执行任务 + }); +} +``` + +### 线程池优势 + +1. **降低资源消耗**:复用线程,减少创建和销毁开销 +2. **提高响应速度**:任务到达时直接执行 +3. **提高线程可管理性**:统一管理线程 +4. **控制并发数**:限制同时执行的线程数 + +--- + +## 线程池类型 + +### 1. FixedThreadPool + +```java +// 固定大小的线程池 +ExecutorService executor = Executors.newFixedThreadPool(5); + +// 特点: +// - 核心线程数 = 最大线程数 = 5 +// - 无界队列 +// - 适合执行长期任务 +``` + +### 2. CachedThreadPool + +```java +// 缓存线程池 +ExecutorService executor = Executors.newCachedThreadPool(); + +// 特点: +// - 核心线程数 = 0 +// - 最大线程数 = Integer.MAX_VALUE +// - 适合执行短期任务 +``` + +### 3. SingleThreadExecutor + +```java +// 单线程线程池 +ExecutorService executor = Executors.newSingleThreadExecutor(); + +// 特点: +// - 核心线程数 = 最大线程数 = 1 +// - 无界队列 +// - 任务按顺序执行 +``` + +### 4. ScheduledThreadPool + +```java +// 定时任务线程池 +ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); + +// 延迟执行 +executor.schedule(() -> { + // 执行任务 +}, 1, TimeUnit.SECONDS); + +// 周期性执行 +executor.scheduleAtFixedRate(() -> { + // 执行任务 +}, 0, 1, TimeUnit.SECONDS); +``` + +--- + +## 线程池参数 + +### ThreadPoolExecutor 参数 + +```java +ThreadPoolExecutor executor = new ThreadPoolExecutor( + corePoolSize, // 核心线程数 + maximumPoolSize, // 最大线程数 + keepAliveTime, // 空闲线程存活时间 + unit, // 时间单位 + workQueue, // 工作队列 + threadFactory, // 线程工厂 + handler // 拒绝策略 +); +``` + +### 参数说明 + +#### 1. corePoolSize(核心线程数) + +```java +// 核心线程数:即使空闲也保留的线程数 +int corePoolSize = 5; +``` + +#### 2. maximumPoolSize(最大线程数) + +```java +// 最大线程数:允许的最大线程数 +int maximumPoolSize = 10; +``` + +#### 3. keepAliveTime(空闲线程存活时间) + +```java +// 空闲线程存活时间:超过核心线程数的线程空闲多久后回收 +long keepAliveTime = 60L; +TimeUnit unit = TimeUnit.SECONDS; +``` + +#### 4. workQueue(工作队列) + +```java +// 工作队列类型 +BlockingQueue workQueue; + +// ArrayBlockingQueue:有界队列 +workQueue = new ArrayBlockingQueue<>(100); + +// LinkedBlockingQueue:无界队列 +workQueue = new LinkedBlockingQueue<>(); + +// SynchronousQueue:同步队列 +workQueue = new SynchronousQueue<>(); +``` + +#### 5. threadFactory(线程工厂) + +```java +// 自定义线程工厂 +ThreadFactory threadFactory = new ThreadFactory() { + private int count = 0; + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setName("MyThread-" + count++); + return thread; + } +}; +``` + +#### 6. handler(拒绝策略) + +```java +// 拒绝策略类型 +RejectedExecutionHandler handler; + +// AbortPolicy:抛出异常(默认) +handler = new ThreadPoolExecutor.AbortPolicy(); + +// CallerRunsPolicy:调用者执行 +handler = new ThreadPoolExecutor.CallerRunsPolicy(); + +// DiscardPolicy:丢弃任务 +handler = new ThreadPoolExecutor.DiscardPolicy(); + +// DiscardOldestPolicy:丢弃最老任务 +handler = new ThreadPoolExecutor.DiscardOldestPolicy(); +``` + +--- + +## 线程池执行流程 + +### 执行流程 + +``` +1. 提交任务 +2. 如果线程数 < corePoolSize,创建核心线程执行 +3. 如果线程数 >= corePoolSize,将任务加入队列 +4. 如果队列已满且线程数 < maximumPoolSize,创建新线程执行 +5. 如果队列已满且线程数 >= maximumPoolSize,执行拒绝策略 +``` + +### 流程图 + +``` +任务提交 + ↓ +线程数 < corePoolSize? + ├─ 是 → 创建核心线程执行 + └─ 否 → 队列未满? + ├─ 是 → 加入队列 + └─ 否 → 线程数 < maximumPoolSize? + ├─ 是 → 创建线程执行 + └─ 否 → 执行拒绝策略 +``` + +--- + +## 线程池监控 + +### 监控指标 + +```java +ThreadPoolExecutor executor = ...; + +// 当前线程数 +int poolSize = executor.getPoolSize(); + +// 活跃线程数 +int activeCount = executor.getActiveCount(); + +// 已完成任务数 +long completedTaskCount = executor.getCompletedTaskCount(); + +// 总任务数 +long taskCount = executor.getTaskCount(); + +// 队列大小 +int queueSize = executor.getQueue().size(); +``` + +### 自定义监控 + +```java +public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor { + @Override + protected void beforeExecute(Thread t, Runnable r) { + super.beforeExecute(t, r); + // 任务执行前 + Log.d("ThreadPool", "Task started: " + r); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + // 任务执行后 + Log.d("ThreadPool", "Task completed: " + r); + } + + @Override + protected void terminated() { + super.terminated(); + // 线程池终止 + Log.d("ThreadPool", "ThreadPool terminated"); + } +} +``` + +--- + +## 线程池最佳实践 + +### 1. 合理设置参数 + +```java +// ✅ 好的配置 +int corePoolSize = Runtime.getRuntime().availableProcessors(); +int maximumPoolSize = corePoolSize * 2; +ThreadPoolExecutor executor = new ThreadPoolExecutor( + corePoolSize, + maximumPoolSize, + 60L, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(100), + new ThreadPoolExecutor.CallerRunsPolicy() +); + +// ❌ 不好的配置 +ExecutorService executor = Executors.newCachedThreadPool(); +// 可能创建过多线程 +``` + +### 2. 使用有界队列 + +```java +// ✅ 使用有界队列 +BlockingQueue queue = new ArrayBlockingQueue<>(100); + +// ❌ 使用无界队列可能导致内存溢出 +BlockingQueue queue = new LinkedBlockingQueue<>(); +``` + +### 3. 设置拒绝策略 + +```java +// 设置合适的拒绝策略 +executor.setRejectedExecutionHandler( + new ThreadPoolExecutor.CallerRunsPolicy() +); +``` + +### 4. 及时关闭线程池 + +```java +// 关闭线程池 +executor.shutdown(); // 等待任务完成 +// 或 +executor.shutdownNow(); // 立即关闭 +``` + +--- + +## 面试常见问题 + +### Q1: 为什么使用线程池? + +**答案:** +1. 降低资源消耗:复用线程 +2. 提高响应速度:任务到达直接执行 +3. 提高线程可管理性:统一管理 +4. 控制并发数:限制线程数 + +### Q2: 线程池的参数? + +**答案:** +1. corePoolSize:核心线程数 +2. maximumPoolSize:最大线程数 +3. keepAliveTime:空闲线程存活时间 +4. workQueue:工作队列 +5. threadFactory:线程工厂 +6. handler:拒绝策略 + +### Q3: 线程池执行流程? + +**答案:** +1. 线程数 < corePoolSize:创建核心线程 +2. 线程数 >= corePoolSize:加入队列 +3. 队列满且线程数 < maximumPoolSize:创建新线程 +4. 队列满且线程数 >= maximumPoolSize:执行拒绝策略 + +### Q4: 拒绝策略有哪些? + +**答案:** +1. AbortPolicy:抛出异常 +2. CallerRunsPolicy:调用者执行 +3. DiscardPolicy:丢弃任务 +4. DiscardOldestPolicy:丢弃最老任务 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/ButterKnife原理.md b/docs/android面试/开源框架/ButterKnife原理.md new file mode 100644 index 0000000..98103bd --- /dev/null +++ b/docs/android面试/开源框架/ButterKnife原理.md @@ -0,0 +1,153 @@ +# ButterKnife原理 + +## 目录 +- [ButterKnife原理](#butterknife原理) +- [注解处理](#注解处理) +- [代码生成](#代码生成) +- [ButterKnife源码分析](#butterknife源码分析) +- [ButterKnife最佳实践](#butterknife最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## ButterKnife原理 + +### 定义 + +```java +// ButterKnife:视图绑定库 +// 使用注解减少 findViewById +// 编译时生成代码 +``` + +### 使用方式 + +```java +public class MainActivity extends AppCompatActivity { + @BindView(R.id.textView) + TextView textView; + + @OnClick(R.id.button) + void onButtonClick() { + // 处理点击 + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + } +} +``` + +--- + +## 注解处理 + +### 注解类型 + +```java +// @BindView:绑定 View +@BindView(R.id.textView) +TextView textView; + +// @OnClick:点击事件 +@OnClick(R.id.button) +void onButtonClick() {} + +// @BindViews:绑定多个 View +@BindViews({R.id.view1, R.id.view2}) +List views; +``` + +### 编译时处理 + +```java +// 编译时,注解处理器扫描注解 +// 生成绑定代码 +// 运行时调用生成的代码 +``` + +--- + +## 代码生成 + +### 生成的代码 + +```java +// 编译时生成 MainActivity_ViewBinding +public class MainActivity_ViewBinding implements Unbinder { + private MainActivity target; + + public MainActivity_ViewBinding(MainActivity target, View source) { + this.target = target; + target.textView = source.findViewById(R.id.textView); + } +} +``` + +--- + +## ButterKnife源码分析 + +### 关键类 + +```java +// ButterKnife:入口类 +// ViewBinding:视图绑定 +// Unbinder:解绑接口 +``` + +--- + +## ButterKnife最佳实践 + +### 1. 及时解绑 + +```java +private Unbinder unbinder; + +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + unbinder = ButterKnife.bind(this); +} + +@Override +protected void onDestroy() { + super.onDestroy(); + if (unbinder != null) { + unbinder.unbind(); + } +} +``` + +--- + +## 面试常见问题 + +### Q1: ButterKnife 的原理? + +**答案:** +- 使用注解标记 View +- 编译时生成绑定代码 +- 运行时调用生成的代码 + +### Q2: ButterKnife 的优缺点? + +**答案:** +- **优点**:减少 findViewById,代码简洁 +- **缺点**:需要编译时处理,已停止维护 + +### Q3: ButterKnife 的替代方案? + +**答案:** +- ViewBinding(官方推荐) +- DataBinding +- Kotlin 扩展 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/Dagger2原理.md b/docs/android面试/开源框架/Dagger2原理.md new file mode 100644 index 0000000..c7cdc5a --- /dev/null +++ b/docs/android面试/开源框架/Dagger2原理.md @@ -0,0 +1,195 @@ +# Dagger2原理 + +## 目录 +- [Dagger2原理](#dagger2原理) +- [依赖注入](#依赖注入) +- [注解处理](#注解处理) +- [组件与模块](#组件与模块) +- [Dagger2源码分析](#dagger2源码分析) +- [Dagger2最佳实践](#dagger2最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## Dagger2原理 + +### 定义 + +```java +// Dagger2:依赖注入框架 +// 编译时生成代码 +// 类型安全 +// 性能好 +``` + +### 核心概念 + +```java +// 1. Component:组件,注入器 +// 2. Module:模块,提供依赖 +// 3. Inject:注入标记 +// 4. Provides:提供方法 +``` + +--- + +## 依赖注入 + +### 依赖注入方式 + +```java +// 1. 构造函数注入 +public class UserRepository { + private ApiService apiService; + + @Inject + public UserRepository(ApiService apiService) { + this.apiService = apiService; + } +} + +// 2. 字段注入 +public class MainActivity extends AppCompatActivity { + @Inject + UserRepository userRepository; +} + +// 3. 方法注入 +@Inject +void setUserRepository(UserRepository userRepository) { + this.userRepository = userRepository; +} +``` + +--- + +## 注解处理 + +### 注解类型 + +```java +// @Inject:标记需要注入的依赖 +@Inject +UserRepository userRepository; + +// @Module:提供依赖的模块 +@Module +public class AppModule { + @Provides + ApiService provideApiService() { + return new ApiService(); + } +} + +// @Component:注入器 +@Component(modules = {AppModule.class}) +public interface AppComponent { + void inject(MainActivity activity); +} +``` + +--- + +## 组件与模块 + +### Module + +```java +@Module +public class AppModule { + @Provides + @Singleton + ApiService provideApiService() { + return new ApiService(); + } +} +``` + +### Component + +```java +@Component(modules = {AppModule.class}) +public interface AppComponent { + void inject(MainActivity activity); +} + +// 使用 +AppComponent component = DaggerAppComponent.builder() + .appModule(new AppModule()) + .build(); +component.inject(this); +``` + +--- + +## Dagger2源码分析 + +### 关键类 + +```java +// Component:组件接口 +// Module:模块类 +// Inject:注入注解 +// DaggerXXX:生成的组件实现 +``` + +### 编译时生成 + +```java +// 编译时生成 DaggerAppComponent +// 实现 AppComponent 接口 +// 提供依赖注入逻辑 +``` + +--- + +## Dagger2最佳实践 + +### 1. 使用单例 + +```java +@Provides +@Singleton +ApiService provideApiService() { + return new ApiService(); +} +``` + +### 2. 模块化设计 + +```java +// 按功能划分模块 +@Module +public class NetworkModule { } + +@Module +public class DatabaseModule { } +``` + +--- + +## 面试常见问题 + +### Q1: Dagger2 的原理? + +**答案:** +- 编译时生成代码 +- 实现依赖注入 +- 类型安全,性能好 + +### Q2: Dagger2 和 Hilt 的区别? + +**答案:** +- **Dagger2**:基础框架,需要手动配置 +- **Hilt**:Dagger2 的封装,更易用,官方推荐 + +### Q3: 依赖注入的优势? + +**答案:** +1. 解耦:降低耦合度 +2. 测试:易于测试 +3. 复用:代码复用 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/EventBus原理.md b/docs/android面试/开源框架/EventBus原理.md new file mode 100644 index 0000000..1ff5236 --- /dev/null +++ b/docs/android面试/开源框架/EventBus原理.md @@ -0,0 +1,160 @@ +# EventBus原理 + +## 目录 +- [EventBus原理](#eventbus原理) +- [事件总线](#事件总线) +- [订阅机制](#订阅机制) +- [线程模式](#线程模式) +- [EventBus源码分析](#eventbus源码分析) +- [EventBus最佳实践](#eventbus最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## EventBus原理 + +### 定义 + +```java +// EventBus:事件总线 +// 基于观察者模式 +// 组件间解耦通信 +``` + +### 核心概念 + +```java +// 1. Event:事件 +// 2. Subscriber:订阅者 +// 3. Publisher:发布者 +``` + +--- + +## 事件总线 + +### 架构 + +```java +// EventBus 使用单例模式 +// 维护订阅者列表 +// 事件发布时,通知所有订阅者 +``` + +--- + +## 订阅机制 + +### 订阅事件 + +```java +// 注册订阅者 +EventBus.getDefault().register(this); + +// 订阅方法 +@Subscribe +public void onEvent(MessageEvent event) { + // 处理事件 +} + +// 注销订阅者 +EventBus.getDefault().unregister(this); +``` + +### 发布事件 + +```java +// 发布事件 +EventBus.getDefault().post(new MessageEvent("Hello")); +``` + +--- + +## 线程模式 + +### 线程模式类型 + +```java +// 1. POSTING:发布线程(默认) +@Subscribe(threadMode = ThreadMode.POSTING) + +// 2. MAIN:主线程 +@Subscribe(threadMode = ThreadMode.MAIN) + +// 3. BACKGROUND:后台线程 +@Subscribe(threadMode = ThreadMode.BACKGROUND) + +// 4. ASYNC:异步线程 +@Subscribe(threadMode = ThreadMode.ASYNC) +``` + +--- + +## EventBus源码分析 + +### 关键类 + +```java +// EventBus:事件总线 +// SubscriberMethodFinder:订阅方法查找 +// Subscription:订阅信息 +``` + +### 关键流程 + +```java +// 1. register():注册订阅者 +// 2. findSubscriberMethods():查找订阅方法 +// 3. post():发布事件 +// 4. postToSubscription():通知订阅者 +``` + +--- + +## EventBus最佳实践 + +### 1. 及时注销 + +```java +@Override +protected void onDestroy() { + super.onDestroy(); + EventBus.getDefault().unregister(this); +} +``` + +### 2. 使用合适的线程模式 + +```java +// 根据需求选择线程模式 +@Subscribe(threadMode = ThreadMode.MAIN) +``` + +--- + +## 面试常见问题 + +### Q1: EventBus 的原理? + +**答案:** +- 基于观察者模式 +- 维护订阅者列表 +- 事件发布时通知所有订阅者 + +### Q2: EventBus 的线程模式? + +**答案:** +1. POSTING:发布线程 +2. MAIN:主线程 +3. BACKGROUND:后台线程 +4. ASYNC:异步线程 + +### Q3: EventBus 的优缺点? + +**答案:** +- **优点**:解耦、易用 +- **缺点**:可能内存泄漏、难以追踪 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/Glide原理.md b/docs/android面试/开源框架/Glide原理.md new file mode 100644 index 0000000..44daeb7 --- /dev/null +++ b/docs/android面试/开源框架/Glide原理.md @@ -0,0 +1,173 @@ +# Glide原理 + +## 目录 +- [Glide架构](#glide架构) +- [图片加载流程](#图片加载流程) +- [缓存机制](#缓存机制) +- [内存管理](#内存管理) +- [Glide源码分析](#glide源码分析) +- [Glide最佳实践](#glide最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## Glide架构 + +### 架构图 + +``` +Request → Engine → DecodeJob → DataFetcher → ModelLoader + ↓ ↓ ↓ ↓ + Memory Disk Network Resource + Cache Cache Cache +``` + +### 核心组件 + +```java +// 1. RequestManager:请求管理 +// 2. Engine:引擎,协调加载 +// 3. DecodeJob:解码任务 +// 4. ModelLoader:模型加载器 +// 5. ResourceCache:资源缓存 +``` + +--- + +## 图片加载流程 + +### 加载流程 + +``` +1. Glide.with().load().into() +2. RequestManager 创建 Request +3. Engine 检查缓存 +4. 缓存未命中,创建 DecodeJob +5. 从网络/磁盘加载 +6. 解码图片 +7. 转换和缓存 +8. 显示到 ImageView +``` + +### 代码示例 + +```java +Glide.with(context) + .load(url) + .placeholder(R.drawable.placeholder) + .error(R.drawable.error) + .override(200, 200) + .into(imageView); +``` + +--- + +## 缓存机制 + +### 三级缓存 + +```java +// 1. 活动资源缓存(Active Resources) +// 2. 内存缓存(Memory Cache) +// 3. 磁盘缓存(Disk Cache) +``` + +### 缓存策略 + +```java +// 默认策略:ALL +// - 原始图片缓存到磁盘 +// - 转换后图片缓存到内存和磁盘 + +Glide.with(context) + .load(url) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(imageView); +``` + +--- + +## 内存管理 + +### 内存优化 + +```java +// 1. 自动管理 Bitmap 内存 +// 2. 根据 ImageView 大小加载 +// 3. 及时释放资源 +``` + +### 内存缓存 + +```java +// LRU 缓存 +// 最近最少使用的图片会被回收 +``` + +--- + +## Glide源码分析 + +### 关键类 + +```java +// Glide:入口类 +// RequestManager:请求管理 +// Engine:引擎 +// DecodeJob:解码任务 +// BitmapPool:Bitmap 对象池 +``` + +--- + +## Glide最佳实践 + +### 1. 使用合适的缓存策略 + +```java +// 根据场景选择缓存策略 +.diskCacheStrategy(DiskCacheStrategy.ALL) // 缓存所有 +.diskCacheStrategy(DiskCacheStrategy.NONE) // 不缓存 +``` + +### 2. 设置图片大小 + +```java +// 根据 ImageView 大小加载 +.override(200, 200) +``` + +### 3. 处理加载失败 + +```java +.error(R.drawable.error) +.fallback(R.drawable.fallback) +``` + +--- + +## 面试常见问题 + +### Q1: Glide 的缓存机制? + +**答案:** +- 三级缓存:活动资源、内存、磁盘 +- LRU 算法管理缓存 +- 自动管理内存 + +### Q2: Glide 和 Picasso 的区别? + +**答案:** +- **Glide**:功能更丰富,内存管理更好 +- **Picasso**:更轻量,API 更简单 + +### Q3: Glide 如何避免内存泄漏? + +**答案:** +- 使用生命周期感知 +- 自动取消请求 +- 及时释放资源 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/Jetpack组件.md b/docs/android面试/开源框架/Jetpack组件.md new file mode 100644 index 0000000..86f4205 --- /dev/null +++ b/docs/android面试/开源框架/Jetpack组件.md @@ -0,0 +1,254 @@ +# Jetpack组件 + +## 目录 +- [Lifecycle](#lifecycle) +- [ViewModel](#viewmodel) +- [LiveData](#livedata) +- [Room](#room) +- [Navigation](#navigation) +- [WorkManager](#workmanager) +- [Paging](#paging) +- [DataBinding](#databinding) +- [Jetpack最佳实践](#jetpack最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## Lifecycle + +### Lifecycle 简介 + +```java +// Lifecycle:生命周期感知组件 +// 自动管理生命周期相关的操作 +``` + +### 使用方式 + +```java +// 实现 LifecycleObserver +public class MyObserver implements LifecycleObserver { + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + public void onResume() { + // Activity onResume 时调用 + } +} + +// 注册观察者 +lifecycle.addObserver(new MyObserver()); +``` + +--- + +## ViewModel + +### ViewModel 简介 + +```java +// ViewModel:保存 UI 相关数据 +// 在配置变更时保持数据 +``` + +### 使用方式 + +```java +public class MyViewModel extends ViewModel { + private MutableLiveData data = new MutableLiveData<>(); + + public LiveData getData() { + return data; + } + + public void setData(String value) { + data.setValue(value); + } +} + +// Activity 中使用 +MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class); +viewModel.getData().observe(this, data -> { + // 更新 UI +}); +``` + +--- + +## LiveData + +### LiveData 简介 + +```java +// LiveData:可观察的数据持有者 +// 自动更新 UI +// 生命周期感知 +``` + +### 使用方式 + +```java +MutableLiveData liveData = new MutableLiveData<>(); +liveData.observe(this, new Observer() { + @Override + public void onChanged(String s) { + // 数据变化时自动更新 + } +}); + +liveData.setValue("New Value"); +``` + +--- + +## Room + +### Room 简介 + +```java +// Room:SQLite 数据库抽象层 +// 编译时检查 +// 支持 LiveData、RxJava +``` + +### 使用方式 + +```java +@Database(entities = {User.class}, version = 1) +public abstract class AppDatabase extends RoomDatabase { + public abstract UserDao userDao(); +} + +@Dao +public interface UserDao { + @Query("SELECT * FROM user") + LiveData> getAllUsers(); +} +``` + +--- + +## Navigation + +### Navigation 简介 + +```java +// Navigation:导航组件 +// 简化 Fragment 导航 +``` + +### 使用方式 + +```java +// 导航到目标 +Navigation.findNavController(view) + .navigate(R.id.action_fragmentA_to_fragmentB); +``` + +--- + +## WorkManager + +### WorkManager 简介 + +```java +// WorkManager:后台任务管理 +// 保证任务执行 +// 支持约束条件 +``` + +### 使用方式 + +```java +WorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class) + .setConstraints(new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build()) + .build(); + +WorkManager.getInstance(context).enqueue(workRequest); +``` + +--- + +## Paging + +### Paging 简介 + +```java +// Paging:分页加载数据 +// 自动处理分页逻辑 +``` + +--- + +## DataBinding + +### DataBinding 简介 + +```java +// DataBinding:数据绑定 +// 减少 findViewById +// 自动更新 UI +``` + +### 使用方式 + +```xml + + + + + + +``` + +--- + +## Jetpack最佳实践 + +### 1. 使用 ViewModel 保存数据 + +```java +// 配置变更时保持数据 +``` + +### 2. 使用 LiveData 更新 UI + +```java +// 自动更新,生命周期感知 +``` + +### 3. 使用 Room 管理数据 + +```java +// 类型安全,编译时检查 +``` + +--- + +## 面试常见问题 + +### Q1: ViewModel 的作用? + +**答案:** +- 保存 UI 相关数据 +- 在配置变更时保持数据 +- 与 Activity/Fragment 生命周期分离 + +### Q2: LiveData 的特点? + +**答案:** +- 可观察 +- 生命周期感知 +- 自动更新 UI + +### Q3: Room 的优势? + +**答案:** +- 编译时检查 +- 类型安全 +- 支持 LiveData、RxJava + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/Picasso原理.md b/docs/android面试/开源框架/Picasso原理.md new file mode 100644 index 0000000..ab148e0 --- /dev/null +++ b/docs/android面试/开源框架/Picasso原理.md @@ -0,0 +1,128 @@ +# Picasso原理 + +## 目录 +- [Picasso架构](#picasso架构) +- [图片加载流程](#图片加载流程) +- [缓存机制](#缓存机制) +- [Picasso源码分析](#picasso源码分析) +- [Picasso最佳实践](#picasso最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## Picasso架构 + +### 架构图 + +``` +Request → Dispatcher → BitmapHunter → NetworkRequest + ↓ ↓ ↓ + Memory Disk Bitmap + Cache Cache Decode +``` + +### 核心组件 + +```java +// 1. Picasso:入口类 +// 2. RequestCreator:请求创建器 +// 3. Dispatcher:调度器 +// 4. BitmapHunter:图片加载器 +// 5. Cache:缓存 +``` + +--- + +## 图片加载流程 + +### 加载流程 + +``` +1. Picasso.with().load().into() +2. 检查内存缓存 +3. 检查磁盘缓存 +4. 从网络加载 +5. 解码图片 +6. 缓存图片 +7. 显示到 ImageView +``` + +### 代码示例 + +```java +Picasso.get() + .load(url) + .placeholder(R.drawable.placeholder) + .error(R.drawable.error) + .resize(200, 200) + .into(imageView); +``` + +--- + +## 缓存机制 + +### 二级缓存 + +```java +// 1. 内存缓存:LruCache +// 2. 磁盘缓存:OkHttp 缓存 +``` + +### 缓存策略 + +```java +// 默认缓存策略 +// - 内存缓存:自动管理 +// - 磁盘缓存:使用 OkHttp 缓存 +``` + +--- + +## Picasso源码分析 + +### 关键类 + +```java +// Picasso:入口类 +// RequestCreator:请求创建 +// Dispatcher:任务调度 +// BitmapHunter:图片加载 +``` + +--- + +## Picasso最佳实践 + +### 1. 设置图片大小 + +```java +// 根据 ImageView 大小加载 +.resize(200, 200) +``` + +### 2. 处理加载失败 + +```java +.error(R.drawable.error) +``` + +--- + +## 面试常见问题 + +### Q1: Picasso 和 Glide 的区别? + +**答案:** +- **Picasso**:更轻量,API 简单,功能基础 +- **Glide**:功能丰富,内存管理更好,支持 GIF + +### Q2: Picasso 的缓存机制? + +**答案:** +- 二级缓存:内存缓存、磁盘缓存 +- 使用 LruCache 管理内存缓存 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/开源框架/RxJava原理.md b/docs/android面试/开源框架/RxJava原理.md new file mode 100644 index 0000000..8e6c404 --- /dev/null +++ b/docs/android面试/开源框架/RxJava原理.md @@ -0,0 +1,185 @@ +# RxJava原理 + +## 目录 +- [RxJava概念](#rxjava概念) +- [观察者模式](#观察者模式) +- [操作符](#操作符) +- [线程调度](#线程调度) +- [背压](#背压) +- [RxJava源码分析](#rxjava源码分析) +- [RxJava最佳实践](#rxjava最佳实践) +- [面试常见问题](#面试常见问题) + +--- + +## RxJava概念 + +### 定义 + +```java +// RxJava:响应式编程库 +// - 基于观察者模式 +// - 支持异步操作 +// - 链式调用 +``` + +### 核心类 + +```java +// Observable:被观察者 +// Observer:观察者 +// Subscriber:订阅者 +// Scheduler:调度器 +``` + +--- + +## 观察者模式 + +### 基本使用 + +```java +Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(ObservableEmitter emitter) { + emitter.onNext("Hello"); + emitter.onNext("World"); + emitter.onComplete(); + } +}) +.subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(String s) { + System.out.println(s); + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onComplete() { + } +}); +``` + +--- + +## 操作符 + +### 常用操作符 + +```java +// map:转换 +Observable.just(1, 2, 3) + .map(i -> i * 2) + .subscribe(System.out::println); + +// filter:过滤 +Observable.just(1, 2, 3, 4, 5) + .filter(i -> i % 2 == 0) + .subscribe(System.out::println); + +// flatMap:扁平化 +Observable.just(1, 2, 3) + .flatMap(i -> Observable.just(i * 2)) + .subscribe(System.out::println); +``` + +--- + +## 线程调度 + +### Scheduler + +```java +// subscribeOn:指定上游线程 +// observeOn:指定下游线程 + +Observable.create(...) + .subscribeOn(Schedulers.io()) // 在 IO 线程执行 + .observeOn(AndroidSchedulers.mainThread()) // 在主线程观察 + .subscribe(...); +``` + +--- + +## 背压 + +### 背压问题 + +```java +// 当生产速度 > 消费速度时,产生背压 +// 可能导致内存溢出 +``` + +### 解决方案 + +```java +// 使用 Flowable +Flowable.create(...) + .onBackpressureBuffer() // 缓冲 + .subscribe(...); +``` + +--- + +## RxJava源码分析 + +### 关键类 + +```java +// Observable:被观察者 +// Observer:观察者 +// Scheduler:调度器 +// Operator:操作符 +``` + +--- + +## RxJava最佳实践 + +### 1. 及时取消订阅 + +```java +Disposable disposable = observable.subscribe(...); +disposable.dispose(); // 取消订阅 +``` + +### 2. 使用合适的操作符 + +```java +// 根据需求选择操作符 +// map、filter、flatMap 等 +``` + +--- + +## 面试常见问题 + +### Q1: RxJava 的原理? + +**答案:** +- 基于观察者模式 +- 支持链式调用 +- 支持线程调度 + +### Q2: subscribeOn 和 observeOn 的区别? + +**答案:** +- **subscribeOn**:指定上游线程 +- **observeOn**:指定下游线程 + +### Q3: RxJava 的背压? + +**答案:** +- 生产速度 > 消费速度时产生 +- 使用 Flowable 处理 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/性能优化/内存优化.md b/docs/android面试/性能优化/内存优化.md new file mode 100644 index 0000000..d4d0d06 --- /dev/null +++ b/docs/android面试/性能优化/内存优化.md @@ -0,0 +1,734 @@ +# 内存优化 + +## 目录 +- [内存泄漏](#内存泄漏) +- [内存抖动](#内存抖动) +- [内存分析工具](#内存分析工具) +- [内存优化方案](#内存优化方案) +- [图片内存优化](#图片内存优化) +- [对象池](#对象池) +- [面试常见问题](#面试常见问题) + +--- + +## 内存泄漏 + +### 什么是内存泄漏? + +内存泄漏是指程序在运行过程中,由于某些原因导致无法释放已经不再使用的内存,造成内存浪费,最终可能导致 OOM(OutOfMemoryError)。 + +### 常见内存泄漏场景 + +#### 1. 静态变量持有 Context + +```java +// ❌ 错误示例 +public class MyActivity extends AppCompatActivity { + private static Context sContext; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + sContext = this; // 静态变量持有 Activity,导致泄漏 + } +} + +// ✅ 正确做法 +public class MyActivity extends AppCompatActivity { + private static Context sContext; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + sContext = getApplicationContext(); // 使用 Application Context + } + + @Override + protected void onDestroy() { + super.onDestroy(); + sContext = null; // 及时释放 + } +} +``` + +#### 2. Handler 内存泄漏 + +```java +// ❌ 错误示例 +public class MyActivity extends AppCompatActivity { + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + // Handler 持有 Activity 的隐式引用 + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + // 如果 Activity 已销毁,但 Handler 还在处理消息,导致泄漏 + } + }, 10000); + } +} + +// ✅ 正确做法 +public class MyActivity extends AppCompatActivity { + private Handler mHandler; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (MyActivity.this.isFinishing()) { + return; // Activity 已销毁,不处理消息 + } + // 处理消息 + } + }; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mHandler.removeCallbacksAndMessages(null); // 移除所有消息 + } +} + +// ✅ 使用静态内部类 + WeakReference +public class MyActivity extends AppCompatActivity { + private static class MyHandler extends Handler { + private WeakReference mActivity; + + MyHandler(MyActivity activity) { + mActivity = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + MyActivity activity = mActivity.get(); + if (activity == null || activity.isFinishing()) { + return; + } + // 处理消息 + } + } +} +``` + +#### 3. 单例模式持有 Context + +```java +// ❌ 错误示例 +public class Singleton { + private static Singleton instance; + private Context mContext; + + private Singleton(Context context) { + this.mContext = context; // 持有 Activity Context + } + + public static Singleton getInstance(Context context) { + if (instance == null) { + instance = new Singleton(context); + } + return instance; + } +} + +// ✅ 正确做法 +public class Singleton { + private static Singleton instance; + private Context mContext; + + private Singleton(Context context) { + this.mContext = context.getApplicationContext(); // 使用 Application Context + } + + public static Singleton getInstance(Context context) { + if (instance == null) { + synchronized (Singleton.class) { + if (instance == null) { + instance = new Singleton(context); + } + } + } + return instance; + } +} +``` + +#### 4. 内部类持有外部类引用 + +```java +// ❌ 错误示例 +public class MyActivity extends AppCompatActivity { + private MyAsyncTask mTask; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mTask = new MyAsyncTask(); // 内部类持有 Activity 引用 + mTask.execute(); + } + + private class MyAsyncTask extends AsyncTask { + @Override + protected Void doInBackground(Void... voids) { + // 耗时操作 + return null; + } + } +} + +// ✅ 正确做法:使用静态内部类 +public class MyActivity extends AppCompatActivity { + private MyAsyncTask mTask; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mTask = new MyAsyncTask(this); + mTask.execute(); + } + + private static class MyAsyncTask extends AsyncTask { + private WeakReference mActivity; + + MyAsyncTask(MyActivity activity) { + mActivity = new WeakReference<>(activity); + } + + @Override + protected Void doInBackground(Void... voids) { + // 耗时操作 + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + MyActivity activity = mActivity.get(); + if (activity == null || activity.isFinishing()) { + return; + } + // 更新 UI + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mTask != null) { + mTask.cancel(true); + } + } +} +``` + +#### 5. 监听器未注销 + +```java +// ❌ 错误示例 +public class MyActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); + Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL); + // 忘记注销监听器 + } +} + +// ✅ 正确做法 +public class MyActivity extends AppCompatActivity { + private SensorManager mSensorManager; + private Sensor mSensor; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); + mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mSensorManager != null) { + mSensorManager.unregisterListener(this); // 注销监听器 + } + } +} +``` + +#### 6. 集合类持有对象引用 + +```java +// ❌ 错误示例 +public class MyActivity extends AppCompatActivity { + private static List sViews = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + View view = findViewById(R.id.view); + sViews.add(view); // 静态集合持有 View,View 持有 Activity,导致泄漏 + } +} + +// ✅ 正确做法 +public class MyActivity extends AppCompatActivity { + private static List> sViews = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + View view = findViewById(R.id.view); + sViews.add(new WeakReference<>(view)); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // 清理集合 + sViews.clear(); + } +} +``` + +--- + +## 内存抖动 + +### 什么是内存抖动? + +内存抖动是指短时间内频繁分配和回收内存,导致 GC 频繁执行,影响应用性能。 + +### 内存抖动的原因 + +1. **频繁创建对象**:在循环中创建大量临时对象 +2. **字符串拼接**:使用 `+` 拼接字符串 +3. **集合扩容**:集合频繁扩容导致内存分配 + +### 内存抖动示例 + +```java +// ❌ 错误示例:频繁创建对象 +for (int i = 0; i < 10000; i++) { + String str = new String("Hello" + i); // 每次循环都创建新对象 + list.add(str); +} + +// ✅ 正确做法:使用 StringBuilder +StringBuilder sb = new StringBuilder(); +for (int i = 0; i < 10000; i++) { + sb.append("Hello").append(i); + list.add(sb.toString()); + sb.setLength(0); // 清空 StringBuilder +} + +// ❌ 错误示例:字符串拼接 +String result = ""; +for (int i = 0; i < 1000; i++) { + result += "item" + i; // 每次拼接都创建新对象 +} + +// ✅ 正确做法:使用 StringBuilder +StringBuilder sb = new StringBuilder(); +for (int i = 0; i < 1000; i++) { + sb.append("item").append(i); +} +String result = sb.toString(); +``` + +--- + +## 内存分析工具 + +### 1. LeakCanary + +#### 集成 LeakCanary +```gradle +dependencies { + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' +} +``` + +#### 使用 +```java +public class MyApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + if (LeakCanary.isInAnalyzerProcess(this)) { + return; + } + LeakCanary.install(this); + } +} +``` + +### 2. Android Studio Memory Profiler + +- 实时查看内存使用 +- 查看内存分配 +- 查看内存泄漏 +- 生成 Heap Dump + +### 3. MAT (Memory Analyzer Tool) + +- 分析 Heap Dump +- 查找内存泄漏 +- 查看对象引用关系 + +### 4. adb 命令 + +```bash +# 查看内存信息 +adb shell dumpsys meminfo + +# 查看进程内存 +adb shell dumpsys meminfo + +# 强制 GC +adb shell am force-stop +``` + +--- + +## 内存优化方案 + +### 1. 使用对象池 + +```java +public class ObjectPool { + private Queue pool = new LinkedList<>(); + private Supplier factory; + private int maxSize; + + public ObjectPool(Supplier factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + } + + public T acquire() { + T obj = pool.poll(); + if (obj == null) { + obj = factory.get(); + } + return obj; + } + + public void release(T obj) { + if (pool.size() < maxSize) { + pool.offer(obj); + } + } +} + +// 使用 +ObjectPool pool = new ObjectPool<>( + StringBuilder::new, 10 +); +StringBuilder sb = pool.acquire(); +// 使用 sb +pool.release(sb); +``` + +### 2. 使用弱引用 + +```java +// WeakReference:GC 时会被回收 +WeakReference weakRef = new WeakReference<>(activity); +Activity activity = weakRef.get(); +if (activity == null) { + // 已被回收 +} + +// SoftReference:内存不足时会被回收 +SoftReference softRef = new SoftReference<>(bitmap); +Bitmap bitmap = softRef.get(); +if (bitmap == null) { + // 已被回收 +} +``` + +### 3. 及时释放资源 + +```java +public class MyActivity extends AppCompatActivity { + private Bitmap mBitmap; + private FileInputStream mInputStream; + + @Override + protected void onDestroy() { + super.onDestroy(); + + // 释放 Bitmap + if (mBitmap != null && !mBitmap.isRecycled()) { + mBitmap.recycle(); + mBitmap = null; + } + + // 关闭流 + if (mInputStream != null) { + try { + mInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + mInputStream = null; + } + } +} +``` + +### 4. 使用 SparseArray 替代 HashMap + +```java +// HashMap 会创建 Integer 对象 +HashMap map = new HashMap<>(); + +// SparseArray 避免自动装箱 +SparseArray sparseArray = new SparseArray<>(); +sparseArray.put(1, "value"); +``` + +### 5. 避免在 onDraw 中创建对象 + +```java +// ❌ 错误示例 +@Override +protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + Paint paint = new Paint(); // 每次绘制都创建新对象 + canvas.drawText("Hello", 0, 0, paint); +} + +// ✅ 正确做法 +public class MyView extends View { + private Paint mPaint; // 复用 Paint 对象 + + public MyView(Context context) { + super(context); + mPaint = new Paint(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawText("Hello", 0, 0, mPaint); + } +} +``` + +--- + +## 图片内存优化 + +### 1. 图片压缩 + +```java +public Bitmap compressBitmap(Bitmap bitmap, int maxWidth, int maxHeight) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + float scale = Math.min((float) maxWidth / width, (float) maxHeight / height); + if (scale < 1.0f) { + int newWidth = Math.round(width * scale); + int newHeight = Math.round(height * scale); + return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); + } + return bitmap; +} +``` + +### 2. 使用合适的图片格式 + +- **WebP**:压缩率高,支持透明 +- **PNG**:无损压缩,适合图标 +- **JPEG**:有损压缩,适合照片 + +### 3. 使用 inSampleSize + +```java +public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(res, resId, options); + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeResource(res, resId, options); +} + +private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + int height = options.outHeight; + int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + int halfHeight = height / 2; + int halfWidth = width / 2; + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + return inSampleSize; +} +``` + +### 4. 使用 RGB_565 + +```java +BitmapFactory.Options options = new BitmapFactory.Options(); +options.inPreferredConfig = Bitmap.Config.RGB_565; // 减少内存占用 +Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options); +``` + +### 5. 及时回收 Bitmap + +```java +if (bitmap != null && !bitmap.isRecycled()) { + bitmap.recycle(); + bitmap = null; +} +``` + +--- + +## 对象池 + +### 实现对象池 + +```java +public class RecyclerPool { + private final Queue pool = new LinkedList<>(); + private final Supplier factory; + private final Consumer reset; + private final int maxSize; + + public RecyclerPool(Supplier factory, Consumer reset, int maxSize) { + this.factory = factory; + this.reset = reset; + this.maxSize = maxSize; + } + + public T acquire() { + T obj = pool.poll(); + if (obj == null) { + obj = factory.get(); + } + return obj; + } + + public void release(T obj) { + if (obj == null) { + return; + } + reset.accept(obj); + if (pool.size() < maxSize) { + pool.offer(obj); + } + } + + public void clear() { + pool.clear(); + } +} + +// 使用示例 +RecyclerPool pool = new RecyclerPool<>( + StringBuilder::new, + sb -> sb.setLength(0), + 10 +); + +StringBuilder sb = pool.acquire(); +sb.append("Hello"); +// 使用完毕 +pool.release(sb); +``` + +--- + +## 面试常见问题 + +### Q1: 什么是内存泄漏?如何避免? + +**答案:** +- 内存泄漏是指程序无法释放不再使用的内存 +- 常见原因:静态变量持有 Context、Handler 未移除消息、监听器未注销等 +- 避免方法:使用 Application Context、使用 WeakReference、及时注销监听器等 + +### Q2: Handler 为什么会导致内存泄漏? + +**答案:** +- Handler 持有 Activity 的隐式引用 +- Message 持有 Handler 的引用 +- MessageQueue 持有 Message 的引用 +- 如果 Handler 还有未处理的消息,Activity 就无法被回收 + +### Q3: 如何检测内存泄漏? + +**答案:** +1. 使用 LeakCanary 自动检测 +2. 使用 Android Studio Memory Profiler +3. 使用 MAT 分析 Heap Dump +4. 使用 adb dumpsys meminfo 命令 + +### Q4: 什么是内存抖动?如何避免? + +**答案:** +- 内存抖动是短时间内频繁分配和回收内存 +- 避免方法:使用对象池、使用 StringBuilder、避免在循环中创建对象 + +### Q5: 如何优化图片内存? + +**答案:** +1. 使用合适的图片格式(WebP、PNG、JPEG) +2. 使用 inSampleSize 压缩 +3. 使用 RGB_565 减少内存 +4. 及时回收 Bitmap +5. 使用图片加载库(Glide、Picasso) + +### Q6: Context 的使用注意事项? + +**答案:** +- Activity Context:生命周期短,不能长期持有 +- Application Context:生命周期长,可以长期持有 +- 静态变量、单例等应使用 Application Context + +### Q7: 如何优化内存使用? + +**答案:** +1. 避免内存泄漏 +2. 使用对象池复用对象 +3. 使用弱引用 +4. 及时释放资源 +5. 优化图片内存 +6. 使用 SparseArray 替代 HashMap + +--- + +## 总结 + +内存优化是 Android 性能优化的重要部分,主要从以下几个方面入手: + +1. **避免内存泄漏**:注意 Context、Handler、监听器的使用 +2. **避免内存抖动**:减少对象创建,使用对象池 +3. **优化图片内存**:压缩、使用合适格式、及时回收 +4. **使用工具检测**:LeakCanary、Memory Profiler、MAT + +通过合理的内存优化,可以避免 OOM,提升应用性能。 + +--- + +*最后更新:2024年* diff --git a/docs/android面试/性能优化/启动优化.md b/docs/android面试/性能优化/启动优化.md new file mode 100644 index 0000000..45f8153 --- /dev/null +++ b/docs/android面试/性能优化/启动优化.md @@ -0,0 +1,557 @@ +# 启动优化 + +## 目录 +- [启动流程分析](#启动流程分析) +- [启动时间测量](#启动时间测量) +- [冷启动优化](#冷启动优化) +- [热启动优化](#热启动优化) +- [启动优化方案](#启动优化方案) +- [启动优化工具](#启动优化工具) +- [面试常见问题](#面试常见问题) + +--- + +## 启动流程分析 + +### 应用启动类型 + +#### 1. 冷启动(Cold Start) +- **定义**:系统进程中没有该应用的任何进程信息,需要创建新进程 +- **特点**:启动时间最长,需要加载所有资源 +- **流程**: + 1. 启动进程 + 2. 创建Application对象 + 3. 启动主线程 + 4. 创建主Activity + 5. 加载布局 + 6. 执行onCreate + 7. 测量、布局、绘制 + 8. 显示到屏幕 + +#### 2. 热启动(Hot Start) +- **定义**:应用进程还在,只是Activity被销毁 +- **特点**:启动时间短,只需恢复Activity +- **流程**: + 1. 恢复Activity + 2. 调用onCreate、onStart、onResume + 3. 显示到屏幕 + +#### 3. 温启动(Warm Start) +- **定义**:应用进程存在,但Activity需要重建 +- **特点**:介于冷启动和热启动之间 + +### 启动时间线 + +``` +进程创建 → Application创建 → Activity创建 → 布局加载 → 首帧绘制 +``` + +--- + +## 启动时间测量 + +### 1. 使用 adb 命令测量 + +```bash +# 测量冷启动时间 +adb shell am start -W -n com.example.app/.MainActivity + +# 输出示例: +# ThisTime: 500ms # 最后一个Activity启动耗时 +# TotalTime: 800ms # 所有Activity启动耗时 +# WaitTime: 1000ms # 系统启动应用耗时 +``` + +### 2. 代码中测量 + +```java +// Application onCreate +@Override +public void onCreate() { + super.onCreate(); + long startTime = System.currentTimeMillis(); + + // 初始化代码 + + long endTime = System.currentTimeMillis(); + Log.d("Startup", "Application onCreate: " + (endTime - startTime) + "ms"); +} + +// Activity onCreate +@Override +protected void onCreate(Bundle savedInstanceState) { + long startTime = System.currentTimeMillis(); + super.onCreate(savedInstanceState); + + // 初始化代码 + + long endTime = System.currentTimeMillis(); + Log.d("Startup", "Activity onCreate: " + (endTime - startTime) + "ms"); +} +``` + +### 3. 使用 TraceView + +```java +// 开始追踪 +Debug.startMethodTracing("startup"); + +// 结束追踪 +Debug.stopMethodTracing(); +``` + +### 4. 使用 Systrace/Perfetto + +```bash +# 使用 Systrace +python systrace.py -t 10 -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res + +# 使用 Perfetto +# 在开发者选项中启用系统追踪 +``` + +--- + +## 冷启动优化 + +### 1. Application 优化 + +#### 延迟初始化 +```java +public class MyApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + + // 必要初始化(立即执行) + initCrashHandler(); + + // 非必要初始化(延迟执行) + new Handler(Looper.getMainLooper()).postDelayed(() -> { + initThirdPartySDK(); + }, 100); + } +} +``` + +#### 异步初始化 +```java +public class MyApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + + // 使用线程池异步初始化 + ExecutorService executor = Executors.newCachedThreadPool(); + executor.execute(() -> { + initHeavySDK(); + }); + } +} +``` + +#### 使用 ContentProvider 初始化(不推荐) +```java +// 不推荐:ContentProvider 的 onCreate 在 Application onCreate 之前执行 +// 会阻塞主线程 +public class InitProvider extends ContentProvider { + @Override + public boolean onCreate() { + // 初始化代码 + return true; + } +} +``` + +### 2. Activity 优化 + +#### 减少 onCreate 耗时 +```java +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 1. 延迟非关键初始化 + new Handler().post(() -> { + initNonCriticalComponents(); + }); + + // 2. 使用 ViewStub 延迟加载 + ViewStub viewStub = findViewById(R.id.view_stub); + viewStub.inflate(); // 需要时才加载 + + // 3. 异步加载数据 + loadDataAsync(); +} +``` + +#### 使用启动窗口 +```xml + + +``` + +```xml + + + + + + + +``` + +```java +// MainActivity.java +@Override +protected void onCreate(Bundle savedInstanceState) { + setTheme(R.style.AppTheme); // 恢复正常主题 + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); +} +``` + +### 3. 布局优化 + +#### 减少布局层级 +```xml + + + + + + + + + + + + + +``` + +#### 使用 ViewStub +```xml + +``` + +```java +// 需要时才加载 +ViewStub viewStub = findViewById(R.id.view_stub); +viewStub.inflate(); +``` + +#### 使用 Merge 标签 +```xml + + + +