This commit is contained in:
renjianbo
2026-01-12 17:14:58 +08:00
parent 301dae780f
commit 8a4717277d
20 changed files with 5452 additions and 19 deletions

View File

@@ -13,15 +13,30 @@
"state": {
"type": "markdown",
"state": {
"file": "docs/Obsidian笔记体系/Areas/05-进程与线程通信/README.md",
"file": "docs/cursor/cursor.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "README"
"title": "cursor"
}
},
{
"id": "42dcaa0fed1f392a",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "个人笔记体系.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "个人笔记体系"
}
}
]
],
"currentTab": 1
}
],
"direction": "vertical"
@@ -183,13 +198,24 @@
"bases:创建新数据库": false
}
},
"active": "f1ecba89214e3cc5",
"active": "42dcaa0fed1f392a",
"lastOpenFiles": [
"docs/Obsidian笔记体系/Areas/05-进程与线程通信/Handler机制源码解析.md",
"docs/Obsidian笔记体系/Resources/工具/脚本库/log_analyzer.py",
"docs/Obsidian笔记体系/Resources/工具/脚本库/performance_monitor.sh",
"docs/Obsidian笔记体系/Resources/工具/脚本库/test_startup_time.sh",
"docs/Obsidian笔记体系/脚本库/performance_monitor.sh",
"docs/Obsidian笔记体系/脚本库",
"docs/Obsidian笔记体系/Resources/工具/脚本库/collect_logs.sh",
"docs/Obsidian笔记体系/Resources/工具/脚本库/install_apks.sh",
"docs/Obsidian笔记体系/Resources/工具/脚本库/README.md",
"docs/Obsidian笔记体系/Resources/工具/效率工具推荐/README.md",
"docs/cursor/cursor.md",
"个人笔记体系.md",
"docs/Obsidian/2026-01-05 个人文档管理.md",
"docs/Obsidian笔记体系/Areas/05-进程与线程通信/README.md",
"docs/Obsidian笔记体系/Areas/05-进程与线程通信/Handler机制源码解析.md",
"docs/Obsidian笔记体系/Areas/06-性能优化体系/内存优化LeakCanary原理.md",
"docs/Obsidian笔记体系/Areas/06-性能优化体系/启动优化方法论.md",
"docs/cursor/cursor.md",
"docs/Obsidian笔记体系/Templates/源码解析模板.md",
"docs/Obsidian笔记体系/Templates/问题排查模板.md",
"docs/Obsidian笔记体系/Templates/技术方案设计模板.md",
@@ -206,20 +232,9 @@
"README.md",
"PackageManagerService.md",
"docs/Obsidian笔记体系/Areas/03-Window系统/README.md",
"docs/Obsidian笔记体系/Areas/04-资源与包管理/README.md",
"动态加载与热修复原理.md",
"docs/Obsidian笔记体系/Areas/03-Window系统/WindowManagerService架构.md",
"docs/Obsidian笔记体系/Areas/03-Window系统/SurfaceFlinger交互流程.md",
"docs/Obsidian笔记体系/Areas/03-Window系统/窗口类型与层级.md",
"docs/Obsidian笔记体系/Areas/01-系统启动流程/Bootloader到Init.md.bak",
"docs/Obsidian笔记体系/Config/自定义脚本/源码链接生成器.py",
"docs/Obsidian笔记体系/Config/自定义脚本/自动生成日报.js",
"docs/Obsidian笔记体系/Config/主题与样式.css",
"docs/Obsidian笔记体系/Config/自定义脚本",
"docs/Obsidian笔记体系/Daily/templates",
"docs/Obsidian笔记体系/Archive/资源-历史会议记录",
"docs/Obsidian笔记体系/Archive/领域-已废弃API研究",
"docs/Obsidian笔记体系/Archive/项目-旧版ROM适配",
"docs/Obsidian笔记体系/Resources/会议与分享/内部技术分享记录"
"docs/Obsidian笔记体系/Config/主题与样式.css"
]
}

View File

@@ -1,4 +1,4 @@
# 05-进程与线程通信
# 05-进程与线程通信
本目录包含Android系统中进程与线程通信相关技术的深入分析和源码解析。涵盖了Binder机制、Handler机制、AIDL/HIDL、跨进程同步等核心内容。

View File

@@ -1 +1,84 @@
# 06-性能优化体系
## 概述
性能优化是Android开发中的核心技能之一涉及启动速度、内存管理、流畅度、功耗等多个维度。本目录系统性地整理了Android性能优化的方法论、工具链和实践经验。
## 目录结构
### 1. 启动优化方法论
- 冷启动、温启动、热启动的区别
- 启动时间测量方法
- 启动优化策略与实践
### 2. 内存优化LeakCanary原理
- 内存泄漏检测原理
- LeakCanary工作机制
- 常见内存泄漏场景与解决方案
### 3. 流畅度Choreographer+VSYNC
- VSYNC机制原理
- Choreographer源码解析
- 卡顿分析与优化
### 4. 功耗优化工具链
- 功耗分析工具
- 电量消耗优化策略
- 后台任务管理
## 性能优化原则
### 1. 测量优先
- 先测量,再优化
- 使用专业工具获取准确数据
- 建立性能基线
### 2. 系统化思考
- 从架构层面考虑优化
- 避免局部优化导致全局问题
- 平衡性能与可维护性
### 3. 持续监控
- 建立性能监控体系
- 设置性能告警阈值
- 定期性能回归测试
## 性能指标
### 启动时间
- **冷启动**: < 2秒
- **热启动**: < 500ms
- **首屏渲染**: < 1秒
### 内存
- **峰值内存**: 根据设备配置设定上限
- **内存泄漏**: 0个
- **GC频率**: 尽量减少Full GC
### 流畅度
- **FPS**: 稳定在60fps
- **掉帧率**: < 1%
- **ANR率**: < 0.1%
### 功耗
- **待机功耗**: < 5mA
- **使用功耗**: 根据场景设定
- **后台功耗**: 最小化
## 工具链
### 官方工具
- **Android Profiler**: 内存、CPU、网络分析
- **Systrace/Perfetto**: 系统级性能分析
- **Battery Historian**: 功耗分析
### 第三方工具
- **LeakCanary**: 内存泄漏检测
- **BlockCanary**: 主线程阻塞检测
- **Matrix**: 腾讯开源性能监控框架
## 相关链接
- [[05-进程与线程通信]]
- [[09-调试与工具链]]
- [[MOCs/Android Framework知识体系图]]

View File

@@ -1 +1,410 @@
# 内存优化LeakCanary原理
## 概述
内存泄漏是Android开发中的常见问题会导致应用内存占用持续增长最终可能引发OOMOut of Memory崩溃。LeakCanary是Square公司开源的内存泄漏检测工具本文档深入解析其工作原理和使用方法。
## 内存泄漏基础
### 什么是内存泄漏
内存泄漏是指程序在运行过程中,由于某些原因导致已分配的内存无法被回收,造成内存浪费和潜在的性能问题。
### Android中的内存泄漏场景
1. **静态变量持有Context**
```java
// 错误示例
public class MyApplication {
private static Context sContext; // 持有Activity引用
}
// 正确做法
public class MyApplication {
private static Context sAppContext; // 使用Application Context
}
```
2. **非静态内部类持有外部类引用**
```java
// 错误示例
public class MainActivity extends Activity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 隐式持有MainActivity引用
}
};
}
// 正确做法:使用静态内部类 + WeakReference
public class MainActivity extends Activity {
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mActivityRef;
MyHandler(MainActivity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivityRef.get();
if (activity != null) {
// 使用activity
}
}
}
}
```
3. **监听器未注销**
```java
// 错误示例
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this); // 注册但未注销
}
}
// 正确做法
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
```
4. **资源未关闭**
```java
// 错误示例
FileInputStream fis = new FileInputStream(file);
// 使用后未关闭
// 正确做法使用try-with-resources
try (FileInputStream fis = new FileInputStream(file)) {
// 使用fis
} // 自动关闭
```
## LeakCanary工作原理
### 核心机制
LeakCanary通过以下步骤检测内存泄漏
1. **对象引用监控**: 监控Activity/Fragment等关键对象的生命周期
2. **弱引用检测**: 使用WeakReference + ReferenceQueue检测对象是否被回收
3. **堆转储分析**: 当检测到泄漏时dump堆内存进行分析
4. **泄漏路径分析**: 使用Shark库分析GC Root到泄漏对象的引用链
### 工作流程
```
Activity创建
LeakCanary.watch() 监控
Activity销毁
等待5秒GC时间
检查对象是否被回收
未回收 → 触发堆转储
分析引用链
生成泄漏报告
```
### 源码关键点
#### 1. ObjectWatcher
```java
// 监控对象是否被回收
public class ObjectWatcher {
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
private final Map<String, KeyedWeakReference> watchedObjects = new HashMap<>();
public void watch(Object watchedObject, String referenceName) {
KeyedWeakReference reference = new KeyedWeakReference(
watchedObject, referenceName, queue, clock);
watchedObjects.put(key, reference);
}
}
```
#### 2. HeapDumper
```java
// 堆转储
public interface HeapDumper {
File dumpHeap();
}
```
#### 3. HeapAnalyzer
```java
// 分析堆转储文件
public class HeapAnalyzer {
public AnalysisResult checkForLeaks(File heapDumpFile) {
// 使用Shark库分析hprof文件
// 查找GC Root到泄漏对象的路径
}
}
```
## LeakCanary集成
### 添加依赖
```gradle
dependencies {
// LeakCanary
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
```
### 自动安装(无需代码)
LeakCanary 2.0+ 使用ContentProvider自动初始化无需手动配置。
### 手动监控
```java
// 监控自定义对象
LeakCanary.INSTANCE.getObjectWatcher().watch(
myObject,
"MyObject被监控"
);
```
## 常见内存泄漏案例
### 1. Handler泄漏
**问题代码**:
```java
public class MainActivity extends Activity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Handler持有MainActivity的隐式引用
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 发送延迟消息
mHandler.sendEmptyMessageDelayed(0, 60000);
}
}
```
**解决方案**:
```java
public class MainActivity extends Activity {
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mActivityRef;
MyHandler(MainActivity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivityRef.get();
if (activity == null) {
return;
}
// 处理消息
}
}
private MyHandler mHandler = new MyHandler(this);
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
```
### 2. 单例持有Context
**问题代码**:
```java
public class AppManager {
private static AppManager sInstance;
private Context mContext;
private AppManager(Context context) {
mContext = context; // 可能持有Activity
}
public static AppManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new AppManager(context);
}
return sInstance;
}
}
```
**解决方案**:
```java
public class AppManager {
private static AppManager sInstance;
private Context mContext;
private AppManager(Context context) {
// 使用Application Context
mContext = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new AppManager(context);
}
return sInstance;
}
}
```
### 3. 匿名内部类
**问题代码**:
```java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 匿名内部类持有外部类引用
new Thread(new Runnable() {
@Override
public void run() {
// 长时间运行的任务
while (true) {
// ...
}
}
}).start();
}
}
```
**解决方案**:
```java
public class MainActivity extends Activity {
private static class MyRunnable implements Runnable {
private WeakReference<MainActivity> mActivityRef;
MyRunnable(MainActivity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void run() {
MainActivity activity = mActivityRef.get();
if (activity == null) {
return;
}
// 执行任务
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new MyRunnable(this)).start();
}
}
```
## 内存优化最佳实践
### 1. 使用Application Context
```java
// 优先使用Application Context
Context appContext = context.getApplicationContext();
```
### 2. 及时释放资源
```java
@Override
protected void onDestroy() {
super.onDestroy();
// 注销监听器
// 取消网络请求
// 停止动画
// 释放资源
}
```
### 3. 使用WeakReference
```java
// 对于可能持有Activity引用的对象使用WeakReference
private WeakReference<Activity> mActivityRef;
```
### 4. 避免静态变量持有View/Context
```java
// 错误
private static TextView sTextView;
// 正确使用WeakReference或避免静态变量
```
### 5. 使用对象池
```java
// 复用对象减少GC压力
ObjectPool<MyObject> pool = new ObjectPool<>(10);
MyObject obj = pool.acquire();
// 使用后归还
pool.release(obj);
```
## 内存分析工具
### 1. Android Profiler
- 实时内存监控
- 堆转储分析
- 内存分配追踪
### 2. MAT (Memory Analyzer Tool)
- 分析hprof文件
- 查找内存泄漏
- 查看对象引用关系
### 3. LeakCanary
- 自动检测泄漏
- 可视化引用链
- 开发阶段使用
## 内存优化指标
- **峰值内存**: 根据设备配置设定上限如512MB
- **内存泄漏**: 0个
- **GC频率**: 尽量减少Full GC
- **内存抖动**: 避免频繁分配/释放
## 相关链接
- [[README]]
- [[09-调试与工具链]]
- [[05-进程与线程通信]]

View File

@@ -1 +1,305 @@
# 功耗优化工具链
## 概述
功耗优化是移动应用开发中的重要环节直接影响用户体验和设备续航。本文档介绍Android功耗优化的工具链、分析方法和优化策略。
## 功耗分析工具
### 1. Battery Historian
Google官方提供的功耗分析工具可以分析设备的电量消耗情况。
#### 安装与使用
```bash
# 安装Battery Historian
go get -u github.com/google/battery-historian
# 启动服务
cd $GOPATH/src/github.com/google/battery-historian
go run cmd/battery-historian/battery-historian.go
```
#### 数据采集
```bash
# 重置电池统计
adb shell dumpsys batterystats --reset
# 执行测试场景
# ... 进行应用操作 ...
# 导出数据
adb bugreport > bugreport.txt
```
#### 关键指标
- **Wake Lock**: 唤醒锁持有时间
- **CPU**: CPU使用率
- **Network**: 网络流量
- **GPS**: GPS使用情况
- **Screen**: 屏幕亮度与唤醒时间
### 2. Android Profiler
Android Studio内置的性能分析工具可以实时监控功耗。
#### 使用步骤
1. 连接设备
2. 打开Android Profiler
3. 选择Energy标签
4. 监控电量消耗
#### 功能特点
- 实时监控
- 可视化展示
- 支持录制和回放
### 3. dumpsys batterystats
系统命令,可以获取详细的电池统计信息。
```bash
# 查看电池统计
adb shell dumpsys batterystats
# 查看特定应用的功耗
adb shell dumpsys batterystats com.example.app
# 重置统计
adb shell dumpsys batterystats --reset
```
### 4. Power Monitor
硬件级功耗监控工具需要支持Power Monitor的设备。
## 功耗优化策略
### 1. CPU优化
#### 减少CPU唤醒
```java
// 避免频繁唤醒CPU
// 使用JobScheduler替代AlarmManager
JobScheduler jobScheduler =
(JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(this, MyJobService.class))
.setPeriodic(15 * 60 * 1000) // 15分钟
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
jobScheduler.schedule(jobInfo);
```
#### 优化算法复杂度
- 使用高效的数据结构
- 避免不必要的循环
- 使用缓存减少计算
#### 后台任务优化
```java
// 使用WorkManager管理后台任务
WorkRequest uploadWork = new OneTimeWorkRequest.Builder(UploadWorker.class)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.build())
.build();
WorkManager.getInstance(context).enqueue(uploadWork);
```
### 2. 网络优化
#### 批量请求
```java
// 合并多个小请求为一个批量请求
// 减少网络唤醒次数
```
#### 使用缓存
```java
// 使用HTTP缓存减少网络请求
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(1, TimeUnit.HOURS)
.build();
```
#### 压缩数据
```java
// 使用Gzip压缩
// 减少传输数据量
```
### 3. Wake Lock管理
#### 及时释放Wake Lock
```java
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag");
try {
wakeLock.acquire();
// 执行需要保持唤醒的操作
} finally {
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
```
#### 使用超时机制
```java
// 设置超时,避免长时间持有
wakeLock.acquire(10 * 60 * 1000L); // 10分钟超时
```
### 4. 定位服务优化
#### 使用低功耗定位
```java
LocationRequest locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_LOW_POWER)
.setInterval(60000) // 1分钟
.setMaxWaitTime(5 * 60 * 1000); // 5分钟最大等待
```
#### 及时停止定位
```java
// 不需要定位时立即停止
locationManager.removeUpdates(locationListener);
```
### 5. 屏幕优化
#### 降低屏幕亮度
```java
// 根据环境光自动调整
WindowManager.LayoutParams params = getWindow().getAttributes();
params.screenBrightness = 0.5f; // 50%亮度
getWindow().setAttributes(params);
```
#### 减少屏幕唤醒时间
```java
// 设置屏幕超时时间
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// 使用后及时清除
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
```
### 6. 后台任务优化
#### 使用Doze模式
```java
// 适配Doze模式
// 使用白名单机制
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
boolean isIgnoringBatteryOptimizations =
powerManager.isIgnoringBatteryOptimizations(getPackageName());
if (!isIgnoringBatteryOptimizations) {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
```
#### 延迟非紧急任务
```java
// 使用JobScheduler延迟执行
JobInfo jobInfo = new JobInfo.Builder(1, componentName)
.setMinimumLatency(30 * 60 * 1000) // 延迟30分钟
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
```
## 功耗测试方法
### 1. 基准测试
```bash
# 1. 重置电池统计
adb shell dumpsys batterystats --reset
# 2. 执行测试场景30分钟
# 模拟用户正常使用
# 3. 导出数据
adb bugreport > bugreport.txt
# 4. 分析Battery Historian报告
```
### 2. 对比测试
- 优化前后对比
- 竞品对比
- 不同设备对比
### 3. 场景测试
- 待机场景
- 使用场景
- 后台场景
## 常见问题
### 1. Wake Lock泄漏
**症状**: 设备无法进入休眠
**排查**:
```bash
adb shell dumpsys power
```
**解决**: 检查所有Wake Lock的acquire/release配对
### 2. 后台网络请求过多
**症状**: 待机时仍有网络流量
**排查**: Battery Historian查看Network统计
**解决**: 使用JobScheduler批量处理
### 3. GPS持续定位
**症状**: 定位服务一直运行
**排查**: Battery Historian查看GPS统计
**解决**: 及时停止定位服务
## 最佳实践
1. **最小化原则**: 只做必要的工作
2. **批量处理**: 合并多个小任务
3. **延迟执行**: 非紧急任务延迟处理
4. **及时释放**: 用完资源立即释放
5. **监控告警**: 建立功耗监控体系
## 相关链接
- [[README]]
- [[启动优化方法论]]
- [[09-调试与工具链/Systrace_Perfetto全解读]]

View File

@@ -1 +1,427 @@
# 启动优化方法论
## 概述
应用启动速度直接影响用户体验是性能优化的重要指标。本文档系统介绍Android应用启动优化的方法论、测量工具和优化策略。
## 启动类型
### 1. 冷启动Cold Start
**定义**: 应用进程不存在,系统需要创建新进程并初始化应用。
**特点**:
- 耗时最长
- 需要加载所有资源
- 创建Application和主Activity
**时间范围**: 通常2-5秒
### 2. 温启动Warm Start
**定义**: 应用进程存在但Activity被销毁需要重新创建Activity。
**特点**:
- 进程已存在
- 只需创建Activity
- 比冷启动快
**时间范围**: 通常1-3秒
### 3. 热启动Hot Start
**定义**: 应用进程和Activity都存在只需将Activity带到前台。
**特点**:
- 最快
- 只需恢复UI状态
- 几乎不需要初始化
**时间范围**: 通常< 500ms
## 启动时间测量
### 1. adb命令测量
```bash
# 测量启动时间
adb shell am start -W -n com.example.app/.MainActivity
# 输出示例:
# Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.app/.MainActivity }
# Status: ok
# Activity: com.example.app/.MainActivity
# ThisTime: 500
# TotalTime: 500
# WaitTime: 520
```
**指标说明**:
- **ThisTime**: 最后一个Activity启动耗时
- **TotalTime**: 所有Activity启动耗时
- **WaitTime**: AMS启动Activity总耗时
### 2. 代码埋点测量
```java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
long startTime = System.currentTimeMillis();
super.onCreate(savedInstanceState);
// 初始化工作
long endTime = System.currentTimeMillis();
Log.d("Startup", "onCreate耗时: " + (endTime - startTime) + "ms");
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// 首帧渲染完成
long renderTime = System.currentTimeMillis() - getIntent().getLongExtra("start_time", 0);
Log.d("Startup", "首帧渲染耗时: " + renderTime + "ms");
}
}
}
```
### 3. 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
# 通过Android Studio的Profiler录制
```
**关键指标**:
- **Application.onCreate**: Application初始化时间
- **Activity.onCreate**: Activity创建时间
- **inflate**: 布局解析时间
- **measure/layout/draw**: 视图测量、布局、绘制时间
### 4. 首屏渲染时间TTI
```java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 监听首屏渲染
View rootView = findViewById(android.R.id.content);
rootView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
// 首屏已渲染
logTTI();
return true;
}
}
);
}
}
```
## 启动优化策略
### 1. Application优化
#### 延迟初始化
```java
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 立即初始化:必须的、轻量的
initCrashHandler();
// 延迟初始化:非关键的、耗时的
initInBackground();
}
private void initInBackground() {
new Thread(() -> {
// 在后台线程初始化
initHeavyLibrary();
initThirdPartySDK();
}).start();
}
}
```
#### 使用启动器框架
```java
// 使用Startup库管理初始化任务
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
AppStartup.initializeComponent(this, MyStartupInitializer.class);
}
}
```
### 2. 主线程优化
#### 减少主线程工作
```java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 避免在主线程做耗时操作
// ❌ 错误:同步网络请求
// String data = networkRequest();
// ✅ 正确:异步加载
loadDataAsync();
setContentView(R.layout.activity_main);
}
private void loadDataAsync() {
new Thread(() -> {
String data = networkRequest();
runOnUiThread(() -> {
updateUI(data);
});
}).start();
}
}
```
#### 使用IdleHandler延迟非关键任务
```java
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 主线程空闲时执行
initNonCriticalComponents();
return false; // false表示只执行一次
}
});
```
### 3. 布局优化
#### 减少布局层级
```xml
<!-- ❌ 错误:嵌套过深 -->
<LinearLayout>
<LinearLayout>
<LinearLayout>
<TextView />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- ✅ 正确使用ConstraintLayout扁平化 -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
```
#### 使用ViewStub延迟加载
```xml
<!-- 延迟加载非首屏内容 -->
<ViewStub
android:id="@+id/view_stub"
android:layout="@layout/heavy_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
```
```java
// 需要时再加载
ViewStub viewStub = findViewById(R.id.view_stub);
viewStub.inflate();
```
#### 优化布局inflate
```java
// 使用AsyncLayoutInflater异步加载布局
new AsyncLayoutInflater(this).inflate(
R.layout.activity_main,
null,
(view, resid, parent) -> {
setContentView(view);
// 布局已加载完成
}
);
```
### 4. 资源优化
#### 减少启动时加载的资源
```java
// 延迟加载大图片
// 使用占位图
ImageView imageView = findViewById(R.id.image);
imageView.setImageResource(R.drawable.placeholder);
// 异步加载
Glide.with(this)
.load(imageUrl)
.into(imageView);
```
#### 使用资源预加载
```java
// 在Application中预加载常用资源
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 预加载资源到内存
preloadResources();
}
private void preloadResources() {
new Thread(() -> {
// 预加载常用drawable
Resources res = getResources();
res.getDrawable(R.drawable.common_icon);
}).start();
}
}
```
### 5. 多进程优化
```xml
<!-- AndroidManifest.xml -->
<activity
android:name=".MainActivity"
android:process=":main" />
<service
android:name=".HeavyService"
android:process=":heavy" />
```
**优点**:
- 主进程启动更快
- 避免主进程OOM
- 隔离崩溃影响
**缺点**:
- 进程间通信开销
- 内存占用增加
### 6. 类加载优化
#### 减少类数量
```java
// 使用ProGuard/R8混淆和优化
// build.gradle
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}
```
#### 使用MultiDex优化
```java
// 对于方法数超过65535的应用
android {
defaultConfig {
multiDexEnabled true
}
}
dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
}
```
### 7. 启动画面优化
#### 使用Splash Screen APIAndroid 12+
```java
// 使用新的Splash Screen API
// 提供更好的启动体验
```
#### 传统启动画面
```java
// 快速显示启动画面
// 避免白屏/黑屏
<style name="LaunchTheme" parent="Theme.AppCompat.Light">
<item name="android:windowBackground">@drawable/splash_background</item>
<item name="android:windowFullscreen">true</item>
</style>
```
## 启动优化检查清单
### Application层
- [ ] 延迟非关键初始化
- [ ] 使用后台线程初始化耗时任务
- [ ] 避免在Application中做网络请求
- [ ] 使用启动器框架管理初始化顺序
### Activity层
- [ ] 减少onCreate中的工作
- [ ] 异步加载数据
- [ ] 使用ViewStub延迟加载
- [ ] 优化布局层级
### 资源层
- [ ] 减少启动时加载的资源
- [ ] 使用占位图
- [ ] 延迟加载大图片
- [ ] 优化资源大小
### 代码层
- [ ] 减少类数量使用ProGuard
- [ ] 避免静态初始化块中的耗时操作
- [ ] 优化依赖库加载
## 性能指标
### 目标值
- **冷启动**: < 2秒
- **温启动**: < 1秒
- **热启动**: < 500ms
- **首屏渲染**: < 1秒
### 测量方法
1. 使用adb命令测量
2. 代码埋点测量
3. Systrace/Perfetto分析
4. 用户感知时间测量
## 相关链接
- [[README]]
- [[流畅度Choreographer+VSYNC]]
- [[09-调试与工具链/Systrace_Perfetto全解读]]

View File

@@ -1 +1,499 @@
# 流畅度Choreographer+VSYNC
## 概述
流畅度是Android应用性能的核心指标之一直接影响用户体验。本文档深入解析VSYNC机制和Choreographer的工作原理以及如何分析和优化应用流畅度。
## VSYNC机制
### 什么是VSYNC
VSYNCVertical Synchronization垂直同步是显示系统与GPU之间的同步机制确保帧在正确的时机刷新到屏幕。
### VSYNC的作用
1. **防止画面撕裂**: 避免在画面刷新过程中显示不完整的帧
2. **统一刷新时机**: 所有绘制操作在VSYNC信号到来时同步执行
3. **保证流畅度**: 确保以固定频率通常60Hz刷新画面
### VSYNC工作流程
```
VSYNC信号16.67ms间隔60Hz
SurfaceFlinger接收VSYNC
通知应用层开始绘制
应用执行measure/layout/draw
提交帧到SurfaceFlinger
下一个VSYNC时显示
```
### VSYNC时间线
```
Frame N-1 Frame N Frame N+1
| | |
VSYNC ──────> VSYNC ──────> VSYNC
| | |
|<-- 16.67ms --><-- 16.67ms -->
```
**关键时间点**:
- **VSYNC间隔**: 16.67ms60Hz
- **绘制窗口**: 必须在16.67ms内完成
- **超过时间**: 会掉帧Jank
## Choreographer源码解析
### Choreographer的作用
Choreographer是Android系统提供的帧调度器负责协调应用层的绘制时机与VSYNC信号同步。
### 核心类结构
```java
public final class Choreographer {
// VSYNC回调接口
private final FrameDisplayEventReceiver mDisplayEventReceiver;
// 回调队列
private final CallbackQueue[] mCallbackQueues;
// 当前帧时间
private long mFrameTimeNanos;
// 帧间隔(纳秒)
private static final long FRAME_INTERVAL_NANOS = 16_666_667L; // 16.67ms
}
```
### 关键方法
#### 1. postFrameCallback
```java
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);
}
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
```
**作用**: 注册一个回调,在下一帧绘制时执行。
**使用场景**:
- 动画帧回调
- 自定义绘制
- 性能监控
#### 2. doFrame
```java
void doFrame(long frameTimeNanos, int frame) {
final long startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
// 掉帧检测
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
}
// 执行回调
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}
```
**回调执行顺序**:
1. **INPUT**: 输入事件处理
2. **ANIMATION**: 动画执行
3. **TRAVERSAL**: 视图遍历measure/layout/draw
4. **COMMIT**: 提交帧
### FrameDisplayEventReceiver
```java
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
// 接收VSYNC信号
long now = System.nanoTime();
if (timestampNanos > now) {
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
```
## 流畅度分析
### 1. 使用Systrace/Perfetto
```bash
# 录制Systrace
python systrace.py -t 10 -o trace.html gfx view sched freq idle am wm
# 关键指标:
# - Frame: 每一帧的耗时
# - VSYNC: VSYNC信号
# - UI Thread: 主线程执行情况
```
**分析要点**:
- 查看Frame是否超过16.67ms
- 识别主线程阻塞
- 查找耗时操作
### 2. 使用GPU Rendering Profile
```java
// 在开发者选项中开启"GPU渲染模式分析"
// 或使用代码开启
View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
```
**指标说明**:
- **绿色线**: 16.67ms基准线
- **蓝色**: 测量时间
- **紫色**: 布局时间
- **红色**: 绘制时间
### 3. 使用Choreographer监控
```java
public class FrameMonitor {
private Choreographer mChoreographer;
private long mLastFrameTimeNanos;
private int mDroppedFrames;
public void start() {
mChoreographer = Choreographer.getInstance();
mChoreographer.postFrameCallback(mFrameCallback);
}
private Choreographer.FrameCallback mFrameCallback =
new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (mLastFrameTimeNanos != 0) {
long frameInterval = frameTimeNanos - mLastFrameTimeNanos;
if (frameInterval > 16_666_667L * 1.5) {
// 掉帧检测
mDroppedFrames++;
Log.w("FrameMonitor", "Dropped frame: " +
(frameInterval / 1_000_000) + "ms");
}
}
mLastFrameTimeNanos = frameTimeNanos;
mChoreographer.postFrameCallback(this);
}
};
}
```
### 4. 使用BlockCanary
```gradle
dependencies {
debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
}
```
```java
// 自动检测主线程阻塞
// 超过阈值会弹出通知
```
## 流畅度优化策略
### 1. 减少主线程耗时操作
```java
// ❌ 错误:在主线程做耗时操作
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 同步网络请求
String data = networkRequest(); // 阻塞主线程
updateUI(data);
}
// ✅ 正确:异步处理
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 异步加载
loadDataAsync();
}
private void loadDataAsync() {
new Thread(() -> {
String data = networkRequest();
runOnUiThread(() -> {
updateUI(data);
});
}).start();
}
```
### 2. 优化布局性能
#### 减少布局层级
```xml
<!-- ❌ 错误:嵌套过深 -->
<LinearLayout>
<LinearLayout>
<LinearLayout>
<TextView />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- ✅ 正确使用ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
```
#### 使用include和merge
```xml
<!-- 复用布局 -->
<include layout="@layout/common_header" />
<!-- 减少层级 -->
<merge>
<TextView />
<ImageView />
</merge>
```
#### 避免过度绘制
```java
// 开启过度绘制检测
// 开发者选项 -> 显示过度绘制区域
// 优化方法:
// 1. 移除不必要的背景
view.setBackground(null);
// 2. 使用clipRect裁剪绘制区域
canvas.clipRect(left, top, right, bottom);
// 3. 使用ViewStub延迟加载
```
### 3. 优化自定义View绘制
```java
public class CustomView extends View {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// ❌ 错误在onDraw中创建对象
// Paint paint = new Paint();
// ✅ 正确:复用对象
if (mPaint == null) {
mPaint = new Paint();
}
// 避免在onDraw中做耗时计算
// 预处理数据,缓存结果
}
private Paint mPaint;
}
```
### 4. 使用硬件加速
```xml
<!-- AndroidManifest.xml -->
<application
android:hardwareAccelerated="true">
</application>
```
```java
// 代码中开启
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
// 注意:硬件加速不是万能的
// 某些操作如clipPath在硬件加速下可能更慢
```
### 5. 优化动画性能
```java
// ❌ 错误使用属性动画在复杂View上
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0, 100);
animator.start();
// ✅ 正确使用ViewPropertyAnimator
view.animate()
.translationX(100)
.setDuration(300)
.start();
// ✅ 更优:使用硬件层
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
view.animate()
.translationX(100)
.withEndAction(() -> {
view.setLayerType(View.LAYER_TYPE_NONE, null);
})
.start();
```
### 6. 使用RecyclerView优化列表
```java
// 优化RecyclerView性能
recyclerView.setHasFixedSize(true); // 固定大小
recyclerView.setItemViewCacheSize(20); // 增加缓存
recyclerView.setDrawingCacheEnabled(true);
// 使用DiffUtil更新数据
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffCallback(oldList, newList));
diffResult.dispatchUpdatesTo(adapter);
```
### 7. 避免内存抖动
```java
// ❌ 错误:频繁创建对象
for (int i = 0; i < 1000; i++) {
String str = new String("item" + i); // 每次循环创建新对象
list.add(str);
}
// ✅ 正确复用对象或使用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.setLength(0);
sb.append("item").append(i);
list.add(sb.toString());
}
```
## 掉帧Jank分析
### 掉帧原因
1. **主线程阻塞**
- 同步I/O操作
- 复杂计算
- 同步网络请求
2. **布局复杂**
- 嵌套层级过深
- 过度绘制
- 频繁measure/layout
3. **内存问题**
- 频繁GC
- 内存抖动
- 内存泄漏
4. **动画问题**
- 复杂动画计算
- 未使用硬件加速
- 动画冲突
### 掉帧检测
```java
// 使用Choreographer检测掉帧
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
long currentTime = System.nanoTime();
long frameInterval = currentTime - mLastFrameTime;
if (frameInterval > 16_666_667L * 1.5) {
// 掉帧
int droppedFrames = (int) (frameInterval / 16_666_667L);
Log.w("Jank", "Dropped " + droppedFrames + " frames");
}
mLastFrameTime = currentTime;
Choreographer.getInstance().postFrameCallback(this);
}
});
```
## 性能指标
### 目标值
- **FPS**: 稳定在60fps
- **帧耗时**: < 16.67ms
- **掉帧率**: < 1%
- **ANR率**: < 0.1%
### 测量方法
1. Systrace/Perfetto分析
2. GPU Rendering Profile
3. Choreographer监控
4. BlockCanary检测
## 最佳实践
1. **测量优先**: 先测量,找到瓶颈再优化
2. **避免过度优化**: 不要过早优化
3. **系统化思考**: 从架构层面考虑
4. **持续监控**: 建立性能监控体系
5. **用户感知**: 关注用户可感知的流畅度
## 相关链接
- [[README]]
- [[启动优化方法论]]
- [[内存优化LeakCanary原理]]
- [[09-调试与工具链/Systrace_Perfetto全解读]]

View File

@@ -1 +1,537 @@
# ADB高级命令
## 概述
ADB (Android Debug Bridge) 是Android开发中最常用的命令行工具用于与Android设备通信。本文档详细介绍ADB的高级用法和实用技巧。
## ADB基础
### 安装与配置
```bash
# 检查ADB版本
adb version
# 查看连接的设备
adb devices
# 查看设备详细信息
adb devices -l
```
### 设备连接
```bash
# USB连接
adb devices
# 网络连接需要先USB连接一次
adb tcpip 5555
adb connect <设备IP>:5555
# 断开网络连接
adb disconnect <设备IP>:5555
# 切换回USB模式
adb usb
```
## 日志相关命令
### 1. logcat基础
```bash
# 查看所有日志
adb logcat
# 清空日志缓冲区
adb logcat -c
# 查看指定tag的日志
adb logcat -s TAG_NAME
# 查看多个tag
adb logcat -s TAG1 TAG2
# 过滤日志级别
adb logcat *:E # 只显示Error级别
adb logcat *:W # 只显示Warning及以上
```
### 2. logcat高级用法
```bash
# 按时间格式显示
adb logcat -v time
# 显示进程ID和线程ID
adb logcat -v threadtime
# 显示颜色(需要终端支持)
adb logcat -v color
# 显示所有信息(最详细)
adb logcat -v long
# 保存日志到文件
adb logcat > log.txt
# 实时查看并保存
adb logcat | tee log.txt
# 按包名过滤
adb logcat | grep com.example.app
```
### 3. 日志过滤技巧
```bash
# 使用grep过滤
adb logcat | grep "关键词"
# 排除某些tag
adb logcat | grep -v "TAG_NAME"
# 组合过滤
adb logcat | grep -E "关键词1|关键词2"
# 按时间范围过滤(需要先保存日志)
adb logcat -t '01-01 12:00:00.000'
```
### 4. 日志级别
```bash
# 设置日志级别
adb shell setprop log.tag.TAG_NAME VERBOSE
adb shell setprop log.tag.TAG_NAME DEBUG
adb shell setprop log.tag.TAG_NAME INFO
adb shell setprop log.tag.TAG_NAME WARN
adb shell setprop log.tag.TAG_NAME ERROR
# 查看当前日志级别
adb shell getprop log.tag.TAG_NAME
```
## 系统服务调试
### 1. dumpsys命令
```bash
# 查看所有可用的服务
adb shell dumpsys -l
# 查看Activity信息
adb shell dumpsys activity
# 查看Activity栈
adb shell dumpsys activity activities
# 查看当前Activity
adb shell dumpsys activity top
# 查看Service信息
adb shell dumpsys activity services
# 查看内存信息
adb shell dumpsys meminfo
# 查看指定应用的内存
adb shell dumpsys meminfo com.example.app
# 查看电池信息
adb shell dumpsys batterystats
# 查看网络信息
adb shell dumpsys netstats
# 查看包信息
adb shell dumpsys package com.example.app
# 查看窗口信息
adb shell dumpsys window
# 查看输入法信息
adb shell dumpsys input_method
```
### 2. Activity Manager命令
```bash
# 启动Activity
adb shell am start -n com.example.app/.MainActivity
# 启动Activity并传递数据
adb shell am start -n com.example.app/.MainActivity \
-e key1 value1 -e key2 value2
# 启动Service
adb shell am startservice -n com.example.app/.MyService
# 发送广播
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED
# 强制停止应用
adb shell am force-stop com.example.app
# 杀死进程
adb shell am kill com.example.app
# 启动Activity并测量启动时间
adb shell am start -W -n com.example.app/.MainActivity
```
### 3. Package Manager命令
```bash
# 安装APK
adb install app.apk
# 安装并替换已存在的应用
adb install -r app.apk
# 安装到SD卡
adb install -s app.apk
# 卸载应用
adb uninstall com.example.app
# 保留数据卸载
adb uninstall -k com.example.app
# 查看已安装的包
adb shell pm list packages
# 查看指定应用的包信息
adb shell pm list packages | grep com.example
# 查看包路径
adb shell pm path com.example.app
# 清除应用数据
adb shell pm clear com.example.app
# 启用/禁用组件
adb shell pm enable com.example.app/.MainActivity
adb shell pm disable com.example.app/.MainActivity
```
### 4. 输入事件模拟
```bash
# 点击屏幕坐标
adb shell input tap 500 1000
# 滑动
adb shell input swipe 300 1000 300 500
# 输入文本
adb shell input text "Hello World"
# 按键事件
adb shell input keyevent KEYCODE_HOME
adb shell input keyevent KEYCODE_BACK
adb shell input keyevent KEYCODE_MENU
# 长按
adb shell input swipe 500 1000 500 1000 2000
```
## 文件操作
### 1. 文件传输
```bash
# 推送文件到设备
adb push local_file.txt /sdcard/
# 从设备拉取文件
adb pull /sdcard/file.txt ./
# 查看设备文件
adb shell ls /sdcard/
# 查看文件内容
adb shell cat /sdcard/file.txt
```
### 2. 文件权限
```bash
# 修改文件权限
adb shell chmod 755 /data/local/tmp/script.sh
# 修改文件所有者
adb shell chown system:system /data/local/tmp/file
# 查看文件权限
adb shell ls -l /data/local/tmp/
```
## 性能数据采集
### 1. CPU信息
```bash
# 查看CPU使用率
adb shell top
# 查看指定进程的CPU使用率
adb shell top -p <PID>
# 查看CPU信息
adb shell cat /proc/cpuinfo
# 查看CPU频率
adb shell cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
```
### 2. 内存信息
```bash
# 查看内存使用情况
adb shell dumpsys meminfo
# 查看指定应用的内存
adb shell dumpsys meminfo com.example.app
# 查看系统内存
adb shell cat /proc/meminfo
# 查看进程内存映射
adb shell cat /proc/<PID>/maps
```
### 3. 网络信息
```bash
# 查看网络接口
adb shell ifconfig
# 查看网络连接
adb shell netstat
# 查看路由表
adb shell ip route
# 查看网络统计
adb shell cat /proc/net/sockstat
```
### 4. 电池信息
```bash
# 查看电池状态
adb shell dumpsys battery
# 重置电池统计
adb shell dumpsys batterystats --reset
# 导出电池报告需要配合bugreport
adb bugreport
```
## 系统属性操作
### 1. 查看系统属性
```bash
# 查看所有属性
adb shell getprop
# 查看指定属性
adb shell getprop ro.build.version.sdk
# 查看属性列表(部分)
adb shell getprop | grep "ro\."
```
### 2. 设置系统属性
```bash
# 设置属性需要root
adb shell setprop property_name property_value
# 设置日志级别
adb shell setprop log.tag.TAG_NAME DEBUG
```
## 调试技巧
### 1. 查看进程信息
```bash
# 查看所有进程
adb shell ps
# 查看指定应用的进程
adb shell ps | grep com.example.app
# 查看进程树
adb shell ps -A
# 查看进程详细信息
adb shell ps -ef
```
### 2. 查看线程信息
```bash
# 查看进程的所有线程
adb shell ps -T -p <PID>
# 查看线程堆栈
adb shell kill -3 <PID> # 发送SIGQUIT信号生成堆栈
```
### 3. 网络调试
```bash
# 设置代理
adb shell settings put global http_proxy <proxy_host>:<proxy_port>
# 清除代理
adb shell settings delete global http_proxy
adb shell settings delete global global_http_proxy_host
adb shell settings delete global global_http_proxy_port
# 查看代理设置
adb shell settings get global http_proxy
```
### 4. 权限调试
```bash
# 授予运行时权限
adb shell pm grant com.example.app android.permission.CAMERA
# 撤销权限
adb shell pm revoke com.example.app android.permission.CAMERA
# 查看应用权限
adb shell dumpsys package com.example.app | grep permission
```
## 高级用法
### 1. 执行Shell命令
```bash
# 执行单条命令
adb shell "ls -l /sdcard/"
# 执行多条命令
adb shell "cd /sdcard && ls -l"
# 执行脚本
adb shell sh /sdcard/script.sh
```
### 2. 端口转发
```bash
# 转发端口
adb forward tcp:8080 tcp:8080
# 转发到Unix域套接字
adb forward tcp:8080 local:/tmp/socket
# 查看所有转发
adb forward --list
# 删除转发
adb forward --remove tcp:8080
```
### 3. 无线调试Android 11+
```bash
# 配对设备(首次)
adb pair <IP>:<PORT>
# 输入配对码
# 连接设备
adb connect <IP>:<PORT>
# 查看已连接的设备
adb devices
```
### 4. 多设备管理
```bash
# 指定设备执行命令
adb -s <device_serial> shell ...
# 查看设备序列号
adb devices -l
```
## 实用脚本
### 1. 清理日志并重启
```bash
#!/bin/bash
adb logcat -c
adb shell reboot
```
### 2. 批量安装APK
```bash
#!/bin/bash
for apk in *.apk; do
adb install -r "$apk"
done
```
### 3. 监控应用崩溃
```bash
#!/bin/bash
adb logcat | grep -i "fatal\|exception\|crash"
```
### 4. 性能数据采集
```bash
#!/bin/bash
# 采集CPU、内存、网络数据
adb shell top -n 1 > cpu.txt
adb shell dumpsys meminfo > meminfo.txt
adb shell dumpsys netstats > netstats.txt
```
## 常见问题
### 1. 设备未识别
```bash
# 检查USB连接
adb kill-server
adb start-server
adb devices
```
### 2. 权限不足
```bash
# 某些命令需要root权限
adb root
adb remount
```
### 3. 日志过多
```bash
# 使用过滤器减少日志
adb logcat -c # 清空缓冲区
adb logcat *:E # 只显示错误
```
## 相关链接
- [[README]]
- [[GDB_LLDB调试Native]]
- [[Systrace_Perfetto全解读]]

View File

@@ -1 +1,538 @@
# GDB/LLDB调试Native
## 概述
GDB和LLDB是调试Native代码C/C++的强大工具。在Android开发中特别是Framework层和NDK开发掌握这些调试工具至关重要。
## GDB基础
### GDB简介
GDB (GNU Debugger) 是Linux/Unix系统上最常用的调试器支持C、C++、Java等多种语言。
### 安装GDB
```bash
# Ubuntu/Debian
sudo apt-get install gdb
# 检查版本
gdb --version
```
### 基本用法
```bash
# 启动GDB
gdb program
# 或者附加到运行中的进程
gdb program <PID>
# 加载core文件
gdb program core
```
### 常用命令
```bash
# 运行程序
(gdb) run
(gdb) r
# 设置断点
(gdb) break main
(gdb) break file.c:100
(gdb) break function_name
(gdb) b main # 简写
# 查看断点
(gdb) info breakpoints
(gdb) i b
# 删除断点
(gdb) delete 1
(gdb) d 1
# 继续执行
(gdb) continue
(gdb) c
# 单步执行(进入函数)
(gdb) step
(gdb) s
# 单步执行(不进入函数)
(gdb) next
(gdb) n
# 查看变量
(gdb) print variable_name
(gdb) p variable_name
# 查看变量类型
(gdb) ptype variable_name
# 查看局部变量
(gdb) info locals
(gdb) i locals
# 查看函数参数
(gdb) info args
(gdb) i args
# 查看堆栈
(gdb) backtrace
(gdb) bt
# 切换堆栈帧
(gdb) frame 1
(gdb) f 1
# 查看当前代码
(gdb) list
(gdb) l
# 退出GDB
(gdb) quit
(gdb) q
```
## LLDB基础
### LLDB简介
LLDB是LLVM项目的一部分是macOS和iOS开发中的标准调试器也支持Linux和Android。
### 安装LLDB
```bash
# macOS (Xcode自带)
# 或通过Homebrew
brew install llvm
# 检查版本
lldb --version
```
### 基本用法
```bash
# 启动LLDB
lldb program
# 附加到进程
lldb -p <PID>
# 加载core文件
lldb -c core program
```
### 常用命令
```bash
# 运行程序
(lldb) run
(lldb) r
# 设置断点
(lldb) breakpoint set --name main
(lldb) breakpoint set --file file.c --line 100
(lldb) b main # 简写
# 查看断点
(lldb) breakpoint list
(lldb) br l
# 删除断点
(lldb) breakpoint delete 1
(lldb) br del 1
# 继续执行
(lldb) continue
(lldb) c
# 单步执行(进入函数)
(lldb) step
(lldb) s
# 单步执行(不进入函数)
(lldb) next
(lldb) n
# 查看变量
(lldb) print variable_name
(lldb) p variable_name
# 查看变量类型
(lldb) frame variable
(lldb) fr v
# 查看局部变量
(lldb) frame variable
(lldb) fr v
# 查看堆栈
(lldb) thread backtrace
(lldb) bt
# 切换线程
(lldb) thread select 1
(lldb) th s 1
# 查看当前代码
(lldb) source list
(lldb) l
# 退出LLDB
(lldb) quit
(lldb) q
```
## Android Native调试
### 1. 使用GDB调试Android Native代码
#### 准备工作
```bash
# 1. 确保应用是可调试的
# AndroidManifest.xml中设置 android:debuggable="true"
# 2. 推送gdbserver到设备
adb push $NDK/prebuilt/android-arm64/gdbserver/gdbserver /data/local/tmp/
# 3. 设置权限
adb shell chmod 755 /data/local/tmp/gdbserver
```
#### 启动调试会话
```bash
# 1. 启动应用并获取PID
adb shell ps | grep com.example.app
# 2. 在设备上启动gdbserver
adb shell /data/local/tmp/gdbserver :5039 --attach <PID>
# 3. 端口转发
adb forward tcp:5039 tcp:5039
# 4. 在主机上启动GDB
$NDK/prebuilt/linux-x86_64/bin/gdb
# 5. 在GDB中连接
(gdb) target remote :5039
# 6. 设置符号文件
(gdb) file /path/to/your/library.so
(gdb) set solib-search-path /path/to/symbols
```
### 2. 使用LLDB调试Android Native代码
#### 准备工作
```bash
# 1. 使用lldb-serverAndroid NDK r21+
adb push $NDK/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/*/lib/linux/aarch64/lldb-server /data/local/tmp/
# 2. 设置权限
adb shell chmod 755 /data/local/tmp/lldb-server
```
#### 启动调试会话
```bash
# 1. 启动应用并获取PID
adb shell ps | grep com.example.app
# 2. 在设备上启动lldb-server
adb shell /data/local/tmp/lldb-server platform --server --listen "*:5039" --attach <PID>
# 3. 端口转发
adb forward tcp:5039 tcp:5039
# 4. 在主机上启动LLDB
lldb
# 5. 在LLDB中连接
(lldb) platform select remote-android
(lldb) platform connect connect://localhost:5039
# 6. 加载符号
(lldb) target create /path/to/your/library.so
```
### 3. Android Studio集成调试
Android Studio提供了图形化的Native调试界面
1. **配置调试器**
- Run -> Edit Configurations
- 选择"Debugger"标签
- 选择"Native"调试器类型
2. **设置断点**
- 在Native代码中设置断点
- 支持条件断点
3. **开始调试**
- 点击Debug按钮
- 应用会在断点处暂停
## 高级调试技巧
### 1. 条件断点
```gdb
# GDB
(gdb) break file.c:100 if variable == 5
(gdb) condition 1 variable > 10
```
```lldb
# LLDB
(lldb) breakpoint set --file file.c --line 100 --condition 'variable == 5'
(lldb) breakpoint modify 1 --condition 'variable > 10'
```
### 2. 观察点Watchpoint
```gdb
# GDB - 监控变量变化
(gdb) watch variable_name
(gdb) watch *0x12345678 # 监控内存地址
```
```lldb
# LLDB
(lldb) watchpoint set variable variable_name
(lldb) watchpoint set expression -- 0x12345678
```
### 3. 内存操作
```gdb
# GDB
(gdb) x/10x 0x12345678 # 查看内存16进制10个单位
(gdb) x/10i $pc # 查看指令
(gdb) x/s 0x12345678 # 查看字符串
```
```lldb
# LLDB
(lldb) memory read --format x --count 10 0x12345678
(lldb) memory read --format instruction --count 10 $pc
(lldb) memory read --format cstring 0x12345678
```
### 4. 寄存器操作
```gdb
# GDB
(gdb) info registers # 查看所有寄存器
(gdb) print $rax # 查看特定寄存器
(gdb) set $rax = 0x1234 # 设置寄存器值
```
```lldb
# LLDB
(lldb) register read # 查看所有寄存器
(lldb) register read rax # 查看特定寄存器
(lldb) register write rax 0x1234 # 设置寄存器值
```
### 5. 多线程调试
```gdb
# GDB
(gdb) info threads # 查看所有线程
(gdb) thread 2 # 切换到线程2
(gdb) thread apply all bt # 查看所有线程堆栈
```
```lldb
# LLDB
(lldb) thread list # 查看所有线程
(lldb) thread select 2 # 切换到线程2
(lldb) thread backtrace all # 查看所有线程堆栈
```
## 崩溃分析
### 1. 分析Core Dump
```bash
# 生成core文件
ulimit -c unlimited
./program
# 使用GDB分析
gdb program core
(gdb) bt # 查看堆栈
(gdb) info registers # 查看寄存器
```
### 2. 分析TombstoneAndroid
```bash
# 获取tombstone文件
adb pull /data/tombstones/tombstone_XX
# 使用addr2line解析地址
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-addr2line -e library.so <address>
# 或使用ndk-stack
$NDK/ndk-stack -sym /path/to/symbols -dump tombstone_XX
```
### 3. 使用GDB分析崩溃
```bash
# 1. 获取崩溃时的堆栈
adb shell gdb -p <PID>
(gdb) bt
# 2. 查看寄存器状态
(gdb) info registers
# 3. 查看内存内容
(gdb) x/10x $sp # 查看栈内容
```
## 实战案例
### 案例1调试JNI调用
```java
// Java层
public native void nativeMethod(int value);
```
```cpp
// Native层
JNIEXPORT void JNICALL
Java_com_example_MainActivity_nativeMethod(JNIEnv *env, jobject thiz, jint value) {
// 设置断点
int result = value * 2;
// ...
}
```
**调试步骤**:
1. 在Native方法中设置断点
2. 从Java层调用Native方法
3. 在断点处检查参数和局部变量
### 案例2调试内存泄漏
```cpp
void* allocateMemory(size_t size) {
void* ptr = malloc(size);
// 设置断点,检查分配的内存
return ptr;
}
```
**调试步骤**:
1. 在分配函数中设置断点
2. 使用watchpoint监控指针变量
3. 检查内存是否被正确释放
### 案例3调试多线程问题
```cpp
void* threadFunction(void* arg) {
// 线程函数
int* value = (int*)arg;
*value = 42;
return NULL;
}
```
**调试步骤**:
1. 在多个线程中设置断点
2. 使用`thread apply all bt`查看所有线程状态
3. 检查共享变量的访问
## 调试脚本
### GDB脚本示例
```gdb
# .gdbinit
# 自动加载符号
file /path/to/library.so
set solib-search-path /path/to/symbols
# 定义命令
define print_stack
bt
info registers
x/20x $sp
end
# 自动执行
break main
run
```
### LLDB脚本示例
```python
# .lldbinit
# 定义命令
def print_stack(debugger, command, result, dict):
"""打印堆栈和寄存器"""
debugger.HandleCommand("bt")
debugger.HandleCommand("register read")
debugger.HandleCommand("memory read --format x --count 20 $sp")
# 注册命令
lldb.debugger.HandleCommand("command script add -f print_stack print_stack")
```
## 常见问题
### 1. 符号文件找不到
```bash
# 确保使用带符号的库文件
# 设置符号搜索路径
(gdb) set solib-search-path /path/to/symbols
```
### 2. 无法附加到进程
```bash
# 检查应用是否可调试
adb shell getprop ro.debuggable
# 使用root权限
adb root
```
### 3. 断点不生效
```bash
# 确保代码已加载
(gdb) info sharedlibrary
# 检查断点位置是否正确
(gdb) info breakpoints
```
## 最佳实践
1. **使用符号文件**: 始终使用带调试符号的库文件
2. **记录调试过程**: 保存调试会话和关键信息
3. **自动化脚本**: 编写脚本简化重复操作
4. **版本匹配**: 确保调试器版本与目标匹配
5. **安全考虑**: 生产环境禁用调试功能
## 相关链接
- [[README]]
- [[ADB高级命令]]
- [[Systrace_Perfetto全解读]]

View File

@@ -1 +1,160 @@
# 09-调试与工具链
## 概述
调试与工具链是Android Framework开发中不可或缺的技能。本目录系统性地整理了Android开发中的调试方法、工具使用和自定义工具开发。
## 目录结构
### 1. ADB高级命令
- ADB常用命令详解
- 系统服务调试
- 日志分析技巧
- 性能数据采集
### 2. GDB/LLDB调试Native
- Native代码调试方法
- GDB/LLDB使用技巧
- 断点与内存分析
- 崩溃堆栈分析
### 3. Systrace/Perfetto全解读
- 系统级性能分析
- Trace文件解析
- 性能瓶颈定位
- 优化建议生成
### 4. 自定义调试工具开发
- 调试工具设计思路
- 工具开发实践
- 工具集成方法
## 调试方法论
### 1. 问题定位流程
```
问题现象
收集日志
分析堆栈
定位代码
复现问题
修复验证
```
### 2. 调试原则
- **系统性思考**: 从整体到局部
- **数据驱动**: 用数据说话,不要猜测
- **工具辅助**: 善用各种调试工具
- **持续学习**: 掌握新工具和方法
### 3. 日志策略
- **分级记录**: 使用合适的日志级别
- **关键路径**: 在关键路径添加日志
- **性能考虑**: 避免日志影响性能
- **隐私保护**: 注意敏感信息脱敏
## 工具分类
### 官方工具
#### Android Studio
- **Debugger**: Java/Kotlin代码调试
- **Profiler**: 性能分析
- **Layout Inspector**: 布局分析
- **Network Inspector**: 网络分析
#### ADB (Android Debug Bridge)
- 设备连接与管理
- 日志查看与分析
- 系统服务调试
- 性能数据采集
#### Systrace/Perfetto
- 系统级性能分析
- 帧率分析
- CPU调度分析
- 系统调用追踪
### 第三方工具
#### LeakCanary
- 内存泄漏检测
- 自动分析泄漏路径
#### BlockCanary
- 主线程阻塞检测
- ANR预警
#### Stetho
- Chrome DevTools集成
- 网络请求查看
- 数据库查看
### 自定义工具
- 根据项目需求定制
- 集成到开发流程
- 自动化测试与验证
## 调试场景
### 1. 崩溃调试
- 收集崩溃堆栈
- 分析崩溃原因
- 复现与修复
### 2. 性能调试
- 定位性能瓶颈
- 分析耗时操作
- 优化验证
### 3. 内存调试
- 内存泄漏检测
- 内存占用分析
- GC分析
### 4. 网络调试
- 请求响应分析
- 网络性能优化
- 错误排查
### 5. UI调试
- 布局问题定位
- 渲染性能分析
- 动画调试
## 最佳实践
### 1. 建立调试环境
- 配置开发环境
- 准备测试设备
- 设置日志收集
### 2. 使用版本控制
- 记录调试过程
- 保存关键日志
- 文档化问题
### 3. 自动化测试
- 单元测试
- 集成测试
- 性能测试
### 4. 持续监控
- 线上监控
- 性能指标
- 异常告警
## 相关链接
- [[06-性能优化体系]]
- [[05-进程与线程通信]]
- [[MOCs/Android Framework知识体系图]]

View File

@@ -1 +1,419 @@
# Systrace/Perfetto全解读
## 概述
Systrace和Perfetto是Android系统级性能分析工具可以追踪系统调用、CPU调度、渲染流程等是性能优化的必备工具。
## Systrace基础
### Systrace简介
Systrace是Android 4.1引入的系统级追踪工具可以记录系统调用、CPU调度、渲染等信息。
### 安装与使用
```bash
# Systrace位于Android SDK的platform-tools/systrace目录
# 或使用Python脚本
python systrace.py [options] [categories]
# 基本用法
python systrace.py -t 10 -o trace.html sched gfx view
```
### 常用参数
```bash
# -t: 追踪时间(秒)
python systrace.py -t 10 -o trace.html
# -o: 输出文件
python systrace.py -t 10 -o my_trace.html
# -b: 缓冲区大小KB
python systrace.py -t 10 -b 32768 -o trace.html
# -a: 指定应用包名
python systrace.py -t 10 -a com.example.app -o trace.html
# -k: 指定要追踪的函数(用逗号分隔)
python systrace.py -t 10 -k load_symbols,unload_symbols -o trace.html
```
### 追踪类别Categories
```bash
# CPU调度
sched
# 图形渲染
gfx
# 视图系统
view
# 输入事件
input
# 磁盘I/O
disk
# 内存
mem
# 活动管理器
am
# 窗口管理器
wm
# 数据库
db
# 网络
network
# 电源管理
power
# 全部类别
-a
```
### 完整示例
```bash
# 追踪应用启动
python systrace.py -t 5 -a com.example.app \
-o startup_trace.html \
sched freq idle am wm gfx view binder_driver hal dalvik camera input res
# 追踪流畅度问题
python systrace.py -t 10 \
-o jank_trace.html \
gfx view sched freq idle
# 追踪内存问题
python systrace.py -t 10 \
-o memory_trace.html \
sched freq idle mem
```
## Perfetto基础
### Perfetto简介
Perfetto是Google开发的下一代性能分析工具从Android 9开始集成功能更强大支持更长的追踪时间。
### 使用Perfetto
#### 1. 通过Android Studio
1. 打开Android Studio
2. 连接设备
3. 打开Profiler
4. 选择CPU Profiler
5. 点击"Record"开始录制
6. 执行操作
7. 停止录制,查看结果
#### 2. 通过命令行
```bash
# 使用perfetto命令行工具
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace
# 或使用配置文件
adb shell perfetto -c /data/local/tmp/config.pb -o /data/local/tmp/trace.pb
```
#### 3. 通过Web UI
```bash
# 1. 录制trace
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace
# 2. 拉取trace文件
adb pull /data/misc/perfetto-traces/trace trace.pb
# 3. 在 https://ui.perfetto.dev/ 打开
```
### Perfetto配置文件
```protobuf
# config.pb ()
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
buffers: {
size_kb: 2048
fill_policy: DISCARD
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_waking"
ftrace_events: "power/suspend_resume"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "gfx/mali_gpu_total"
buffer_size_kb: 2048
drain_period_ms: 250
}
}
}
duration_ms: 10000
```
## Trace文件分析
### 1. 打开Trace文件
```bash
# Systrace HTML文件
# 直接在浏览器中打开 trace.html
# Perfetto文件
# 在 https://ui.perfetto.dev/ 打开
# 或使用Android Studio打开
```
### 2. 关键指标
#### Frame信息
- **绿色**: 正常帧(< 16.67ms
- **黄色**: 轻微掉帧16.67-33.33ms
- **红色**: 严重掉帧(> 33.33ms
#### CPU信息
- **CPU频率**: 查看CPU是否降频
- **CPU使用率**: 查看CPU负载
- **CPU调度**: 查看线程调度情况
#### 渲染信息
- **VSYNC**: 垂直同步信号
- **Choreographer**: 帧调度
- **RenderThread**: 渲染线程
- **GPU**: GPU渲染时间
### 3. 分析技巧
#### 查找卡顿
1. 找到红色或黄色的Frame
2. 点击Frame查看详细信息
3. 查看该时间段内的CPU活动
4. 查找耗时操作
#### 分析启动时间
1. 找到应用启动的起点
2. 追踪到首帧渲染完成
3. 分析各个阶段的耗时
4. 识别瓶颈
#### 分析内存问题
1. 查看内存分配事件
2. 查找频繁的GC
3. 分析内存增长趋势
## 实战案例
### 案例1分析应用启动
```bash
# 1. 录制启动trace
python systrace.py -t 5 -a com.example.app \
-o startup.html \
sched freq idle am wm gfx view
# 2. 分析步骤
# - 找到Application.onCreate开始时间
# - 找到MainActivity.onCreate开始时间
# - 找到首帧渲染完成时间
# - 计算各阶段耗时
```
**关键指标**:
- Application初始化时间
- Activity创建时间
- 布局inflate时间
- 首帧渲染时间
### 案例2分析流畅度问题
```bash
# 1. 录制流畅度trace
python systrace.py -t 10 \
-o jank.html \
gfx view sched freq idle
# 2. 分析步骤
# - 找到掉帧的Frame红色/黄色)
# - 查看该Frame的耗时
# - 分析主线程活动
# - 查找阻塞操作
```
**常见问题**:
- 主线程阻塞
- 布局复杂
- 过度绘制
- 内存抖动
### 案例3分析CPU使用率
```bash
# 1. 录制CPU trace
python systrace.py -t 10 \
-o cpu.html \
sched freq idle
# 2. 分析步骤
# - 查看CPU频率变化
# - 查看CPU使用率
# - 分析线程调度
# - 查找CPU密集型操作
```
## 高级技巧
### 1. 自定义Trace点
```java
// 在代码中添加自定义trace点
import android.os.Trace;
// 开始trace
Trace.beginSection("my_custom_section");
// 执行操作
doSomething();
// 结束trace
Trace.endSection();
```
### 2. 异步Trace
```java
// 异步trace
Trace.beginAsyncSection("async_operation", cookie);
// 执行异步操作
Trace.endAsyncSection("async_operation", cookie);
```
### 3. 计数器
```java
// 设置计数器
Trace.setCounter("my_counter", value);
```
### 4. 使用ATrace命令
```bash
# 在shell中启用trace
adb shell setprop debug.atrace.tags.enableflags 0x1
# 开始trace
adb shell atrace -t 10 -b 32768 gfx view sched > trace.txt
# 停止trace
adb shell atrace --async_stop
```
## Perfetto高级功能
### 1. 长时间追踪
```bash
# Perfetto支持更长的追踪时间
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace
# 可以追踪数小时
```
### 2. 多数据源
Perfetto支持同时追踪多个数据源
- CPU调度
- 内存分配
- 网络活动
- 电源管理
- 自定义事件
### 3. SQL查询
Perfetto支持SQL查询trace数据
```sql
-- 查询所有Frame信息
SELECT * FROM slice WHERE name = 'Choreographer#doFrame';
-- 查询掉帧
SELECT * FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16666667;
-- 查询CPU使用率
SELECT ts, cpu, value
FROM counter
WHERE name = 'cpu.freq';
```
## 性能优化建议
### 1. 基于Trace的优化
1. **识别瓶颈**: 找到耗时最长的操作
2. **分析原因**: 理解为什么耗时
3. **制定方案**: 设计优化策略
4. **验证效果**: 再次录制trace对比
### 2. 常见优化点
- **减少主线程工作**: 将耗时操作移到后台线程
- **优化布局**: 减少布局层级和复杂度
- **减少GC**: 避免内存抖动
- **优化算法**: 使用更高效的算法
### 3. 持续监控
- 建立性能基线
- 定期录制trace
- 设置性能告警
- 跟踪性能趋势
## 工具对比
| 特性 | Systrace | Perfetto |
|------|----------|----------|
| 支持版本 | Android 4.1+ | Android 9+ |
| 追踪时间 | 较短 | 较长 |
| 数据源 | 有限 | 丰富 |
| 分析能力 | 基础 | 强大 |
| SQL查询 | 不支持 | 支持 |
| Web UI | 基础 | 强大 |
## 最佳实践
1. **明确目标**: 在录制前明确要分析的问题
2. **合适时长**: 选择适当的追踪时间
3. **关键类别**: 只追踪相关的类别
4. **多次录制**: 多次录制确保结果一致
5. **对比分析**: 优化前后对比分析
## 相关链接
- [[README]]
- [[06-性能优化体系/流畅度Choreographer+VSYNC]]
- [[06-性能优化体系/启动优化方法论]]

View File

@@ -1 +1,538 @@
# 自定义调试工具开发
## 概述
在Android开发中有时需要开发自定义的调试工具来满足特定需求。本文档介绍如何设计和开发自定义调试工具。
## 工具设计原则
### 1. 明确需求
在开发工具前,需要明确:
- **解决什么问题**: 工具要解决的具体问题
- **目标用户**: 谁将使用这个工具
- **使用场景**: 在什么情况下使用
- **性能要求**: 对性能的影响要求
### 2. 设计原则
- **易用性**: 界面简洁,操作简单
- **可靠性**: 稳定可靠,不影响应用运行
- **可扩展性**: 便于后续扩展功能
- **性能**: 对应用性能影响最小
### 3. 技术选型
- **语言**: Java/Kotlin/C++
- **框架**: 根据需求选择
- **UI**: 命令行/图形界面/Web界面
- **部署**: 独立应用/集成到应用/插件
## 工具类型
### 1. 性能监控工具
#### 功能需求
- CPU使用率监控
- 内存使用监控
- 帧率监控
- 网络请求监控
#### 实现示例
```java
public class PerformanceMonitor {
private static PerformanceMonitor sInstance;
private Handler mHandler;
private boolean mMonitoring = false;
public static PerformanceMonitor getInstance() {
if (sInstance == null) {
sInstance = new PerformanceMonitor();
}
return sInstance;
}
public void startMonitoring() {
if (mMonitoring) {
return;
}
mMonitoring = true;
mHandler = new Handler(Looper.getMainLooper());
mHandler.post(mMonitorRunnable);
}
public void stopMonitoring() {
mMonitoring = false;
if (mHandler != null) {
mHandler.removeCallbacks(mMonitorRunnable);
}
}
private Runnable mMonitorRunnable = new Runnable() {
@Override
public void run() {
if (!mMonitoring) {
return;
}
// 收集性能数据
collectPerformanceData();
// 1秒后再次执行
mHandler.postDelayed(this, 1000);
}
};
private void collectPerformanceData() {
// CPU使用率
float cpuUsage = getCpuUsage();
// 内存使用
long memoryUsage = getMemoryUsage();
// 帧率
float fps = getFPS();
// 记录数据
Log.d("Performance", String.format(
"CPU: %.2f%%, Memory: %dMB, FPS: %.2f",
cpuUsage, memoryUsage / 1024 / 1024, fps
));
}
private float getCpuUsage() {
// 实现CPU使用率计算
// 通过/proc/stat计算
return 0.0f;
}
private long getMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
return runtime.totalMemory() - runtime.freeMemory();
}
private float getFPS() {
// 使用Choreographer计算FPS
return 0.0f;
}
}
```
### 2. 日志收集工具
#### 功能需求
- 自动收集日志
- 日志分类存储
- 日志上传
- 日志分析
#### 实现示例
```java
public class LogCollector {
private static final String LOG_DIR = "logs";
private File mLogDir;
private File mCurrentLogFile;
public LogCollector(Context context) {
mLogDir = new File(context.getFilesDir(), LOG_DIR);
if (!mLogDir.exists()) {
mLogDir.mkdirs();
}
}
public void startCollecting() {
// 创建新的日志文件
String fileName = "log_" + System.currentTimeMillis() + ".txt";
mCurrentLogFile = new File(mLogDir, fileName);
// 启动日志收集线程
new Thread(() -> {
collectLogs();
}).start();
}
private void collectLogs() {
try {
Process process = Runtime.getRuntime().exec("logcat");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
FileWriter writer = new FileWriter(mCurrentLogFile);
String line;
while ((line = reader.readLine()) != null) {
// 过滤和格式化日志
if (shouldCollect(line)) {
writer.write(line + "\n");
writer.flush();
}
}
writer.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean shouldCollect(String line) {
// 实现日志过滤逻辑
return line.contains("MyApp");
}
public void uploadLogs(String url) {
// 实现日志上传逻辑
// 可以使用OkHttp等网络库
}
}
```
### 3. 内存分析工具
#### 功能需求
- 内存快照
- 内存泄漏检测
- 内存使用分析
- 内存报告生成
#### 实现示例
```java
public class MemoryAnalyzer {
private ActivityManager mActivityManager;
public MemoryAnalyzer(Context context) {
mActivityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
}
public MemoryInfo getMemoryInfo() {
ActivityManager.MemoryInfo memInfo =
new ActivityManager.MemoryInfo();
mActivityManager.getMemoryInfo(memInfo);
return new MemoryInfo(
memInfo.totalMem,
memInfo.availMem,
memInfo.threshold,
memInfo.lowMemory
);
}
public void dumpHeap(String fileName) {
try {
Debug.dumpHprofData(fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
public void analyzeMemoryLeak() {
// 实现内存泄漏检测逻辑
// 可以集成LeakCanary
}
public static class MemoryInfo {
public long totalMem;
public long availMem;
public long threshold;
public boolean lowMemory;
public MemoryInfo(long total, long avail,
long threshold, boolean low) {
this.totalMem = total;
this.availMem = avail;
this.threshold = threshold;
this.lowMemory = low;
}
}
}
```
### 4. 网络监控工具
#### 功能需求
- 网络请求拦截
- 请求响应记录
- 网络性能分析
- 请求重放
#### 实现示例
```java
public class NetworkMonitor {
private List<NetworkRequest> mRequests = new ArrayList<>();
private boolean mMonitoring = false;
public void startMonitoring() {
mMonitoring = true;
// 使用OkHttp Interceptor拦截请求
}
public void stopMonitoring() {
mMonitoring = false;
}
public void addRequest(NetworkRequest request) {
if (mMonitoring) {
mRequests.add(request);
}
}
public List<NetworkRequest> getRequests() {
return new ArrayList<>(mRequests);
}
public void exportRequests(String fileName) {
// 导出请求记录
try {
FileWriter writer = new FileWriter(fileName);
for (NetworkRequest request : mRequests) {
writer.write(request.toString() + "\n");
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static class NetworkRequest {
public String url;
public String method;
public Map<String, String> headers;
public String requestBody;
public String responseBody;
public long duration;
public int statusCode;
}
}
```
## 工具集成方式
### 1. 独立应用
```java
// 作为独立的调试应用
public class DebugApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// 初始化调试工具
DebugTools.init(this);
}
}
```
**优点**:
- 不影响主应用
- 可以独立更新
- 便于分发
**缺点**:
- 需要单独安装
- 可能无法访问主应用数据
### 2. 集成到应用
```java
// 在主应用中集成
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
// 只在Debug版本启用
DebugTools.init(this);
}
}
}
```
**优点**:
- 直接访问应用数据
- 无需单独安装
- 集成度高
**缺点**:
- 可能影响应用性能
- 需要控制发布版本
### 3. 插件化
```java
// 使用插件机制
public class DebugPlugin {
public void load(Context context) {
// 动态加载调试功能
}
public void unload() {
// 卸载调试功能
}
}
```
**优点**:
- 灵活加载/卸载
- 不影响主应用
- 便于扩展
**缺点**:
- 实现复杂
- 需要插件框架支持
## UI设计
### 1. 命令行界面
```java
public class DebugCLI {
public void start() {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Debug> ");
String command = scanner.nextLine();
executeCommand(command);
}
}
private void executeCommand(String command) {
String[] parts = command.split(" ");
String cmd = parts[0];
switch (cmd) {
case "memory":
showMemoryInfo();
break;
case "cpu":
showCpuInfo();
break;
case "fps":
showFPS();
break;
case "help":
showHelp();
break;
default:
System.out.println("Unknown command: " + cmd);
}
}
}
```
### 2. 图形界面
```java
// 使用Android原生UI
public class DebugActivity extends AppCompatActivity {
private TextView mMemoryText;
private TextView mCpuText;
private TextView mFpsText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_debug);
mMemoryText = findViewById(R.id.memory_text);
mCpuText = findViewById(R.id.cpu_text);
mFpsText = findViewById(R.id.fps_text);
startMonitoring();
}
private void startMonitoring() {
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
updateUI();
handler.postDelayed(this, 1000);
}
});
}
private void updateUI() {
// 更新UI显示
mMemoryText.setText(getMemoryInfo());
mCpuText.setText(getCpuInfo());
mFpsText.setText(getFpsInfo());
}
}
```
### 3. Web界面
```java
// 使用WebView或HTTP服务器
public class DebugWebServer {
private HttpServer mServer;
public void start(int port) {
try {
mServer = HttpServer.create(new InetSocketAddress(port), 0);
mServer.createContext("/", new DebugHandler());
mServer.setExecutor(null);
mServer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
private class DebugHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = generateResponse();
exchange.sendResponseHeaders(200, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
```
## 工具发布
### 1. 版本管理
```gradle
// build.gradle
android {
defaultConfig {
versionCode 1
versionName "1.0.0"
}
}
```
### 2. 文档编写
- **使用说明**: 如何安装和使用
- **功能说明**: 功能介绍
- **常见问题**: FAQ
- **更新日志**: 版本更新记录
### 3. 分发方式
- **内部使用**: 通过内部渠道分发
- **GitHub**: 开源发布
- **应用商店**: 发布到应用商店
## 最佳实践
1. **性能优先**: 确保工具不影响应用性能
2. **易于使用**: 界面简洁,操作直观
3. **稳定可靠**: 充分测试,确保稳定
4. **文档完善**: 提供详细的使用文档
5. **持续维护**: 根据反馈持续改进
## 相关链接
- [[README]]
- [[ADB高级命令]]
- [[06-性能优化体系]]

View File

@@ -0,0 +1,440 @@
# 效率工具推荐
## 概述
本文档收集了Android Framework开发中常用的效率工具包括开发工具、调试工具、性能分析工具等。
## 开发工具
### 1. Android Studio
**简介**: Google官方Android开发IDE
**主要功能**:
- 代码编辑与智能提示
- 调试器Java/Kotlin/Native
- Profiler性能分析
- Layout Inspector布局分析
- Network Inspector网络分析
**下载地址**: https://developer.android.com/studio
**推荐理由**:
- 官方支持,功能完善
- 集成多种调试工具
- 持续更新
### 2. IntelliJ IDEA
**简介**: JetBrains开发的Java IDE
**主要功能**:
- 强大的代码分析
- 丰富的插件生态
- 优秀的重构工具
**适用场景**:
- 大型项目开发
- 需要深度代码分析
**下载地址**: https://www.jetbrains.com/idea/
### 3. VS Code
**简介**: 轻量级代码编辑器
**主要功能**:
- 轻量快速
- 丰富的扩展
- 多语言支持
**适用场景**:
- 快速编辑
- 多语言项目
- 脚本编写
**下载地址**: https://code.visualstudio.com/
## 调试工具
### 1. LeakCanary
**简介**: Square开源的内存泄漏检测工具
**主要功能**:
- 自动检测内存泄漏
- 可视化泄漏路径
- 集成简单
**使用方式**:
```gradle
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
```
**推荐理由**:
- 自动检测,无需手动操作
- 提供详细的泄漏信息
- 社区活跃
**GitHub**: https://github.com/square/leakcanary
### 2. BlockCanary
**简介**: 主线程阻塞检测工具
**主要功能**:
- 检测主线程阻塞
- ANR预警
- 性能监控
**使用方式**:
```gradle
dependencies {
debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
}
```
**推荐理由**:
- 及时发现性能问题
- 提供详细的阻塞信息
**GitHub**: https://github.com/markzhai/AndroidPerformanceMonitor
### 3. Stetho
**简介**: Facebook开发的Android调试工具
**主要功能**:
- Chrome DevTools集成
- 网络请求查看
- 数据库查看
- SharedPreferences查看
**使用方式**:
```gradle
dependencies {
debugImplementation 'com.facebook.stetho:stetho:1.6.0'
}
```
**推荐理由**:
- 使用Chrome DevTools界面友好
- 功能丰富
**GitHub**: https://github.com/facebook/stetho
## 性能分析工具
### 1. Systrace/Perfetto
**简介**: Google官方系统级性能分析工具
**主要功能**:
- 系统调用追踪
- CPU调度分析
- 渲染流程分析
- 帧率分析
**使用方式**:
```bash
python systrace.py -t 10 -o trace.html sched gfx view
```
**推荐理由**:
- 官方工具,功能强大
- 系统级分析能力
**文档**: [[09-调试与工具链/Systrace_Perfetto全解读]]
### 2. Android Profiler
**简介**: Android Studio内置性能分析工具
**主要功能**:
- CPU分析
- 内存分析
- 网络分析
- 功耗分析
**推荐理由**:
- 集成在IDE中使用方便
- 实时监控
### 3. Battery Historian
**简介**: Google官方功耗分析工具
**主要功能**:
- 电量消耗分析
- Wake Lock分析
- 网络使用分析
**使用方式**:
```bash
go get -u github.com/google/battery-historian
```
**推荐理由**:
- 官方工具
- 详细的功耗分析
**GitHub**: https://github.com/google/battery-historian
## 代码质量工具
### 1. Lint
**简介**: Android官方代码检查工具
**主要功能**:
- 代码规范检查
- 性能问题检测
- 安全问题检测
**使用方式**:
```bash
./gradlew lint
```
**推荐理由**:
- 官方工具
- 集成在构建流程中
### 2. FindBugs/SpotBugs
**简介**: Java代码静态分析工具
**主要功能**:
- Bug检测
- 代码质量分析
**推荐理由**:
- 检测能力强
- 社区活跃
### 3. SonarQube
**简介**: 代码质量管理平台
**主要功能**:
- 代码质量分析
- 技术债务管理
- 持续集成
**推荐理由**:
- 功能全面
- 支持多种语言
## 版本控制工具
### 1. Git
**简介**: 分布式版本控制系统
**主要功能**:
- 版本管理
- 分支管理
- 代码合并
**推荐理由**:
- 行业标准
- 功能强大
**文档**: [[Obsidian/git]]
### 2. SourceTree
**简介**: Git图形化客户端
**主要功能**:
- 可视化Git操作
- 分支管理
- 代码对比
**推荐理由**:
- 界面友好
- 操作直观
### 3. GitKraken
**简介**: 现代化的Git客户端
**主要功能**:
- 可视化提交历史
- 分支管理
- 代码对比
**推荐理由**:
- 界面美观
- 功能丰富
## 文档工具
### 1. Markdown编辑器
#### Typora
- 所见即所得
- 支持数学公式
- 界面简洁
#### Obsidian
- 知识管理
- 双向链接
- 插件丰富
#### VS Code + Markdown插件
- 轻量级
- 扩展丰富
### 2. 文档生成工具
#### MkDocs
- 基于Markdown
- 主题丰富
- 部署简单
#### Doxygen
- 代码文档生成
- 支持多种语言
## 效率提升工具
### 1. Alfred (macOS)
**简介**: 效率启动器
**主要功能**:
- 快速启动应用
- 文件搜索
- 自定义工作流
**推荐理由**:
- 大幅提升操作效率
- 可定制性强
### 2. Everything (Windows)
**简介**: 文件搜索工具
**主要功能**:
- 极速文件搜索
- 支持正则表达式
**推荐理由**:
- 搜索速度快
- 资源占用低
### 3. Ditto (Windows)
**简介**: 剪贴板管理工具
**主要功能**:
- 剪贴板历史
- 多剪贴板管理
**推荐理由**:
- 提高复制粘贴效率
## 网络工具
### 1. Charles
**简介**: HTTP代理工具
**主要功能**:
- 网络请求拦截
- 请求修改
- 性能分析
**推荐理由**:
- 功能强大
- 界面友好
### 2. Postman
**简介**: API测试工具
**主要功能**:
- API测试
- 请求集合管理
- 自动化测试
**推荐理由**:
- 功能全面
- 团队协作
### 3. Wireshark
**简介**: 网络协议分析工具
**主要功能**:
- 网络包捕获
- 协议分析
- 流量分析
**推荐理由**:
- 功能强大
- 专业级工具
## 系统工具
### 1. iTerm2 (macOS)
**简介**: 终端替代工具
**主要功能**:
- 分屏
- 标签页
- 主题定制
**推荐理由**:
- 功能强大
- 可定制性强
### 2. Windows Terminal
**简介**: Windows现代终端
**主要功能**:
- 多标签页
- 主题定制
- 集成PowerShell/CMD
**推荐理由**:
- 官方支持
- 现代化界面
### 3. tmux
**简介**: 终端复用器
**主要功能**:
- 会话管理
- 窗口分割
- 远程会话保持
**推荐理由**:
- 提高终端使用效率
- 支持远程开发
## 推荐配置
### Android开发环境
1. **IDE**: Android Studio
2. **版本控制**: Git + SourceTree
3. **调试工具**: LeakCanary + BlockCanary + Stetho
4. **性能分析**: Android Profiler + Systrace
5. **文档**: Obsidian + MkDocs
### 效率工具组合
1. **macOS**: Alfred + iTerm2 + tmux
2. **Windows**: Everything + Ditto + Windows Terminal
## 相关链接
- [[09-调试与工具链]]
- [[脚本库/README]]

View File

@@ -0,0 +1,565 @@
# 脚本库
## 概述
本文档收集了Android开发中常用的脚本包括构建脚本、自动化脚本、工具脚本等。
## 构建脚本
### 1. Gradle构建脚本
#### 通用构建配置
```gradle
// build.gradle (Project)
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
```
#### 应用构建配置
```gradle
// build.gradle (App)
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.example.app"
minSdkVersion 21
targetSdkVersion 33
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}
```
### 2. 构建优化脚本
```gradle
// 并行构建
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
// 增加内存
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m
```
## 自动化脚本
### 1. 批量安装APK
```bash
#!/bin/bash
# install_apks.sh
# 批量安装当前目录下的所有APK
for apk in *.apk; do
if [ -f "$apk" ]; then
echo "Installing $apk..."
adb install -r "$apk"
if [ $? -eq 0 ]; then
echo "Successfully installed $apk"
else
echo "Failed to install $apk"
fi
fi
done
```
### 2. 清理并重启
```bash
#!/bin/bash
# clean_and_reboot.sh
# 清理日志并重启设备
echo "Clearing logcat..."
adb logcat -c
echo "Clearing app data..."
adb shell pm clear com.example.app
echo "Rebooting device..."
adb reboot
echo "Waiting for device..."
adb wait-for-device
echo "Device is ready!"
```
### 3. 性能数据采集
```bash
#!/bin/bash
# collect_performance.sh
# 采集性能数据
OUTPUT_DIR="performance_data_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"
echo "Collecting CPU info..."
adb shell top -n 1 > "$OUTPUT_DIR/cpu.txt"
echo "Collecting memory info..."
adb shell dumpsys meminfo > "$OUTPUT_DIR/meminfo.txt"
echo "Collecting battery info..."
adb shell dumpsys batterystats > "$OUTPUT_DIR/batterystats.txt"
echo "Collecting network stats..."
adb shell dumpsys netstats > "$OUTPUT_DIR/netstats.txt"
echo "Data collected in $OUTPUT_DIR"
```
### 4. 日志收集脚本
```bash
#!/bin/bash
# collect_logs.sh
# 收集并过滤日志
PACKAGE_NAME="com.example.app"
OUTPUT_FILE="logs_$(date +%Y%m%d_%H%M%S).txt"
echo "Collecting logs for $PACKAGE_NAME..."
adb logcat | grep "$PACKAGE_NAME" > "$OUTPUT_FILE" &
LOGCAT_PID=$!
# 运行30秒
sleep 30
# 停止日志收集
kill $LOGCAT_PID
echo "Logs saved to $OUTPUT_FILE"
```
## ADB工具脚本
### 1. 设备管理脚本
```bash
#!/bin/bash
# device_manager.sh
# 设备管理工具
function list_devices() {
echo "Connected devices:"
adb devices -l
}
function get_device_info() {
echo "Device Info:"
echo "Model: $(adb shell getprop ro.product.model)"
echo "Android Version: $(adb shell getprop ro.build.version.release)"
echo "SDK Version: $(adb shell getprop ro.build.version.sdk)"
echo "Manufacturer: $(adb shell getprop ro.product.manufacturer)"
}
function install_apk() {
if [ -z "$1" ]; then
echo "Usage: install_apk <apk_file>"
return 1
fi
adb install -r "$1"
}
case "$1" in
list)
list_devices
;;
info)
get_device_info
;;
install)
install_apk "$2"
;;
*)
echo "Usage: $0 {list|info|install}"
exit 1
;;
esac
```
### 2. 应用管理脚本
```bash
#!/bin/bash
# app_manager.sh
# 应用管理工具
PACKAGE_NAME="$2"
function stop_app() {
adb shell am force-stop "$PACKAGE_NAME"
echo "Stopped $PACKAGE_NAME"
}
function clear_data() {
adb shell pm clear "$PACKAGE_NAME"
echo "Cleared data for $PACKAGE_NAME"
}
function uninstall_app() {
adb uninstall "$PACKAGE_NAME"
echo "Uninstalled $PACKAGE_NAME"
}
function get_app_info() {
adb shell dumpsys package "$PACKAGE_NAME" | grep -E "versionName|versionCode"
}
case "$1" in
stop)
stop_app
;;
clear)
clear_data
;;
uninstall)
uninstall_app
;;
info)
get_app_info
;;
*)
echo "Usage: $0 {stop|clear|uninstall|info} <package_name>"
exit 1
;;
esac
```
## Python工具脚本
### 1. APK分析脚本
```python
#!/usr/bin/env python3
# apk_analyzer.py
# APK信息分析工具
import subprocess
import sys
import re
def get_apk_info(apk_path):
"""获取APK信息"""
try:
# 使用aapt获取信息
result = subprocess.run(
['aapt', 'dump', 'badging', apk_path],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"Error: {result.stderr}")
return None
info = {}
for line in result.stdout.split('\n'):
if 'package:' in line:
# 解析包名和版本
package_match = re.search(r"name='([^']+)'", line)
version_match = re.search(r"versionCode='(\d+)'", line)
version_name_match = re.search(r"versionName='([^']+)'", line)
if package_match:
info['package'] = package_match.group(1)
if version_match:
info['version_code'] = version_match.group(1)
if version_name_match:
info['version_name'] = version_name_match.group(1)
if 'application-label:' in line:
label_match = re.search(r"label='([^']+)'", line)
if label_match:
info['label'] = label_match.group(1)
return info
except Exception as e:
print(f"Error: {e}")
return None
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python apk_analyzer.py <apk_path>")
sys.exit(1)
apk_path = sys.argv[1]
info = get_apk_info(apk_path)
if info:
print("APK Information:")
for key, value in info.items():
print(f" {key}: {value}")
```
### 2. 日志分析脚本
```python
#!/usr/bin/env python3
# log_analyzer.py
# 日志分析工具
import re
import sys
from collections import Counter
def analyze_logs(log_file):
"""分析日志文件"""
errors = []
warnings = []
patterns = {
'crash': r'FATAL|crash|exception',
'anr': r'ANR|Application Not Responding',
'oom': r'OutOfMemory|OOM',
}
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
for line_num, line in enumerate(f, 1):
# 检查错误
if 'E/' in line or 'ERROR' in line.upper():
errors.append((line_num, line.strip()))
# 检查警告
if 'W/' in line or 'WARN' in line.upper():
warnings.append((line_num, line.strip()))
# 检查特定模式
for pattern_name, pattern in patterns.items():
if re.search(pattern, line, re.IGNORECASE):
print(f"[{pattern_name.upper()}] Line {line_num}: {line.strip()}")
print(f"\nSummary:")
print(f" Errors: {len(errors)}")
print(f" Warnings: {len(warnings)}")
if errors:
print(f"\nFirst 10 errors:")
for line_num, line in errors[:10]:
print(f" Line {line_num}: {line}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python log_analyzer.py <log_file>")
sys.exit(1)
log_file = sys.argv[1]
analyze_logs(log_file)
```
## Git工具脚本
### 1. 批量提交脚本
```bash
#!/bin/bash
# batch_commit.sh
# 批量提交更改
COMMIT_MESSAGE="$1"
if [ -z "$COMMIT_MESSAGE" ]; then
echo "Usage: batch_commit.sh <commit_message>"
exit 1
fi
# 添加所有更改
git add .
# 提交
git commit -m "$COMMIT_MESSAGE"
# 推送到远程
read -p "Push to remote? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git push
fi
```
### 2. 分支清理脚本
```bash
#!/bin/bash
# clean_branches.sh
# 清理已合并的分支
# 更新远程分支信息
git fetch --prune
# 切换到main分支
git checkout main
git pull
# 删除已合并的本地分支
git branch --merged | grep -v "\*\|main\|master" | xargs -n 1 git branch -d
# 删除远程已合并的分支
git remote prune origin
echo "Branches cleaned!"
```
## 性能测试脚本
### 1. 启动时间测试
```bash
#!/bin/bash
# test_startup_time.sh
# 测试应用启动时间
PACKAGE_NAME="com.example.app"
MAIN_ACTIVITY="com.example.app/.MainActivity"
ITERATIONS=10
echo "Testing startup time for $PACKAGE_NAME"
echo "Iterations: $ITERATIONS"
echo ""
total_time=0
for i in $(seq 1 $ITERATIONS); do
# 停止应用
adb shell am force-stop "$PACKAGE_NAME"
sleep 1
# 启动应用并测量时间
result=$(adb shell am start -W -n "$MAIN_ACTIVITY" | grep "TotalTime")
time=$(echo "$result" | awk '{print $2}')
echo "Iteration $i: ${time}ms"
total_time=$((total_time + time))
sleep 2
done
average=$((total_time / ITERATIONS))
echo ""
echo "Average startup time: ${average}ms"
```
### 2. 内存监控脚本
```bash
#!/bin/bash
# monitor_memory.sh
# 监控应用内存使用
PACKAGE_NAME="$1"
INTERVAL=5
if [ -z "$PACKAGE_NAME" ]; then
echo "Usage: monitor_memory.sh <package_name> [interval_seconds]"
exit 1
fi
if [ -n "$2" ]; then
INTERVAL="$2"
fi
echo "Monitoring memory for $PACKAGE_NAME (interval: ${INTERVAL}s)"
echo "Press Ctrl+C to stop"
echo ""
while true; do
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
meminfo=$(adb shell dumpsys meminfo "$PACKAGE_NAME" | grep "TOTAL")
if [ -n "$meminfo" ]; then
pss=$(echo "$meminfo" | awk '{print $2}')
echo "[$timestamp] PSS: ${pss}KB"
fi
sleep "$INTERVAL"
done
```
## 部署脚本
### 1. 自动部署脚本
```bash
#!/bin/bash
# deploy.sh
# 自动构建和部署
echo "Building APK..."
./gradlew assembleRelease
if [ $? -ne 0 ]; then
echo "Build failed!"
exit 1
fi
APK_PATH="app/build/outputs/apk/release/app-release.apk"
if [ ! -f "$APK_PATH" ]; then
echo "APK not found: $APK_PATH"
exit 1
fi
echo "Installing APK..."
adb install -r "$APK_PATH"
if [ $? -eq 0 ]; then
echo "Deployment successful!"
else
echo "Deployment failed!"
exit 1
fi
```
## 使用说明
### 脚本执行权限
```bash
# 添加执行权限
chmod +x script.sh
# 执行脚本
./script.sh
```
### 脚本路径
建议将常用脚本放在 `~/scripts/` 目录下并添加到PATH
```bash
# 添加到 ~/.bashrc 或 ~/.zshrc
export PATH="$HOME/scripts:$PATH"
```
## 相关链接
- [[效率工具推荐/README]]
- [[09-调试与工具链/ADB高级命令]]

View File

@@ -0,0 +1,42 @@
#!/bin/bash
# collect_logs.sh
# 收集并过滤日志
PACKAGE_NAME="${1:-com.example.app}"
DURATION="${2:-30}"
OUTPUT_FILE="logs_${PACKAGE_NAME}_$(date +%Y%m%d_%H%M%S).txt"
echo "Collecting logs for package: $PACKAGE_NAME"
echo "Duration: ${DURATION} seconds"
echo "Output file: $OUTPUT_FILE"
echo "Press Ctrl+C to stop early"
echo ""
# 清空日志缓冲区
adb logcat -c
# 开始收集日志
adb logcat | grep --line-buffered "$PACKAGE_NAME" > "$OUTPUT_FILE" &
LOGCAT_PID=$!
# 等待指定时间
sleep "$DURATION"
# 停止日志收集
kill $LOGCAT_PID 2>/dev/null
wait $LOGCAT_PID 2>/dev/null
# 统计日志行数
LINE_COUNT=$(wc -l < "$OUTPUT_FILE" 2>/dev/null || echo "0")
echo ""
echo "Log collection completed!"
echo " File: $OUTPUT_FILE"
echo " Lines: $LINE_COUNT"
# 显示最后几行
if [ "$LINE_COUNT" -gt 0 ]; then
echo ""
echo "Last 10 lines:"
tail -n 10 "$OUTPUT_FILE"
fi

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# install_apks.sh
# 批量安装当前目录下的所有APK
echo "Searching for APK files in current directory..."
APK_COUNT=0
SUCCESS_COUNT=0
FAIL_COUNT=0
for apk in *.apk; do
if [ -f "$apk" ]; then
APK_COUNT=$((APK_COUNT + 1))
echo ""
echo "[$APK_COUNT] Installing $apk..."
adb install -r "$apk"
if [ $? -eq 0 ]; then
echo "✓ Successfully installed $apk"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo "✗ Failed to install $apk"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
fi
done
echo ""
echo "========================================="
echo "Summary:"
echo " Total APKs: $APK_COUNT"
echo " Success: $SUCCESS_COUNT"
echo " Failed: $FAIL_COUNT"
echo "========================================="

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# log_analyzer.py
# 日志分析工具
import re
import sys
from collections import Counter
from datetime import datetime
def analyze_logs(log_file):
"""分析日志文件"""
errors = []
warnings = []
patterns = {
'crash': r'FATAL|crash|exception',
'anr': r'ANR|Application Not Responding',
'oom': r'OutOfMemory|OOM',
'nullpointer': r'NullPointerException',
'illegalstate': r'IllegalStateException',
}
pattern_matches = {key: [] for key in patterns.keys()}
print(f"Analyzing log file: {log_file}")
print("=" * 60)
try:
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
for line_num, line in enumerate(f, 1):
# 检查错误
if 'E/' in line or 'ERROR' in line.upper():
errors.append((line_num, line.strip()))
# 检查警告
if 'W/' in line or 'WARN' in line.upper():
warnings.append((line_num, line.strip()))
# 检查特定模式
for pattern_name, pattern in patterns.items():
if re.search(pattern, line, re.IGNORECASE):
pattern_matches[pattern_name].append((line_num, line.strip()))
# 输出统计信息
print(f"\nSummary:")
print(f" Total lines analyzed: {line_num}")
print(f" Errors: {len(errors)}")
print(f" Warnings: {len(warnings)}")
for pattern_name, matches in pattern_matches.items():
if matches:
print(f" {pattern_name.upper()}: {len(matches)}")
# 显示错误示例
if errors:
print(f"\nFirst 10 errors:")
for line_num, line in errors[:10]:
print(f" Line {line_num}: {line[:100]}")
# 显示模式匹配示例
for pattern_name, matches in pattern_matches.items():
if matches:
print(f"\n{pattern_name.upper()} occurrences (first 5):")
for line_num, line in matches[:5]:
print(f" Line {line_num}: {line[:100]}")
# 生成报告文件
report_file = log_file + ".report.txt"
with open(report_file, 'w', encoding='utf-8') as f:
f.write(f"Log Analysis Report\n")
f.write(f"Generated: {datetime.now()}\n")
f.write(f"Source file: {log_file}\n")
f.write("=" * 60 + "\n\n")
f.write(f"Errors: {len(errors)}\n")
f.write(f"Warnings: {len(warnings)}\n\n")
if errors:
f.write("Errors:\n")
for line_num, line in errors:
f.write(f" Line {line_num}: {line}\n")
print(f"\nReport saved to: {report_file}")
except FileNotFoundError:
print(f"Error: File not found: {log_file}")
return 1
except Exception as e:
print(f"Error: {e}")
return 1
return 0
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python log_analyzer.py <log_file>")
sys.exit(1)
log_file = sys.argv[1]
exit_code = analyze_logs(log_file)
sys.exit(exit_code)

View File

@@ -0,0 +1,51 @@
#!/bin/bash
# performance_monitor.sh
# 性能监控脚本
PACKAGE_NAME="${1:-com.example.app}"
INTERVAL="${2:-5}"
OUTPUT_FILE="performance_${PACKAGE_NAME}_$(date +%Y%m%d_%H%M%S).csv"
if [ -z "$PACKAGE_NAME" ]; then
echo "Usage: performance_monitor.sh <package_name> [interval_seconds]"
exit 1
fi
echo "Monitoring performance for: $PACKAGE_NAME"
echo "Interval: ${INTERVAL} seconds"
echo "Output file: $OUTPUT_FILE"
echo "Press Ctrl+C to stop"
echo ""
# 创建CSV文件头
echo "Timestamp,PSS(KB),Private_Dirty(KB),Heap_Size(KB),Heap_Alloc(KB)" > "$OUTPUT_FILE"
# 监控循环
while true; do
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 获取内存信息
meminfo=$(adb shell dumpsys meminfo "$PACKAGE_NAME" 2>/dev/null)
if [ -n "$meminfo" ]; then
# 提取PSS
pss=$(echo "$meminfo" | grep "TOTAL" | awk '{print $2}')
# 提取Private Dirty
private_dirty=$(echo "$meminfo" | grep "TOTAL" | awk '{print $5}')
# 提取Heap信息
heap_size=$(echo "$meminfo" | grep "App Summary" -A 10 | grep "java heap" | awk '{print $4}')
heap_alloc=$(echo "$meminfo" | grep "App Summary" -A 10 | grep "java heap" | awk '{print $5}')
# 写入CSV
echo "$timestamp,$pss,$private_dirty,$heap_size,$heap_alloc" >> "$OUTPUT_FILE"
# 显示当前状态
printf "\r[%s] PSS: %s KB | Heap: %s KB" "$timestamp" "${pss:-N/A}" "${heap_alloc:-N/A}"
else
echo "Warning: Could not get memory info for $PACKAGE_NAME"
fi
sleep "$INTERVAL"
done

View File

@@ -0,0 +1,74 @@
#!/bin/bash
# test_startup_time.sh
# 测试应用启动时间
PACKAGE_NAME="${1:-com.example.app}"
MAIN_ACTIVITY="${2}"
ITERATIONS="${3:-10}"
if [ -z "$MAIN_ACTIVITY" ]; then
# 尝试从包名推断主Activity
MAIN_ACTIVITY="${PACKAGE_NAME}/.MainActivity"
fi
echo "Testing startup time for $PACKAGE_NAME"
echo "Main Activity: $MAIN_ACTIVITY"
echo "Iterations: $ITERATIONS"
echo ""
total_time=0
min_time=999999
max_time=0
times=()
for i in $(seq 1 $ITERATIONS); do
# 停止应用
adb shell am force-stop "$PACKAGE_NAME" > /dev/null 2>&1
sleep 1
# 启动应用并测量时间
result=$(adb shell am start -W -n "$MAIN_ACTIVITY" 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Iteration $i: Failed to start"
continue
fi
# 提取TotalTime
time=$(echo "$result" | grep "TotalTime" | awk '{print $2}')
if [ -z "$time" ]; then
echo "Iteration $i: Could not get time"
continue
fi
echo "Iteration $i: ${time}ms"
times+=($time)
total_time=$((total_time + time))
if [ $time -lt $min_time ]; then
min_time=$time
fi
if [ $time -gt $max_time ]; then
max_time=$time
fi
sleep 2
done
if [ ${#times[@]} -eq 0 ]; then
echo "No successful iterations!"
exit 1
fi
average=$((total_time / ${#times[@]}))
echo ""
echo "========================================="
echo "Results:"
echo " Successful iterations: ${#times[@]}"
echo " Average: ${average}ms"
echo " Min: ${min_time}ms"
echo " Max: ${max_time}ms"
echo "========================================="

204
个人笔记体系.md Normal file
View File

@@ -0,0 +1,204 @@
作为一位使用 Obsidian 管理笔记的高级 Android Framework 工程师,我为你设计一套兼顾 **技术深度**、**项目实践** 和 **知识演进** 的笔记目录体系。这套系统基于 **PARA 方法**(项目-领域-资源-归档)和 **MOC内容地图** 理念,适合长期技术积累与快速检索。
---
## 📁 **Android Framework 工程师 Obsidian 笔记体系**
### 1. **🎯 项目 (Projects)**
_当前正在进行的项目、任务、需求开发笔记_
```
Projects/
├── 项目A-系统定制化/
│ ├── 需求与背景.md
│ ├── 技术方案设计.md
│ ├── 关键问题记录/
│ │ ├── 2024-XX-XX-开机动画卡顿调试.md
│ │ └── 2024-XX-XX-Service重启机制优化.md
│ ├── 测试与验证记录.md
│ └── 复盘与总结.md
├── 项目B-性能优化专项/
│ ├── 目标与指标.md
│ ├── 工具链配置Perfetto+Systrace.md
│ └── 问题追踪列表.md
└── 项目C-XX模块重构/
├── 架构设计图.excalidraw
└── 迁移计划.md
```
---
### 2. **🧠 领域 (Areas)**
_需要持续精进的专业领域知识库按模块组织_
```
Areas/
├── 01-系统启动流程/
│ ├── Bootloader到Init.md
│ ├── Zygote进程启动.md
│ ├── SystemServer核心服务.md
│ └── Launcher启动流程.md
├── 02-Activity管理/
│ ├── Activity启动流程跨进程.md
│ ├── Activity栈管理Task&Stack.md
│ ├── 生命周期深度解析.md
│ └── 异常恢复机制.md
├── 03-Window系统/
│ ├── WindowManagerService架构.md
│ ├── SurfaceFlinger交互流程.md
│ ├── 窗口类型与层级.md
│ └── 触摸事件传递.md
├── 04-资源与包管理/
│ ├── PackageManagerService.md
│ ├── Resource资源加载机制.md
│ └── 动态加载与热修复原理.md
├── 05-进程与线程通信/
│ ├── Binder机制内核到Java层.md
│ ├── Handler机制源码解析.md
│ ├── AIDL与HIDL使用与原理.md
│ └── 跨进程同步与锁优化.md
├── 06-性能优化体系/
│ ├── 启动优化方法论.md
│ ├── 内存优化LeakCanary原理.md
│ ├── 流畅度Choreographer+VSYNC.md
│ └── 功耗优化工具链.md
├── 07-系统安全/
│ ├── SELinux策略编写.md
│ ├── 权限管理框架.md
│ ├── 密钥存储与加密.md
│ └── 漏洞案例库.md
├── 08-定制化开发/
│ ├── 系统属性定制.md
│ ├── 系统服务添加流程.md
│ ├── 开机动画与OTA.md
│ └── 厂商定制接口规范.md
└── 09-调试与工具链/
├── ADB高级命令.md
├── GDB/LLDB调试Native.md
├── Systrace/Perfetto全解读.md
└── 自定义调试工具开发.md
```
---
### 3. **📚 资源 (Resources)**
_外部收集的参考资料、文章、论文、工具_
```
Resources/
├── 论文/
│ ├── Android系统优化论文/
│ └── 移动操作系统前沿/
├── 技术文章/
│ ├── 官方文档笔记/
│ ├── 优质博客归档Gityuan等/
│ └── 内核相关文章/
├── 工具/
│ ├── 脚本库/
│ └── 效率工具推荐/
└── 会议与分享/
├── Android开发者峰会笔记/
└── 内部技术分享记录/
```
---
### 4. **🗃️ 归档 (Archive)**
_已完成或暂停的项目、过时但仍有参考价值的笔记_
```
Archive/
├── 项目-旧版ROM适配/
├── 领域-已废弃API研究/
└── 资源-历史会议记录/
```
---
### 5. **🧭 导航与索引 (Maps of Content)**
_核心枢纽连接碎片知识形成知识网络_
```
MOCs/
├── Android Framework知识体系图.md
│ - 用双向链接将各领域模块关联,形成全景图
├── 高频问题索引.md
│ - 例如“开机ANR” → 链接到启动流程、Handler、性能工具等笔记
├── 源码阅读地图.md
│ - 记录AOSP源码阅读路径如ActivityThread → Instrumentation → AMS
└── 技能提升路线图.md
- 阶段性学习目标与完成情况
```
---
### 6. **📓 工作日志 (Daily Notes)**
_每日工作记录、临时想法、会议记录_
```
Daily/
├── 2024-06-01.md
├── 2024-06-02.md
└── templates/
└── 每日模板.md
```
*模板建议包含:今日重点、问题记录、明日计划、临时灵感*
---
### 7. **🛠️ 模板库 (Templates)**
_标准化笔记模板提升记录效率_
```
Templates/
├── 问题排查模板.md
│ - 现象描述、可能原因、排查步骤、根本原因、解决方案
├── 技术方案设计模板.md
├── 会议记录模板.md
└── 源码解析模板.md
```
---
### 8. **⚙️ 配置与脚本 (Config)**
_Obsidian 插件配置、自定义脚本_
```
Config/
├── 插件列表与配置.md
├── 自定义脚本/
│ ├── 自动生成日报.js
│ └── 源码链接生成器.py
└── 主题与样式.css
```
---
## 🔧 **使用建议**
1. **标签系统**
- 使用 `#bug``#optimization``#hard` 标记问题类型
- 使用 `#prd``#review``#done` 标记笔记状态
2. **双向链接**
- 在笔记中大量使用 `[[ ]]` 链接相关概念
- 使用 `![[ ]]` 嵌入关键图表或代码片段
3. **图谱利用**
- 定期查看知识图谱,发现隐藏联系
- 为核心概念创建“中心笔记”Hub Notes
4. **定期复盘**
- 每周整理 `Daily Notes` 到对应领域或项目
- 每月更新 `MOCs`,重构知识结构
---
这套体系能够帮助你在 **深度开发**、**问题排查**、**技术演进** 之间自由切换,既满足日常工作记录,又构建持续增长的技术知识库。你可以根据实际工作流微调,关键是保持 **持续记录****有效连接**