性能优化提效方案:App冷启动优化成绩测试干货分享(一)

背景:

大家做公司的性能优化部分工作时候,有一个非常关键的指标那就是和竞品对比系统各个app的冷启动优化时间,特别是针对一些主流的第三方App,因为他们Apk一般都是同一个,所以参考各个第三方app的冷启动时间,确实可以反应出手机的一个性能如何,一般情况下冷启动时间越短肯定就是性能越好。
在这里插入图片描述

冷启动时间测试方法:

方法一:
直接冷启动看logcat中的输出时间
这里在App进行冷启动过程中会

adb logcat -c;#清理日志
adb shell killall  com.android.settings;#杀死App确保冷启动
adb shell am start -n com.android.settings/.Settings;#启动app
adb logcat | grep Displayed
Starting: Intent { cmp=com.android.settings/.Settings }
01-06 11:27:46.151   496   514 I ActivityTaskManager: Displayed com.android.settings/.Settings for user 0: +1s402ms

方法二:
使用am start -W方法看输出
adb shell am start -n com.android.settings/.Settings -W

解释一下:
-W参数:列出启动过程中统计到的具体数据;
测试结果如下:

adb shell killall  com.android.settings;#进行杀进程确保冷启动
adb shell am start -n com.android.settings/.Settings -W
Starting: Intent { cmp=com.android.settings/.Settings }
Status: ok
LaunchState: COLD
Activity: com.android.settings/.homepage.SettingsHomepageActivity
TotalTime: 1056
WaitTime: 1125
Complete

LaunchState表示冷、热、温启动;

TotalTime表示新应用启动的耗时,包括新进程的启动和Activity的启动耗时(包括创建进程 + Application初始化 + Activity初始化到界面显示的时间),但不包括前一个应用Activity pause的耗时;

WaitTime就是总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间;因此,WaitTime比TotalTime稍大;

开发者一般只要关心TotalTime即可,这个时间才是自己应用真正启动的耗时。上面App冷启动耗时是1056ms。

方法三:
这种方式是需要测试人员使用高速摄像机进行拍摄,整个过程需要人工进行。

大概就是针对手机上的App在桌面进行手动点击,到App完全展示画面,整个过程都有录像或者高速摄像机照片。冷启动时间就是人工观看点击时间点到画面完全展示时间点。
在这里插入图片描述
对于我们开发人员来说,最常用的肯定是方法1,2,不太可能每次优化都用摄像机来拍摄的。但是对于方法1,2每次可以输入adb命令进测试,得到优化结果。
但是对于频繁修改验证,或者冷启动测试可能需要多次测试取出平均值来说每次手动进手动adb输入命令验证肯定是不现实的,方法过于低效。

那么有什么方法可以更加智能化测试这些冷启动时间及比较方便对比优化前后的成绩差异么?
其实这个肯定是可以有的,方法大家应该也都可以想到,可以借助脚本进行测试,脚本进行测试结果报告对比分析等。

下面马哥分享相关的冷启动测试脚本,方便大家进行自动化冷启动相关的测试

分享干货脚本测试冷启动

(注意脚本大部分来自AI编写,进行了一些局部修改,所以AI在脚本编写方面比人工还是快很多)
先看看脚本的使用:

先看看使用帮助

test@test:~/demos$ ./app_launch_benchmark.sh -help
未知参数: -help
App启动时间测试工具 v2.0

用法: ./app_launch_benchmark.sh [选项]

选项:
  -c, --config FILE     指定配置文件 (默认: app_config.cfg)
  -n, --iterations N    指定测试次数 (默认: 3)
  -t, --template        创建配置文件模板
  -T, --test            测试命令格式
  -h, --help            显示帮助信息

示例:
  ./app_launch_benchmark.sh                     # 使用默认配置测试
  ./app_launch_benchmark.sh -c my_apps.cfg -n 5 # 自定义配置,测试5次
  ./app_launch_benchmark.sh -T                  # 测试命令格式

报告文件:
  测试完成后会生成: launch_report_YYYYMMDD_HHMMSS.txt

这里可以看出大家也可以进行配置一些变量
看看配置文件app_config.cfg

test@test:~/demos$ cat app_config.cfg 
# App启动时间测试配置文件
# 格式:包名|Activity类名|显示名称|启动类型|备注

# 系统应用 - 冷启动测试
com.android.settings|com.android.settings.Settings|系统设置|cold|冷启动测试
com.android.dialer|com.android.dialer.main.impl.MainActivity|拨号器|cold|冷启动测试

# 第三方应用
# com.tencent.mm|com.tencent.mm.ui.LauncherUI|微信|cold|社交应用冷启动
# com.taobao.taobao|com.taobao.tao.homepage.MainActivity3|淘宝|warm|电商应用温启动

# 测试参数
WAIT_TIME=3
COLD_WAIT_TIME=5
FORCE_STOP=true
CLEAR_DATA=false
TEST_MODE=all


使用脚本进行测试:
在这里插入图片描述

app_launch_benchmark.sh 脚本源码如下:

#!/bin/bash
# 文件名:app_launch_benchmark.sh
# 描述:安卓App启动时间测试工具

# ============================================
# 配置部分
# ============================================
DEFAULT_CONFIG="app_config.cfg"
DEFAULT_ITERATIONS=3
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="launch_report_${TIMESTAMP}.txt"

# ============================================
# 颜色定义
# ============================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
NC='\033[0m'

# ============================================
# 工具函数
# ============================================

print_msg() {
    local msg="$1"
    local color="$2"
    echo -e "${color}${msg}${NC}"
    # 同时写入报告文件(去掉颜色代码)
    echo -e "$msg" | sed 's/\x1b\[[0-9;]*m//g' >> "$REPORT_FILE"
}

print_to_report() {
    echo "$1" >> "$REPORT_FILE"
}

print_to_both() {
    local msg="$1"
    local color="$2"
    echo -e "${color}${msg}${NC}"
    echo "$msg" >> "$REPORT_FILE"
}

# ============================================
# 核心函数
# ============================================

init_report() {
    # 清空并初始化报告文件
    echo "==========================================" > "$REPORT_FILE"
    echo "          APP启动时间测试报告" >> "$REPORT_FILE"
    echo "==========================================" >> "$REPORT_FILE"
    echo "生成时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$REPORT_FILE"
    echo "配置文件: $CONFIG_FILE" >> "$REPORT_FILE"
    echo "测试次数: $ITERATIONS" >> "$REPORT_FILE"
    echo "设备信息: $(adb shell getprop ro.product.model 2>/dev/null || echo '未知')" >> "$REPORT_FILE"
    echo "" >> "$REPORT_FILE"
    echo "测试详情" >> "$REPORT_FILE"
    echo "--------" >> "$REPORT_FILE"
    echo "" >> "$REPORT_FILE"
}

check_adb() {
    print_msg "检查ADB连接..." "$BLUE"
    
    if ! adb devices | grep -q "device$"; then
        print_msg "错误:未找到已连接的Android设备" "$RED"
        exit 1
    fi
    
    print_msg "✓ ADB连接正常" "$GREEN"
}

create_config_template() {
    cat > app_config_template.cfg << 'EOF'
# App启动时间测试配置文件
# 格式:包名|Activity类名|显示名称

# 系统应用
com.android.deskclock|com.android.deskclock.DeskClock|时钟
com.android.settings|com.android.settings.Settings|系统设置
com.android.dialer|com.android.dialer.main.impl.MainActivity|拨号器

# 第三方应用示例
# com.tencent.mm|com.tencent.mm.ui.LauncherUI|微信
# com.taobao.taobao|com.taobao.tao.homepage.MainActivity3|淘宝

# 测试参数
ITERATIONS=3
WAIT_TIME=2
EOF
    print_msg "配置文件模板已创建:app_config_template.cfg" "$GREEN"
}

parse_config() {
    local config_file=$1
    
    if [[ ! -f "$config_file" ]]; then
        print_msg "错误:配置文件不存在: $config_file" "$RED"
        exit 1
    fi
    
    APPS=()
    ITERATIONS=$DEFAULT_ITERATIONS
    WAIT_TIME=2
    
    # 读取参数
    while IFS='=' read -r key value; do
        key=$(echo "$key" | xargs)
        value=$(echo "$value" | xargs)
        
        case $key in
            ITERATIONS)
                if [[ "$value" =~ ^[0-9]+$ ]] && [[ "$value" -gt 0 ]]; then
                    ITERATIONS=$value
                fi
                ;;
            WAIT_TIME)
                if [[ "$value" =~ ^[0-9]+$ ]] && [[ "$value" -gt 0 ]]; then
                    WAIT_TIME=$value
                fi
                ;;
        esac
    done < "$config_file"
    
    # 读取App配置
    while IFS='|' read -r package activity name; do
        package=$(echo "$package" | xargs)
        activity=$(echo "$activity" | xargs)
        name=$(echo "$name" | xargs)
        
        [[ -z "$package" ]] && continue
        [[ "$package" == \#* ]] && continue
        [[ -z "$activity" ]] && continue
        [[ -z "$name" ]] && name="$package"
        
        APPS+=("$package|$activity|$name")
    done < <(grep -v -E "^(#|$|ITERATIONS|WAIT_TIME)" "$config_file")
    
    if [[ ${#APPS[@]} -eq 0 ]]; then
        print_msg "错误:配置文件中未找到有效的App配置" "$RED"
        exit 1
    fi
    
    print_msg "✓ 加载了 ${#APPS[@]} 个应用" "$GREEN"
    print_msg "测试次数: $ITERATIONS" "$CYAN"
    print_msg "等待时间: ${WAIT_TIME}秒" "$CYAN"
    
    # 记录到报告
    print_to_report "应用数量: ${#APPS[@]}"
    print_to_report "测试次数: $ITERATIONS"
    print_to_report "等待时间: ${WAIT_TIME}秒"
    print_to_report ""
}

stop_app() {
    local package=$1
    adb shell am force-stop "$package" > /dev/null 2>&1
    sleep 0.5
}

launch_and_measure() {
    local package=$1
    local activity=$2
    
    # 停止应用确保冷启动
    stop_app "$package"
    
    # 执行启动命令
    local output
    output=$(adb shell am start -n "$package/$activity" -W 2>&1)
    
    # 调试:输出原始结果
    # echo "DEBUG: $output"
    
    # 解析时间
    local total_time=0
    
    # 尝试多种方式提取TotalTime
    if echo "$output" | grep -q "TotalTime:"; then
        total_time=$(echo "$output" | grep "TotalTime:" | awk '{print $2}' | tr -d '\r')
    elif echo "$output" | grep -q "TotalTime"; then
        total_time=$(echo "$output" | tr ' ' '\n' | grep -A1 "TotalTime" | tail -1 | tr -d '\r')
    fi
    
    # 验证是否为数字
    if ! [[ "$total_time" =~ ^[0-9]+$ ]]; then
        total_time=0
    fi
    
    echo "$total_time"
}

test_single_app() {
    local package=$1
    local activity=$2
    local name=$3
    local app_num=$4
    local total_apps=$5
    
    print_to_both "" ""
    print_to_both "========================================" "$PURPLE"
    print_to_both "[$app_num/$total_apps] 测试应用: $name" "$YELLOW"
    print_to_both "包名: $package" "$CYAN"
    print_to_both "Activity: $activity" "$CYAN"
    print_to_both "----------------------------------------" "$PURPLE"
    
    local times=()
    local success_count=0
    
    for ((i=1; i<=ITERATIONS; i++)); do
        print_msg "  第 $i/$ITERATIONS 次测试..." "$BLUE"
        print_to_report "  第 $i 次测试:"
        
        local launch_time=$(launch_and_measure "$package" "$activity")
        
        if [[ "$launch_time" -gt 0 ]]; then
            times+=("$launch_time")
            success_count=$((success_count + 1))
            
            # 显示结果
            local status_msg="    耗时: ${launch_time}ms"
            if [[ "$launch_time" -lt 500 ]]; then
                print_msg "${status_msg} 🚀" "$GREEN"
            elif [[ "$launch_time" -lt 1000 ]]; then
                print_msg "${status_msg} ⚡" "$GREEN"
            elif [[ "$launch_time" -lt 2000 ]]; then
                print_msg "${status_msg}" "$YELLOW"
            else
                print_msg "${status_msg} 🐌" "$RED"
            fi
            
            # 记录到报告
            print_to_report "    结果: ${launch_time}ms"
        else
            print_msg "    ✗ 启动失败" "$RED"
            print_to_report "    结果: 启动失败"
        fi
        
        # 返回桌面,等待下一次测试
        adb shell input keyevent KEYCODE_HOME
        sleep "$WAIT_TIME"
    done
    
    # 显示和记录统计结果
    if [[ ${#times[@]} -gt 0 ]]; then
        calculate_and_record_stats "$name" times[@]
    else
        print_msg "  ✗ 所有测试均失败" "$RED"
        print_to_report "  统计结果: 所有测试均失败"
    fi
    
    print_to_both "========================================" "$PURPLE"
    print_to_both "" ""
}

calculate_and_record_stats() {
    local name=$1
    local times_array=("${!2}")
    
    local sum=0
    local count=${#times_array[@]}
    local min=999999
    local max=0
    
    for time in "${times_array[@]}"; do
        sum=$((sum + time))
        if [[ $time -lt $min ]]; then min=$time; fi
        if [[ $time -gt $max ]]; then max=$time; fi
    done
    
    local avg=$((sum / count))
    
    # 计算标准差
    local variance_sum=0
    for time in "${times_array[@]}"; do
        local diff=$((time - avg))
        variance_sum=$((variance_sum + diff * diff))
    done
    local std_dev=$(echo "scale=0; sqrt($variance_sum / $count)" | bc 2>/dev/null || echo 0)
    
    # 显示统计结果
    print_msg "  📊 统计结果:" "$CYAN"
    print_msg "    成功次数: $count/$ITERATIONS" "$CYAN"
    print_msg "    平均时间: ${avg}ms" "$CYAN"
    print_msg "    最短时间: ${min}ms" "$CYAN"
    print_msg "    最长时间: ${max}ms" "$CYAN"
    
    if [[ "$std_dev" -gt 0 ]]; then
        print_msg "    标准差: ${std_dev}ms" "$CYAN"
    fi
    
    # 评价
    local evaluation=""
    if [[ $avg -lt 300 ]]; then
        evaluation="🚀 极快"
    elif [[ $avg -lt 600 ]]; then
        evaluation="⚡ 快速"
    elif [[ $avg -lt 1000 ]]; then
        evaluation="✅ 良好"
    elif [[ $avg -lt 2000 ]]; then
        evaluation="⚠️ 一般"
    else
        evaluation="🐌 较慢"
    fi
    print_msg "    评价: $evaluation" "$GREEN"
    
    # 记录到报告
    print_to_report "  统计结果:"
    print_to_report "    成功次数: $count/$ITERATIONS"
    print_to_report "    平均时间: ${avg}ms"
    print_to_report "    最短时间: ${min}ms"
    print_to_report "    最长时间: ${max}ms"
    if [[ "$std_dev" -gt 0 ]]; then
        print_to_report "    标准差: ${std_dev}ms"
    fi
    print_to_report "    评价: $evaluation"
}

generate_summary() {
    print_to_both "" ""
    print_to_both "========================================" "$PURPLE"
    print_to_both "              测试总结" "$YELLOW"
    print_to_both "========================================" "$PURPLE"
    
    # 这里可以添加总结逻辑
    print_to_both "测试完成时间: $(date '+%Y-%m-%d %H:%M:%S')" "$CYAN"
    print_to_both "报告文件: $REPORT_FILE" "$GREEN"
    
    print_to_both "" ""
    print_to_both "提示: 查看详细结果请打开报告文件:" "$BLUE"
    print_to_both "  cat $REPORT_FILE" "$CYAN"
    print_to_both "  或" "$BLUE"
    print_to_both "  less $REPORT_FILE" "$CYAN"
}

run_all_tests() {
    print_msg "开始测试..." "$YELLOW"
    print_msg "总共 ${#APPS[@]} 个应用,每个测试 $ITERATIONS 次" "$BLUE"
    echo ""
    
    # 初始化报告
    init_report
    
    local app_index=1
    local total_apps=${#APPS[@]}
    
    for app_info in "${APPS[@]}"; do
        IFS='|' read -r package activity name <<< "$app_info"
        
        # 测试单个应用
        test_single_app "$package" "$activity" "$name" "$app_index" "$total_apps"
        
        app_index=$((app_index + 1))
    done
    
    # 生成总结
    generate_summary
    
    print_msg "" ""
    print_msg "✓ 测试完成!" "$GREEN"
    print_msg "详细报告已保存到: $REPORT_FILE" "$CYAN"
    
    # 显示报告最后几行
    echo ""
    print_msg "报告最后几行内容:" "$YELLOW"
    tail -10 "$REPORT_FILE" | while read line; do
        echo "  $line"
    done
}

test_command_format() {
    print_msg "测试命令格式..." "$CYAN"
    
    local test_package="com.android.deskclock"
    local test_activity="com.android.deskclock.DeskClock"
    
    print_msg "执行命令: adb shell am start -n $test_package/$test_activity -W" "$BLUE"
    
    local output
    output=$(adb shell am start -n "$test_package/$test_activity" -W 2>&1)
    
    echo "命令输出:"
    echo "$output"
    echo ""
    
    local total_time=$(echo "$output" | grep "TotalTime" | awk '{print $2}' 2>/dev/null)
    if [[ -n "$total_time" ]] && [[ "$total_time" =~ ^[0-9]+$ ]]; then
        print_msg "✓ 命令执行成功,获取到时间: ${total_time}ms" "$GREEN"
        return 0
    else
        print_msg "✗ 命令执行失败或格式不正确" "$RED"
        echo "建议:"
        echo "1. 检查包名和Activity是否正确"
        echo "2. 手动执行命令测试: adb shell am start -n com.android.deskclock/com.android.deskclock.DeskClock -W"
        return 1
    fi
}

# ============================================
# 主程序
# ============================================

show_help() {
    cat << EOF
App启动时间测试工具作者:AI+(千里马wx号:androidframework007) v2.0

用法: $0 [选项]

选项:
  -c, --config FILE     指定配置文件 (默认: app_config.cfg)
  -n, --iterations N    指定测试次数 (默认: 3)
  -t, --template        创建配置文件模板
  -T, --test            测试命令格式
  -h, --help            显示帮助信息

示例:
  $0                     # 使用默认配置测试
  $0 -c my_apps.cfg -n 5 # 自定义配置,测试5次
  $0 -T                  # 测试命令格式

报告文件:
  测试完成后会生成: launch_report_YYYYMMDD_HHMMSS.txt
EOF
}

# 参数解析
CONFIG_FILE="$DEFAULT_CONFIG"
ITERATIONS="$DEFAULT_ITERATIONS"
TEST_CMD=false

while [[ $# -gt 0 ]]; do
    case $1 in
        -c|--config)
            CONFIG_FILE="$2"
            shift 2
            ;;
        -n|--iterations)
            if [[ "$2" =~ ^[0-9]+$ ]] && [[ "$2" -gt 0 ]]; then
                ITERATIONS="$2"
            fi
            shift 2
            ;;
        -t|--template)
            create_config_template
            exit 0
            ;;
        -T|--test)
            TEST_CMD=true
            shift
            ;;
        -h|--help)
            show_help
            exit 0
            ;;
        *)
            print_msg "未知参数: $1" "$RED"
            show_help
            exit 1
            ;;
    esac
done

# 主函数
main() {
    echo ""
    print_msg "========================================" "$PURPLE"
    print_msg "      APP启动时间测试工具 v2.0" "$YELLOW"
    print_msg "========================================" "$PURPLE"
    echo ""
    
    # 检查ADB
    check_adb
    
    if [[ "$TEST_CMD" == true ]]; then
        test_command_format
        exit 0
    fi
    
    # 解析配置
    parse_config "$CONFIG_FILE"
    
    # 运行测试
    run_all_tests
}

# 运行主函数
main

原文地址:
https://mp.weixin.qq.com/s/vW6-zag2CKrFgcYAPdeUFg

更多framework实战开发干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值