测试
This commit is contained in:
51
.obsidian/workspace.json
vendored
51
.obsidian/workspace.json
vendored
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# 05-进程与线程通信
|
||||
# 05-进程与线程通信
|
||||
|
||||
本目录包含Android系统中进程与线程通信相关技术的深入分析和源码解析。涵盖了Binder机制、Handler机制、AIDL/HIDL、跨进程同步等核心内容。
|
||||
|
||||
|
||||
@@ -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知识体系图]]
|
||||
@@ -1 +1,410 @@
|
||||
# 内存优化(LeakCanary原理)
|
||||
|
||||
## 概述
|
||||
|
||||
内存泄漏是Android开发中的常见问题,会导致应用内存占用持续增长,最终可能引发OOM(Out 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-进程与线程通信]]
|
||||
|
||||
@@ -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全解读]]
|
||||
|
||||
@@ -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 API(Android 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全解读]]
|
||||
|
||||
@@ -1 +1,499 @@
|
||||
# 流畅度(Choreographer+VSYNC)
|
||||
|
||||
## 概述
|
||||
|
||||
流畅度是Android应用性能的核心指标之一,直接影响用户体验。本文档深入解析VSYNC机制和Choreographer的工作原理,以及如何分析和优化应用流畅度。
|
||||
|
||||
## VSYNC机制
|
||||
|
||||
### 什么是VSYNC
|
||||
|
||||
VSYNC(Vertical 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.67ms(60Hz)
|
||||
- **绘制窗口**: 必须在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全解读]]
|
||||
@@ -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全解读]]
|
||||
@@ -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-server(Android 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. 分析Tombstone(Android)
|
||||
|
||||
```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全解读]]
|
||||
@@ -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知识体系图]]
|
||||
@@ -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-性能优化体系/启动优化方法论]]
|
||||
@@ -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-性能优化体系]]
|
||||
440
docs/Obsidian笔记体系/Resources/工具/效率工具推荐/README.md
Normal file
440
docs/Obsidian笔记体系/Resources/工具/效率工具推荐/README.md
Normal 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]]
|
||||
565
docs/Obsidian笔记体系/Resources/工具/脚本库/README.md
Normal file
565
docs/Obsidian笔记体系/Resources/工具/脚本库/README.md
Normal 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高级命令]]
|
||||
42
docs/Obsidian笔记体系/Resources/工具/脚本库/collect_logs.sh
Normal file
42
docs/Obsidian笔记体系/Resources/工具/脚本库/collect_logs.sh
Normal 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
|
||||
35
docs/Obsidian笔记体系/Resources/工具/脚本库/install_apks.sh
Normal file
35
docs/Obsidian笔记体系/Resources/工具/脚本库/install_apks.sh
Normal 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 "========================================="
|
||||
100
docs/Obsidian笔记体系/Resources/工具/脚本库/log_analyzer.py
Normal file
100
docs/Obsidian笔记体系/Resources/工具/脚本库/log_analyzer.py
Normal 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)
|
||||
51
docs/Obsidian笔记体系/Resources/工具/脚本库/performance_monitor.sh
Normal file
51
docs/Obsidian笔记体系/Resources/工具/脚本库/performance_monitor.sh
Normal 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
|
||||
74
docs/Obsidian笔记体系/Resources/工具/脚本库/test_startup_time.sh
Normal file
74
docs/Obsidian笔记体系/Resources/工具/脚本库/test_startup_time.sh
Normal 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
204
个人笔记体系.md
Normal 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`,重构知识结构
|
||||
|
||||
---
|
||||
|
||||
这套体系能够帮助你在 **深度开发**、**问题排查**、**技术演进** 之间自由切换,既满足日常工作记录,又构建持续增长的技术知识库。你可以根据实际工作流微调,关键是保持 **持续记录** 和 **有效连接**。
|
||||
Reference in New Issue
Block a user