feat: 更新 Baseline Profile,覆盖日期检查器/调休/工具页等场景

This commit is contained in:
xfy 2026-06-02 13:10:49 +08:00
parent ecf8a70f5d
commit b3abe92b2c
3 changed files with 3437 additions and 3488 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,11 @@ import android.util.Log
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import org.junit.Assert.assertNotNull
import org.junit.Assert.fail
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -30,10 +31,22 @@ import org.junit.runner.RunWith
*
* 测试覆盖全部用户交互路径实现全量 AOT
* 1. 冷启动 首帧渲染
* 2. FAB 展开 年视图 月视图
* 3. 日期选择 周视图折叠/展开
* 4. 关于页 开源许可页
* 5. 返回主界面
* 2. 显示调休切换 ON/OFFDayCell 大规模重组 + staggered 动画
* 3. CalendarPager 翻页 "今天"按钮跳回
* 4. 跨月日期点击 自动跳转
* 5. 月视图 年视图切换
* 6. 年视图翻年 "今年"按钮跳回
* 7. 年视图点击 MiniMonth 返回月视图
* 8. DayCell 点击
* 9. BottomCard 拖拽折叠到周视图
* 10. 周视图左右翻页
* 11. BottomCard 拖拽展开回月视图
* 12. 工具页面 日期检查器
* 13. DatePickerDialog 打开/确认
* 14. 日期检查器添加行 + 输入天数
* 15. 日期检查器滑动删除行
* 16. 关于页面 开源许可页面
* 17. 返回主界面 CalendarPager 翻页
*/
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@ -41,6 +54,22 @@ class BaselineProfileGenerator {
@get:Rule
val baselineProfileRule = BaselineProfileRule()
private fun MacrobenchmarkScope.safeFindFab(): UiObject2? =
device.wait(Until.findObject(By.res("fab_menu")), 3000)
private fun MacrobenchmarkScope.safeWaitCalendarPager(timeout: Long = 5000): UiObject2? =
device.wait(Until.findObject(By.res("calendar_pager")), timeout)
private fun MacrobenchmarkScope.waitForMainActivity() {
for (i in 1..5) {
val pager = device.wait(Until.findObject(By.res("calendar_pager")), 2000)
if (pager != null) return
val fab = device.wait(Until.findObject(By.res("fab_menu")), 1000)
if (fab != null) return
Thread.sleep(500)
}
}
@Test
fun generateAppStartupProfile() {
baselineProfileRule.collect(
@ -49,84 +78,212 @@ class BaselineProfileGenerator {
profileBlock = {
val TAG = "BaselineProfile"
// 1. 冷启动:从 launcher 启动应用
// ── 1. 冷启动 ──────────────────────────────────────────
pressHome()
device.executeShellCommand(
"am start -W -n plus.rua.project/.MainActivity"
)
device.waitForIdle()
// 2. 展开 FAB 菜单,等待菜单项出现
val fab = device.findObject(By.res("fab_menu"))
assertNotNull("FAB 按钮必须存在", fab)
fab!!.click()
val yearViewItem = device.wait(Until.findObject(By.text("年视图")), 3000)
Log.d(TAG, "FAB 菜单展开: yearViewItem=${yearViewItem != null}")
// ── 2. 切换"显示调休"ON覆盖 DayCell 大规模重组 + staggered 动画) ──
val fab1 = safeFindFab()
assertNotNull("FAB 必须存在", fab1)
fab1!!.click()
device.waitForIdle()
val legalHolidayOn = device.wait(
Until.findObject(By.text("显示调休")), 3000
)
assertNotNull("显示调休必须出现", legalHolidayOn)
legalHolidayOn!!.click()
device.waitForIdle()
// 3. 切换到年视图(覆盖 YearGridView、YearHeader、MiniMonth 路径)
assertNotNull("年视图菜单项必须出现", yearViewItem)
// ── 3. 右滑 CalendarPager → 上一个月 ──────────────────────
val calendarPager = safeWaitCalendarPager(3000)
assertNotNull("CalendarPager 必须存在", calendarPager)
calendarPager!!.swipe(Direction.RIGHT, 0.8f)
device.waitForIdle()
// ── 4. 点击"今天"按钮跳回当月(覆盖 MonthHeader 今天按钮) ──
val todayBtn = device.wait(Until.findObject(By.text("今天")), 3000)
assertNotNull("今天按钮必须出现", todayBtn)
todayBtn!!.click()
device.waitForIdle()
// ── 5. 点击跨月日期(网格左上角 = 上月灰色日期) ──────────
val pagerRef = safeWaitCalendarPager(3000)
assertNotNull("CalendarPager 必须存在(跨月点击)", pagerRef)
val pagerBounds = pagerRef!!.visibleBounds
val colW = pagerBounds.width() / 7
val rowH = pagerBounds.height() / 6
device.click(pagerBounds.left + colW / 2, pagerBounds.top + rowH / 2)
device.waitForIdle()
// ── 6. 再次点击"今天"跳回当月 ────────────────────────────
val todayBtn2 = device.wait(Until.findObject(By.text("今天")), 3000)
if (todayBtn2 != null) {
todayBtn2.click()
device.waitForIdle()
}
// ── 7. 切换到年视图 ──────────────────────────────────────
val fab2 = safeFindFab()
assertNotNull("FAB 必须存在(年视图)", fab2)
fab2!!.click()
device.waitForIdle()
val yearViewItem = device.wait(
Until.findObject(By.text("年视图")), 3000
)
assertNotNull("年视图必须出现", yearViewItem)
yearViewItem!!.click()
val yearGrid = device.wait(Until.findObject(By.res("year_grid")), 3000)
Log.d(TAG, "年视图加载: yearGrid=${yearGrid != null}")
val yearGrid = device.wait(
Until.findObject(By.res("year_grid")), 3000
)
assertNotNull("YearGridView 必须加载", yearGrid)
device.waitForIdle()
// 4. 在年视图中滑动到不同年份(覆盖动画和分页路径)
yearGrid!!.swipe(Direction.UP, 0.5f)
// ── 8. 左滑年视图 → 下一年HorizontalPager ─────────────
yearGrid!!.swipe(Direction.LEFT, 0.8f)
device.waitForIdle()
yearGrid.swipe(Direction.DOWN, 0.5f)
Thread.sleep(500)
// ── 9. 尝试点击"今年"按钮跳回当前年 ──────────────────
val thisYearBtn = device.wait(
Until.findObject(By.text("今年")), 2000
)
if (thisYearBtn != null && thisYearBtn.visibleBounds.height() > 0) {
thisYearBtn.click()
device.waitForIdle()
Thread.sleep(500)
}
// ── 10. 点击当前月份 MiniMonth 返回月视图 ───────────────
val now = java.time.LocalDate.now()
val currentMonthDesc = "${now.year}${now.monthValue}"
val miniMonth = device.wait(
Until.findObject(By.desc(currentMonthDesc)), 3000
)
if (miniMonth != null) {
miniMonth.click()
} else {
val yearGridRef = device.wait(Until.findObject(By.res("year_grid")), 3000)
if (yearGridRef != null) {
val ygBounds = yearGridRef.visibleBounds
val monthW = ygBounds.width() / 3
val monthH = ygBounds.height() / 4
val mIdx = now.monthValue - 1
device.click(
ygBounds.left + (mIdx % 3) * monthW + monthW / 2,
ygBounds.top + (mIdx / 3) * monthH + monthH / 2
)
}
}
Thread.sleep(1500)
device.waitForIdle()
safeWaitCalendarPager(5000)
device.waitForIdle()
// 5. 展开 FAB 并切换回月视图
val fabForMonth = device.findObject(By.res("fab_menu"))
assertNotNull("FAB 按钮必须存在(返回月视图)", fabForMonth)
fabForMonth!!.click()
val monthViewItem = device.wait(Until.findObject(By.text("月视图")), 3000)
Log.d(TAG, "FAB 菜单展开: monthViewItem=${monthViewItem != null}")
assertNotNull("月视图菜单项必须出现", monthViewItem)
monthViewItem!!.click()
device.waitForIdle()
// 6. 等待月视图完全加载,点击某一天(覆盖 DayCell 点击路径 + 底部卡片展开)
device.wait(Until.findObject(By.res("calendar_pager")), 3000)
val todayCell = device.wait(Until.findObject(By.descContains("今天")), 3000)
// ── 11. 点击 DayCell ────────────────────────────────────
val todayCell = device.wait(
Until.findObject(By.descContains("今天")), 3000
)
if (todayCell != null) {
Log.d(TAG, "找到今天 DayCell")
todayCell.click()
} else {
// 回退点击任意一天15号总是存在
Log.d(TAG, "未找到今天,回退点击 15 号")
val day15 = device.findObject(By.text("15"))
assertNotNull("DayCell 15 必须可点击", day15)
day15!!.click()
val calRef = safeWaitCalendarPager(3000)
if (calRef != null) {
val cb = calRef.visibleBounds
device.click(cb.centerX(), cb.centerY())
}
}
device.waitForIdle()
// 7. 拖拽 BottomCard 触发月视图↔周视图折叠/展开
val bottomCard = device.findObject(By.res("bottom_card"))
// ── 12. 拖拽 BottomCard 折叠到周视图 ─────────────────────
val bottomCard = device.wait(Until.findObject(By.res("bottom_card")), 5000)
assertNotNull("BottomCard 必须存在", bottomCard)
val bounds = bottomCard!!.visibleBounds
val centerX = bounds.centerX()
val centerY = bounds.centerY()
val dragDistance = (bounds.height() * 0.4).toInt()
// 向上拖拽 → 折叠到周视图
device.drag(centerX, centerY, centerX, centerY - dragDistance, 20)
device.waitForIdle()
// 向下拖拽 → 展开到月视图
device.drag(centerX, centerY - dragDistance, centerX, centerY, 20)
val bcBounds = bottomCard!!.visibleBounds
val cx = bcBounds.centerX()
val cy = bcBounds.centerY()
val dragDist = (bcBounds.height() * 0.4).toInt()
device.drag(cx, cy, cx, cy - dragDist, 20)
device.waitForIdle()
// 8. 展开 FAB 并进入工具页面
val fabForTools = device.findObject(By.res("fab_menu"))
assertNotNull("FAB 按钮必须存在(工具页)", fabForTools)
fabForTools!!.click()
val toolsButton = device.wait(Until.findObject(By.text("工具")), 3000)
Log.d(TAG, "FAB 菜单展开: toolsButton=${toolsButton != null}")
assertNotNull("工具菜单项必须出现", toolsButton)
// ── 13. 周视图左右翻页 ──────────────────────────────────
val weekPager = safeWaitCalendarPager(3000)
assertNotNull("周视图 CalendarPager 必须存在", weekPager)
weekPager!!.swipe(Direction.LEFT, 0.5f)
device.waitForIdle()
weekPager.swipe(Direction.RIGHT, 0.5f)
device.waitForIdle()
// ── 14. 拖拽 BottomCard 展开回月视图 ─────────────────────
device.drag(cx, cy - dragDist, cx, cy, 20)
device.waitForIdle()
// ── 15. 切换"显示调休"OFF ────────────────────────────────
val fab3 = safeFindFab()
assertNotNull("FAB 必须存在(关闭调休)", fab3)
fab3!!.click()
device.waitForIdle()
val legalHolidayOff = device.wait(
Until.findObject(By.text("显示调休")), 3000
)
assertNotNull("显示调休必须出现", legalHolidayOff)
legalHolidayOff!!.click()
device.waitForIdle()
// ── 16. CalendarPager 左右翻页 ──────────────────────────
val mainPager = safeWaitCalendarPager(5000)
if (mainPager != null) {
mainPager.swipe(Direction.LEFT, 0.5f)
device.waitForIdle()
safeWaitCalendarPager(3000)?.swipe(Direction.RIGHT, 0.5f)
device.waitForIdle()
}
// ── 17. 进入关于页面 ────────────────────────────────────
val fab5 = safeFindFab()
assertNotNull("FAB 必须存在(关于)", fab5)
fab5!!.click()
device.waitForIdle()
val aboutButton = device.wait(
Until.findObject(By.text("关于")), 3000
)
assertNotNull("关于必须出现", aboutButton)
aboutButton!!.click()
device.waitForIdle()
// ── 18. 进入开源许可页面 ────────────────────────────────
val licensesButton = device.wait(
Until.findObject(By.text("开放源代码许可")), 3000
)
assertNotNull("开放源代码许可按钮必须存在", licensesButton)
licensesButton!!.click()
device.waitForIdle()
// ── 19. 等待许可列表加载 ────────────────────────────────
device.wait(Until.findObject(By.textContains("Apache")), 2000)
// ── 20. 返回关于页 ──────────────────────────────────────
device.pressBack()
device.waitForIdle()
// ── 21. 返回主界面 ──────────────────────────────────────
device.pressBack()
device.waitForIdle()
// ── 22. 进入工具页面 ────────────────────────────────────
val fab4 = safeFindFab()
assertNotNull("FAB 必须存在(工具)", fab4)
fab4!!.click()
device.waitForIdle()
val toolsButton = device.wait(
Until.findObject(By.text("工具")), 3000
)
assertNotNull("工具必须出现", toolsButton)
toolsButton!!.click()
device.waitForIdle()
// 9. 进入日期检查器(覆盖 DateCheckerScreen
// ── 23. 进入日期检查器 ──────────────────────────────────
val dateCheckerEntry = device.wait(
Until.findObject(By.res("tool_date_checker")), 3000
)
@ -134,70 +291,47 @@ class BaselineProfileGenerator {
dateCheckerEntry!!.click()
device.waitForIdle()
// 10. 点击日历图标打开 DatePickerDialog覆盖 DatePicker
// ── 24. 打开生产日期 DatePicker → 确定 ───────────────────
val datePickerBtn = device.wait(
Until.findObject(By.res("date_picker_button")), 3000
)
if (datePickerBtn != null) {
datePickerBtn.click()
device.waitForIdle()
}
// 11. 等待 DatePickerDialog 并点击确定
device.wait(Until.findObject(By.text("确定")), 2000)
val confirmBtn = device.findObject(By.text("确定"))
val confirmBtn = device.wait(
Until.findObject(By.text("确定")), 2000
)
if (confirmBtn != null) {
confirmBtn.click()
device.waitForIdle()
}
// 12. 点击 FAB 添加新行(覆盖 FAB + LazyColumn items 重组)
val dateCheckerFab = device.findObject(By.res("date_checker_fab"))
if (dateCheckerFab != null) {
dateCheckerFab.click()
device.waitForIdle()
}
// 13. 返回工具页
device.pressBack()
// ── 25. FAB 添加新行 ────────────────────────────────────
val dateCheckerFab = device.wait(
Until.findObject(By.res("date_checker_fab")), 3000
)
assertNotNull("DateChecker FAB 必须存在", dateCheckerFab)
dateCheckerFab!!.click()
device.waitForIdle()
// 14. 返回主界面
device.pressBack()
// ── 26. 在新行输入天数 ──────────────────────────────────
val screenW = device.displayWidth
val screenH = device.displayHeight
device.click((screenW * 0.35f).toInt(), (screenH * 0.80f).toInt())
Thread.sleep(500)
device.executeShellCommand("input text '90'")
device.waitForIdle()
device.click(screenW / 2, (screenH * 0.15f).toInt())
device.waitForIdle()
// 15. 左右滑动切换月份(覆盖 CalendarPager 翻页)
val calendarPager = device.findObject(By.res("calendar_pager"))
assertNotNull("CalendarPager 必须存在", calendarPager)
calendarPager!!.swipe(Direction.LEFT, 0.5f)
device.waitForIdle()
calendarPager.swipe(Direction.RIGHT, 0.5f)
device.waitForIdle()
// 16. 进入关于页面(覆盖 AboutScreen + AnimatedGif
val fabForAbout = device.findObject(By.res("fab_menu"))
assertNotNull("FAB 按钮必须存在(关于页)", fabForAbout)
fabForAbout!!.click()
val aboutButton = device.wait(Until.findObject(By.text("关于")), 3000)
assertNotNull("关于菜单项必须出现", aboutButton)
aboutButton!!.click()
device.waitForIdle()
// 17. 进入开源许可页面(覆盖 LicensesScreen
val licensesButton = device.wait(Until.findObject(By.text("开放源代码许可")), 3000)
assertNotNull("开放源代码许可按钮必须存在", licensesButton)
licensesButton!!.click()
device.waitForIdle()
// 18. 等待许可列表加载
device.wait(Until.findObject(By.textContains("Apache")), 2000)
// 19. 返回关于页
device.pressBack()
device.waitForIdle()
// 20. 返回主界面
device.pressBack()
// ── 27. 滑动删除新行SwipeToDismiss ───────────────────
device.swipe(
(screenW * 0.85f).toInt(), (screenH * 0.75f).toInt(),
(screenW * 0.15f).toInt(), (screenH * 0.75f).toInt(),
30
)
Thread.sleep(800)
device.waitForIdle()
Log.d(TAG, "Baseline profile 生成完成,所有路径已覆盖")

View File

@ -158,9 +158,10 @@ get_screen_center() {
echo "$w $h"
}
# 清理后台进程
# 清理后台进程(整棵进程树)
cleanup_bg() {
if [ -n "$BG_PID" ] && kill -0 "$BG_PID" 2>/dev/null; then
pkill -P "$BG_PID" 2>/dev/null || true
kill "$BG_PID" 2>/dev/null || true
wait "$BG_PID" 2>/dev/null || true
fi
@ -209,7 +210,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true || true
sleep 1
echo " → 自动执行trace 开始后将连续左右滑动翻页"
wait_then_do 2 bash -c "
@ -229,7 +230,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将依次点击日历中不同位置的日期"
# 日历网格大致区域7列 x 最多6行
@ -255,7 +256,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将自动拖拽 BottomCard 上下切换"
wait_then_do 2 bash -c "
@ -277,7 +278,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
# 打开年视图(点击 FAB → 年视图)
adb shell input tap "$fab_x" "$fab_y"
@ -303,7 +304,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
# 打开年视图
adb shell input tap "$fab_x" "$fab_y"
@ -339,7 +340,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
# 先翻几页离开当月
adb shell input swipe $((screen_w*20/100)) $grid_y $((screen_w*80/100)) $grid_y 200
@ -364,7 +365,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将反复点击 FAB"
wait_then_do 2 bash -c "
@ -382,7 +383,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将反复切换"显示调休""
wait_then_do 2 bash -c "
@ -404,7 +405,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将点击首行左侧(上月)和末行右侧(下月)的灰色日期"
local col_w=$((screen_w / 7))
@ -433,7 +434,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${TOOLS_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${TOOLS_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行工具页面已打开trace 期间保持静态"
;;
@ -445,7 +446,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${DATECHECKER_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${DATECHECKER_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将自动添加行、输入天数、滑动删除"
wait_then_do 2 bash -c "
@ -487,7 +488,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${ABOUT_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${ABOUT_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行关于页面已打开trace 期间保持静态"
;;
@ -499,7 +500,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${LICENSES_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${LICENSES_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行许可页面已打开trace 开始后将上下滑动"
wait_then_do 2 bash -c "
@ -519,7 +520,7 @@ run_scenario() {
echo "[场景] ${SCENARIO_NAME}"
echo " ${SCENARIO_DESC}"
echo ""
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
sleep 1
echo " → 自动执行trace 开始后将按顺序自动执行所有主要交互"
local col_w=$((screen_w / 7))
@ -871,7 +872,7 @@ else
if [ "$NO_LAUNCH" = false ]; then
echo ""
echo "[1/5] 启动应用..."
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
adb shell am start -n "${PACKAGE}/${MAIN_ACTIVITY}" >/dev/null 2>&1 || true || true
sleep 2
else
echo ""