基于香橙派 3B(自编译 Android 11)的 Contacts 联系人 APP 日志添加、编译推送及调试实践
第一篇:Android 基础概念
一、APP 和 Activity 的关系
| 概念 | 说明 |
|---|---|
| APP(应用) | 多个 Activity 的集合 |
| Activity(页面) | APP 中的一个界面 |
| PeopleActivity | Contacts APP 的主入口 Activity(类似首页) |
打开联系人 APP 流程:
- 点击桌面图标 → Android 系统启动 Contacts 应用进程
- 启动
PeopleActivity→ 显示联系人列表主页面
二、Contacts APP 的主要 Activity
| Activity | 用途 |
|---|---|
PeopleActivity |
主页面 --- 联系人列表、收藏、群组 |
ContactEditorActivity |
编辑/新建联系人 |
ContactSelectionActivity |
选择联系人(发短信时选人) |
QuickContactActivity |
快速联系人卡片(弹窗) |
AccountFilterActivity |
按账户过滤联系人列表 |
三、priv-app 含义
priv-app= privileged application(特权应用)- 比普通 APP 拥有更高权限,可调用系统级 API
- Contacts 是核心系统应用,放在
/product/priv-app/Contacts/
四、路径权限对比
| 路径 | 类型 | 权限等级 |
|---|---|---|
/product/priv-app/ |
特权应用 | 🔒 最高 |
/system/app/ |
系统应用 | 较高 |
/data/app/ |
用户安装 APP | 普通 |
五、adb 命令速查表
| 命令 | 含义 |
|---|---|
adb devices |
查看连接的设备 |
adb root |
获取 root 权限 |
adb remount |
重新挂载分区为可读写 |
adb shell |
进入设备 shell |
adb push 本地 远程 |
推送文件到设备 |
adb pull 远程 本地 |
从设备拉取文件 |
adb logcat |
查看设备运行日志 |
adb logcat -v time |
带时间戳输出日志 |
adb logcat -s TAG |
只过滤指定 TAG 的日志 |
adb logcat -c |
清空日志缓冲区 |
adb shell am force-stop 包名 |
强制停止 APP |
adb shell am start -n 包名/Activity名 |
启动指定 Activity |
adb shell pm path 包名 |
查看 APP 安装路径 |
第二篇:MainActivity 核心解析
一、onCreate() --- 第一个执行的方法
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 【核心步骤 1】:绑定界面布局
setContentView(R.layout.activity_main);
// 【核心步骤 2】:绑定组件(按钮、文本框等)
// Button myButton = findViewById(R.id.button1);
}
⚠️ 注意 :
encrypt是听误,正确的词是onCreate(创建)。
二、Log 日志打印
java
Log.e("MyTag", "这是我的日志信息");
参数解析:
| 参数 | 作用 |
|---|---|
| 第一个参数(Tag/标签) | 过滤日志用,相当于给这句话贴标签 |
| 第二个参数(Msg/消息) | 你想看的具体内容 |
Log 级别对照表:
| 方法 | 级别 | 颜色 | 用途 |
|---|---|---|---|
Log.v() |
Verbose(啰嗦) | 黑色 | 最详细的日志 |
Log.d() |
Debug(调试) | 蓝色 | 调试信息 |
Log.i() |
Info(信息) | 绿色 | 一般信息 |
Log.w() |
Warning(警告) | 橙色 | 警告信息 |
Log.e() |
Error(错误) | 🔴 红色 | 错误信息(新手推荐) |
💡 小白技巧 :刚开始学习用
Log.e(),红色日志一眼就能看到。
三、极简 MainActivity 模板
java
package com.example.myapp;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("小白测试", "我的第一个 App 成功启动啦!");
}
}
第三篇:源码分析方法
一、源码分析的两大核心方式
| 方式 | 定义 | 适用场景 | 特点 |
|---|---|---|---|
| 主动分析(正向追踪) | 顺着代码逻辑向下追溯 | 研究完整生命周期或初始化流程 | 逻辑连贯,但容易迷失 |
| 调用分析(逆向追踪) | 从具体 API 出发,跳转到定义 | 了解某个方法的底层原理 | 目的性强,效率高 |
二、无法跳转的函数的解决方案
痛点 :onCreate、onDestroy 等生命周期方法无法跳转。
解决方案:打印方法调用堆栈(Trace)
java
Log.e("SourceAnalysis", "Log Triggered Here!", new Throwable());
三个参数解析:
| 参数 | 说明 |
|---|---|
| 参数1(Tag) | 日志标签,用于过滤 |
| 参数2(Msg) | 提示信息 |
| 参数3(Throwable) | 关键:创建异常对象,自动捕捉当前线程堆栈 |
输出示例:
php
E/SourceAnalysis: Log Triggered Here!
java.lang.Throwable
at com.example.app.MainActivity.onCreate(MainActivity.java:15)
at android.app.Activity.performCreate(Activity.java:8051)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3600)
分析方法:根据日志中的类名和行号,全局搜索跳转到对应源码。
第四篇:Android 系统调试方法
一、Logcat 日志调试(最常用)
| 命令 | 用途 |
|----------------------|-----------------|---------------|
| adb logcat | 实时查看所有日志 |
| adb logcat -v time | 带时间戳输出(推荐) |
| adb logcat *:E | 仅显示 Error 级别及以上 |
| `adb logcat | grep "关键字"` | 过滤特定日志(Linux) |
二、UART 串口调试(硬件调试)
适用场景:U-Boot 阶段、Kernel 崩溃、驱动加载失败、ADB 无法连接时。
使用步骤:
- USB 转 TTL 模块连接香橙派 Debug UART 引脚(TX→RX,RX→TX)
- 电脑端设置波特率为 1500000(Rockchip 标准)
- 串口控制台输入
dmesg查看内核日志,输入logcat查看 Android 日志
三、ADB Shell 系统状态审查
| 命令 | 用途 |
|---|---|
adb shell dumpsys activity |
查看当前 Activity 状态 |
adb shell dumpsys window |
查看窗口层级和焦点 |
adb shell bugreport |
生成全面体检报告 |
adb shell top |
查看 CPU 占用 |
adb shell procrank |
查看内存占用 |
四、Native 层调试(C/C++ 崩溃)
Tombstone(墓碑日志):
- Native 进程崩溃时,系统在
/data/tombstones/生成日志文件 adb pull /data/tombstones/拉取后配合ndk-stack定位代码行
第五篇:Contacts 联系人 APP 日志添加与编译推送
一、修改文件位置
bash
packages/apps/Contacts/src/com/android/contacts/activities/PeopleActivity.java
二、添加的中文日志代码
java
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: super.onCreate 完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 账户类型管理器和过滤器控制器初始化完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 运行时权限检查完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: Intent 处理完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 联系人列表过滤器验证完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 设置内容视图 contacts_drawer_activity 完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 操作栏设置完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 抽屉导航设置完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 注册监听器完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: 创建视图和 Fragment 完成");
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity: onCreate 全部完成");
关键说明:
Constants.PERFORMANCE_TAG="ContactsPerf"(在Constants.java中定义)- 使用
Log.d()(DEBUG 级别) - ⚠️
Constants类中没有TAG字段,只有PERFORMANCE_TAG
三、onCreate 执行流程(启动顺序)
| 顺序 | 步骤 |
|---|---|
| 1 | 设置主题(在 super.onCreate 之前) |
| 2 | super.onCreate --- Android 框架初始化 |
| 3 | 初始化管理器(AccountTypeManager、ContactListFilterController) |
| 4 | 检查运行时权限 |
| 5 | 处理 Intent(无效则结束 Activity) |
| 6 | 过滤器验证 |
| 7 | 设置布局(R.layout.contacts_drawer_activity) |
| 8 | 设置 ActionBar |
| 9 | 设置抽屉导航(汉堡按钮) |
| 10 | 恢复导航模式(默认 ALL_CONTACTS) |
| 11 | 注册监听器 |
| 12 | 记录是否重建实例 |
| 13 | 创建视图和 Fragment |
| 14 | 释放窗口背景 |
四、编译 Contacts APK
环境准备(必须在同一终端):
bash
cd ~/aosp/RK3566_Android_Source_Code/RK356X_Android11
source build/envsetup.sh
lunch 51 # 选择 rk3566_r-userdebug
编译命令:
bash
# 只编译 Contacts 主模块(推荐)
mmm packages/apps/Contacts:Contacts
# 或编译 Contacts 及测试模块
mmm packages/apps/Contacts
编译产物路径:
bash
out/target/product/rk3566_r/product/priv-app/Contacts/Contacts.apk
五、推送 APK 到设备
完整流程:
bash
# 1. 确认设备连接
adb devices
# 2. 获取 root 权限并 remount
adb root && adb remount
# 3. 推送 APK(注意正确路径!)
adb push out/target/product/rk3566_r/product/priv-app/Contacts/Contacts.apk /product/priv-app/Contacts/Contacts.apk
# 4. 确认路径正确
adb shell pm path com.android.contacts
# 输出应为:package:/product/priv-app/Contacts/Contacts.apk
# 5. 强制停止 APP 并重启
adb shell am force-stop com.android.contacts
adb shell am start -n com.android.contacts/.activities.PeopleActivity
六、为什么系统应用可以直接替换 APK?
| 对比项 | 普通应用 | 系统应用 |
|---|---|---|
| 安装位置 | /data/app/ |
/product/priv-app/、/system/app/ |
| 安装方式 | adb install |
直接替换 APK 文件 |
| 生效条件 | 需要安装流程 | 重启 APP 即可 |
原理:
- 安装位置不同 :系统应用在
/system等只读分区,系统启动时扫描目录生成应用列表,不依赖复杂的数据库注册机制 - 加载方式不同 :重启 APP 时,系统直接重新读取磁盘上的 APK 文件,没有额外的安装状态需要同步
七、日志查看命令
bash
# 只看 ContactsPerf 标签的日志(推荐)
adb logcat -v time -s ContactsPerf
# 清空旧日志再看
adb logcat -c && adb logcat -v time -s ContactsPerf
# 直接启动 APP
adb shell am start -n com.android.contacts/.activities.PeopleActivity
八、常见问题排查
| 问题 | 解决方案 |
|---|---|
| 看不到中文日志 | adb shell setprop log.tag.ContactsPerf DEBUG |
| APP 没更新 | 检查是否推对了路径(/product/ 不是 /system/) |
| 推完还是旧代码 | adb shell am force-stop com.android.contacts 强杀再重启 |
| 不知道 APK 路径 | adb shell pm path com.android.contacts |
附录:控制台快捷键
| 快捷键 | 作用 |
|---|---|
Tab |
自动补全命令或路径 |
Ctrl + C |
终止当前命令 |
Ctrl + D |
退出 shell |
↑ / ↓ |
浏览历史命令 |
js
05-28 07:05:09.474 D/ContactsPerf( 2117): ContactsApplication.onCreate start
05-28 07:05:09.476 D/ContactsPerf( 2117): ContactsApplication.onCreate finish
05-28 07:05:09.623 D/ContactsPerf( 2117): PeopleActivity.onCreate start
05-28 07:05:09.623 D/ContactsPerf( 2117): PeopleActivity: 调用堆栈
05-28 07:05:09.623 D/ContactsPerf( 2117): java.lang.Throwable: stack trace
05-28 07:05:09.623 D/ContactsPerf( 2117): at com.android.contacts.activities.PeopleActivity.onCreate(PeopleActivity.java:348)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.Activity.performCreate(Activity.java:8022)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.Activity.performCreate(Activity.java:8006)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3404)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.os.Handler.dispatchMessage(Handler.java:106)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.os.Looper.loop(Looper.java:223)
05-28 07:05:09.623 D/ContactsPerf( 2117): at android.app.ActivityThread.main(ActivityThread.java:7664)
05-28 07:05:09.623 D/ContactsPerf( 2117): at java.lang.reflect.Method.invoke(Native Method)
05-28 07:05:09.623 D/ContactsPerf( 2117): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
05-28 07:05:09.623 D/ContactsPerf( 2117): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
05-28 07:05:09.665 D/ContactsPerf( 2117): PeopleActivity: super.onCreate 完成
05-28 07:05:09.708 D/ContactsPerf( 2117): PeopleActivity: 账户类型管理器和过滤器控制器初始化完成
05-28 07:05:09.713 D/ContactsPerf( 2117): PeopleActivity: 运行时权限检查完成
05-28 07:05:09.714 D/ContactsPerf( 2117): PeopleActivity: Intent 处理完成
05-28 07:05:09.714 D/ContactsPerf( 2117): PeopleActivity: 联系人列表过滤器验证完成
05-28 07:05:09.808 D/ContactsPerf( 2117): PeopleActivity: 设置内容视图 contacts_drawer_activity 完成
05-28 07:05:09.837 D/ContactsPerf( 2117): PeopleActivity: 操作栏设置完成
05-28 07:05:09.846 D/ContactsPerf( 2117): PeopleActivity: 抽屉导航设置完成
05-28 07:05:09.847 D/ContactsPerf( 2117): PeopleActivity: 设置默认导航模式,当前视图=ALL_CONTACTS
05-28 07:05:09.847 D/ContactsPerf( 2117): PeopleActivity: 注册监听器完成
05-28 07:05:09.847 D/ContactsPerf( 2117): PeopleActivity: 是否为重建实例=false
05-28 07:05:09.928 D/ContactsPerf( 2117): PeopleActivity: 创建视图和 Fragment 完成
05-28 07:05:09.928 D/ContactsPerf( 2117): PeopleActivity.onCreate finish
05-28 07:05:09.929 D/ContactsPerf( 2117): PeopleActivity: onCreate 全部完成
📊 日志分析
一、(2117) 是什么?
每次启动 APP 都会不一样。
| 项目 | 说明 |
|---|---|
2117 |
进程 ID(PID) |
| 含义 | Android 系统为每个运行的 APP 进程分配的唯一编号 |
| 变化规律 | 每次 APP 冷启动(完全退出后重启),PID 都会变化 |
| 同一进程内 | PID 保持不变,可用于判断日志是否来自同一次启动 |
示例对比:
ini
第一次启动:D/ContactsPerf( 2117) ← PID=2117
第二次启动:D/ContactsPerf( 2513) ← PID=2513(变化了)
第三次启动:D/ContactsPerf( 1892) ← PID=1892(又变了)
实际应用:
- 同一 PID 的所有日志 → 同一次启动过程
- PID 变了 → APP 被完全杀死后重新启动了
二、堆栈信息解读
堆栈日志从下往上读,最下面是最早的调用,最上面是当前执行位置。
scss
堆栈内容(从下往上倒序):
│
├─ ZygoteInit.main() ← 【最底层】系统启动进程
│
├─ RuntimeInit.main() ← 运行时初始化
│
├─ ActivityThread.main() ← 主线程入口
│
├─ Looper.loop() ← 消息循环开始
│
├─ Handler.dispatchMessage() ← 分发消息
│
├─ ActivityThread$H.handleMessage() ← 处理 Activity 启动消息
│
├─ TransactionExecutor.execute() ← 执行事务
│
├─ LaunchActivityItem.execute() ← 执行启动 Activity 的具体操作
│
├─ ActivityThread.handleLaunchActivity() ← 处理启动 Activity
│
├─ ActivityThread.performLaunchActivity() ← 执行启动 Activity
│
├─ Instrumentation.callActivityOnCreate() ← 通过 Instrumentation 调用 onCreate
│
├─ Activity.performCreate() ← Activity 执行创建
│
└─ PeopleActivity.onCreate() ← 【最顶层】我们加的日志位置
三、关键调用链(核心路径)
从堆栈中提取的关键调用路径:
scss
# 起点:系统进程启动
ZygoteInit.main()
↓
# Activity 主线程入口
ActivityThread.main()
↓
# 消息循环
Looper.loop()
↓
# 收到启动 Activity 的消息
ActivityThread$H.handleMessage()
↓
# 执行启动事务
TransactionExecutor.execute()
↓
LaunchActivityItem.execute()
↓
# 创建 Activity 实例
ActivityThread.performLaunchActivity()
↓
# 调用生命周期
Instrumentation.callActivityOnCreate()
↓
Activity.performCreate()
↓
# 🎯 我们代码执行的地方
PeopleActivity.onCreate()
四、日志时间线分析
| 时间 | 事件 | 耗时 |
|---|---|---|
| 07:05:09.474 | ContactsApplication.onCreate start | - |
| 07:05:09.476 | ContactsApplication.onCreate finish | 2ms |
| 07:05:09.623 | PeopleActivity.onCreate start | 147ms 后 |
| 07:05:09.623 | 打印堆栈 | - |
| 07:05:09.665 | super.onCreate 完成 | 42ms |
| 07:05:09.708 | 初始化管理器完成 | 43ms |
| 07:05:09.713 | 权限检查完成 | 5ms |
| 07:05:09.714 | Intent 处理完成 | 1ms |
| 07:05:09.808 | 设置布局完成 | 94ms |
| 07:05:09.837 | 操作栏设置完成 | 29ms |
| 07:05:09.846 | 抽屉导航设置完成 | 9ms |
| 07:05:09.847 | 注册监听器完成 | 1ms |
| 07:05:09.928 | 创建视图和 Fragment 完成 | 81ms |
| 07:05:09.928 | PeopleActivity.onCreate finish | - |
总耗时 :PeopleActivity.onCreate 从 09.623 到 09.928 ≈ 305ms
耗时最长的步骤:
- 设置布局:94ms
- 创建视图和 Fragment:81ms
- super.onCreate:42ms + 初始化管理器:43ms = 85ms
五、堆栈分析的实际用途
| 场景 | 如何使用堆栈 |
|---|---|
| 找不到方法调用者 | 打印堆栈,直接从下往上找到谁调用了你 |
| 理解系统调用流程 | 堆栈天然展示了完整的调用链 |
| 定位崩溃根源 | 崩溃日志中的堆栈直接指向出错代码行 |
| 学习源码架构 | 通过堆栈了解 Android 系统的层层调用关系 |
六、核心知识点总结
| 知识点 | 结论 |
|---|---|
| PID(进程 ID) | 每次冷启动都不同,同一进程内不变,用于区分不同启动 |
| 堆栈读法 | 从下往上读,最下面是调用源头,最上面是当前位置 |
| PeopleActivity 启动入口 | 从 ActivityThread.performLaunchActivity 进入 |
| 生命周期管控 | 由 Instrumentation 和 ActivityThread 配合完成 |
| 性能瓶颈 | 布局加载和 Fragment 创建是最耗时的环节 |