アプリのパフォーマンススクリプト
パフォーマンステストは、FireタブレットやFire TVデバイスにおける互換性、信頼性、速度、応答時間、安定性、リソース使用量などのアプリのパフォーマンス指標をテストするプロセスです。このテストでは、開発者とのコミュニケーションを通じてパフォーマンスのボトルネックを特定し、パフォーマンスを向上させます。モバイルアプリのテストは主要パフォーマンス指標(KPI)主導型で、特定のKPIとステップのセットがAmazonデバイス上で実行され、Amazon/汎用デバイスのリソース(ログなど)を使用して指標が計算されます。
このページでは、Amazonアプリストアに申請されたアプリのパフォーマンスKPIをテストするために必要な手順とスニペットを紹介します。テストの対象となる基本KPIは、レイテンシ、使用準備完了、メモリ、ビデオストリーミングです。
セットアップ
開始する前に、開発用コンピューターに次のソフトウェアパッケージをインストールします。
ソフトウェアパッケージのインストールに加えて、次のことを行う必要があります。
- JAVA_HOMEフォルダとANDROID_HOMEフォルダのパスを設定します。
- デバイスで開発者モードを有効にし、USBデバッグを有効にします。手順については、Amazon Fire TVでデバッグを有効にするを参照してください。
- デバイスの
uid
またはDevice_Identifierをキャプチャします。手順については、DSNを参照してください。
テスト戦略
テスト中、アプリランチャーインテントまたはMonkeyツールを使用して、アプリが数回起動され、強制停止されます。ADB logcatのキャプチャ、ナビゲーションアクション(使用準備完了およびメモリKPI)の実行、バイタル/ADBログからのタイマー値の取得、アプリを強制停止する前のメモリとRAMの使用量のキャプチャなど、イテレーションのたびに、特定のアクションを実行する必要があります。このループは、設定されたイテレーションの回数だけ継続されます。テスト結果はネットワークの状態、システム負荷、その他の要因の影響を受ける可能性があるため、外部要因による干渉を平均化するには複数回のイテレーションを行う必要があります。レイテンシのKPIでは最低50回のイテレーションを、使用準備完了KPIでは最低10回、メモリKPIでは最低5回のイテレーションを実行することをお勧めします。
パフォーマンステストでキャプチャされたログ、スクリーンショット、およびその他のアーティファクトは、後でデバッグやその他の重要な情報に使用できます。Appiumデバイスオブジェクトは、ティアダウンの一環として強制停止されます。
デバイスタイプの取得
テスト自動化スクリプトに追加できるテストメソッドの例として、次のコードサンプルを使用してください。
public String get_device_type() {
String deviceType = null;
try (BufferedReader read = new BufferedReader(new InputStreamReader
(Runtime.getRuntime().exec
("adb -s "+ DSN +" shell getprop ro.build.configuration")
.getInputStream())))
{
String outputLines = read.readLine();
switch (outputLines) {
case "tv":
deviceType = "FTV";
break;
case "tablet":
deviceType = "Tablet";
break;
}
catch (Exception e) {
System.***out***.println("デバイスタイプ情報の取得中に例外が発生しました: " + e);
}
return deviceType;
}
import java.io.BufferedReader
import java.io.InputStreamReader
fun getDeviceType(): String? {
var deviceType: String? = null
try {
BufferedReader(InputStreamReader(Runtime.getRuntime().exec("adb -s $DSN shell getprop ro.build.configuration").inputStream)).use { read ->
val outputLines = read.readLine()
when (outputLines) {
"tv" -> deviceType = "FTV"
"tablet" -> deviceType = "Tablet"
}
}
} catch (e: Exception) {
println("デバイスタイプ情報の取得中に例外が発生しました: $e")
}
return deviceType
}
アプリのメインランチャーアクティビティの生成
次のコードサンプルを使用して、メインランチャーアクティビティを生成します。このメソッドは、テスト対象のアプリのメインアクティビティを取得し、アプリパッケージとメインアクティビティを組み合わせてメインランチャーアクティビティを生成します。
try (BufferedReader read = new BufferedReader(new InputStreamReader
(Runtime.getRuntime().exec("adb -s "+ DSN +" shell pm dump"+ appPackage +" | grep -A 1 MAIN").getInputStream()))) {
String outputLine = null;
while ((line = read.readLine()) != null) {
if (line.contains(appPackage + "/")) {
break;
}
}
outputLine = outputLine.split("/")[1];
outputLine = outputLine.split(" ")[0];
String appLauncherIntent = appPackage + "/" + outputLine;
return appLauncherIntent
}
catch (Exception e) {
System.out.println("アプリのメインアクティビティを生成中に例外が発生しました" + e);
}
import java.io.BufferedReader
import java.io.InputStreamReader
try {
val process = Runtime.getRuntime().exec("adb -s $DSN shell pm dump $appPackage | grep -A 1 MAIN")
val inputStream = process.inputStream
val reader = BufferedReader(InputStreamReader(inputStream))
var line: String? = null
var outputLine: String? = null
while (reader.readLine().also { line = it } != null) {
if (line!!.contains("$appPackage/")) {
break
}
}
outputLine = outputLine!!.split("/")[1]
outputLine = outputLine!!.split(" ")[0]
val appLauncherIntent = "$appPackage/$outputLine"
appLauncherIntent
} catch (e: Exception) {
println("アプリのメインアクティビティを生成中に例外が発生しました $e")
}
メインランチャーアクティビティ(アプリインテント)でのアプリの起動
次のコードサンプルを使用して、アプリインテントまたはアプリパッケージとメインアクティビティを使用してアプリを起動します。
try (BufferedReader read = new BufferedReader(new InputStreamReader
(Runtime.getRuntime().exec("adb -s "+ DSN +" shell am start -n " + appActivity).getInputStream()))) {
String deviceName = getDeviceName(DSN);
String line;
while ((line = read.readLine()) != null) {
if (line.startsWith("Starting: Intent")) {
System.out.println("アプリインテントを使用してアプリが正常に起動されました - " + appIntent);
break;
} else if (line.contains("Error")) {
System.out.println("App Launch Error");
}
}
} catch (Exception e) {
System.out.println("アプリインテントを使用してアプリを起動中に例外が発生しました" + e);
}
import java.io.BufferedReader
import java.io.InputStreamReader
val process = Runtime.getRuntime().exec("adb -s $DSN shell am start -n $appActivity")
val inputStream = process.inputStream
val reader = BufferedReader(InputStreamReader(inputStream))
try {
val deviceName = getDeviceName(DSN)
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line!!.startsWith("Starting: Intent")) {
println("アプリインテントを使用してアプリが正常に起動されました - $appIntent")
break
} else if (line!!.contains("Error")) {
println("App Launch Error")
}
}
} catch (e: Exception) {
println("アプリインテントを使用してアプリを起動中に例外が発生しました $e")
}
Monkeyツールを使用したアプリの起動
Monkeyツールを使用してアプリを起動するには、次のコードを使用します。
try {
String monkeyCommand = null;
if (DEVICE_TYPE.equals(FTV)) {
monkeyCommand = " shell monkey --pct-syskeys 0 -p "
}
else {
monkeyCommand = " shell monkey -p "
}
BufferedReader launchRead = new BufferedReader(new InputStreamReader
(Runtime.getRuntime().exec("adb -s "+ DSN + monkeyCommand + appPackage +" -c android.intent.category.LAUNCHER 1").getInputStream()));
String line;
while ((line = launchRead.readLine()) != null) {
if (line.contains("Events injected")) {
System.out.println("Monkeyツールを使用してアプリが正常に起動されました - " + appPackage);
launchRead.close();
return true;
}
else if (line.contains("エラー") || line.contains("アクティビティが見つかりませんでした")) {
System.out.println("Monkeyツールからアプリを起動中にエラーが発生しました。起動にインテントを使用しています");
launchRead.close();
return false;
}
}
}
catch (Exception e) {
System.out.println("Monkeyを使用してアプリを起動中に例外が発生しました" + e);
return false;
}
try {
val monkeyCommand: String?
if (DEVICE_TYPE == FTV) {
monkeyCommand = " shell monkey --pct-syskeys 0 -p "
}
else {
monkeyCommand = " shell monkey -p "
}
val launchRead = BufferedReader(InputStreamReader(
Runtime.getRuntime().exec("adb -s $DSN $monkeyCommand $appPackage -c android.intent.category.LAUNCHER 1").inputStream))
var line: String?
while (launchRead.readLine().also { line = it } != null) {
if (line!!.contains("Events injected")) {
println("Monkeyツールを使用してアプリが正常に起動されました - $appPackage")
launchRead.close()
return true
}
else if (line!!.contains("エラー") || line!!.contains("アクティビティが見つかりませんでした")) {
println("Monkeyツールからアプリを起動中にエラーが発生しました。起動にインテントを使用しています")
launchRead.close()
return false
}
}
}
catch (e: Exception) {
println("Monkeyを使用してアプリを起動中に例外が発生しました $e")
return false
}
アプリの強制停止
次のコードサンプルを使用して、アプリを強制停止します。
try {
Runtime.getRuntime().exec("adb -s "+ DSN +" shell am force-stop " + appPackage);
System.out.println("アプリを強制停止しました - " + appPackage);
}
catch (Exception e) {
System.out.println("アプリを強制停止中に例外が発生しました" + e);
}
try {
Runtime.getRuntime().exec("adb -s ${DSN} shell am force-stop ${appPackage}")
println("アプリを強制停止しました - $appPackage")
} catch (e: Exception) {
println("アプリを強制停止中に例外が発生しました: $e")
}
レイテンシKPI
最初のフレームまでのレイテンシは、アプリが起動してから、アプリによって最初のフレームが描画されるまでにかかる時間です。これは、さまざまな方法で起動中のアプリのパフォーマンスを測定するために使用されます。このKPIは、ユーザーの行動を再現し、ユーザーエクスペリエンスを向上させるために使用されます。
アプリをプログラムによって起動する前に、アプリのパッケージ名、main_activity、その他の詳細など、基本的なパラメーターが必要です。
ログで使用される一般的な略語
public String COOL_APP = "cool_app_launch_time";
public String COOL_ACTIVITY = "cool_activity_launch_time";
public String WARM_APP_WARM = "warm_app_warm_transition_launch_time";
public String WARM_APP_COOL = "warm_app_cool_transition_launch_time";
public String AM_ACTIVITY_LAUNCH_TIME = "am_activity_launch_time:";
public String WM_ACTIVITY_LAUNCH_TIME = "wm_activity_launch_time:";
public String WARM_ACTIVITY_LAUNCH_TIME = "performance:warm_activity_launch_time:";
public String AM_FULLY_DRAWN = "am_activity_fully_drawn_time:";
public String WM_FULLY_DRAWN = "wm_activity_fully_drawn_time:";
const val COOL_APP: String = "cool_app_launch_time"
const val COOL_ACTIVITY: String = "cool_activity_launch_time"
const val WARM_APP_WARM: String = "warm_app_warm_transition_launch_time"
const val WARM_APP_COOL: String = "warm_app_cool_transition_launch_time"
const val AM_ACTIVITY_LAUNCH_TIME: String = "am_activity_launch_time:"
const val WM_ACTIVITY_LAUNCH_TIME: String = "wm_activity_launch_time:"
const val WARM_ACTIVITY_LAUNCH_TIME: String = "performance:warm_activity_launch_time:"
const val AM_FULLY_DRAWN: String = "am_activity_fully_drawn_time:"
const val WM_FULLY_DRAWN: String = "wm_activity_fully_drawn_time:"
使用される一般的なADB logcatコマンド
次のコードサンプルを使用して、ADBログをスレッドタイムでキャプチャし、アプリのパフォーマンスのレイテンシ指標を取得します。
public String ADB_LOGCAT_DUMP = "adb shell logcat -v threadtime -b all -d";
public String ADB_LOGCAT_CLEAR = "adb shell logcat -v threadtime -b all -c";
//以下のメソッドは、デバイスのDSN値を指定してadbコマンドを追加するために使用されます
public Process adb(String DSN, String message) {
Process process = null;
try {
process = Runtime.getRuntime().exec("adb -s " + DSN + message);
}
catch (Exception e) {
System.out.println("adbコマンドの実行中に例外が発生しました" + e);
}
return process;
}
public const val ADB_LOGCAT_DUMP: String = "adb shell logcat -v threadtime -b all -d"
public const val ADB_LOGCAT_CLEAR: String = "adb shell logcat -v threadtime -b all -c"
//以下の関数は、デバイスのDSN値を指定してadbコマンドを追加するために使用されます
fun adb(DSN: String, message: String): Process? {
return try {
Runtime.getRuntime().exec("adb -s $DSN$message")
}
catch (e: Exception) {
println("adbコマンドの実行中に例外が発生しました$e")
null
}
}
バイタルバッファーログからタイマー値をキャプチャ
次のコードサンプルを使用して、バイタルバッファーからパフォーマンスレイテンシタイマー値を除外します。
public int get_vitals_timer(File logFile, String appPackage, String metricSearch) {
BufferedReader reader = null;
String line_Metric;
int timer = 0;
try {
reader = new BufferedReader(new FileReader(logFile))
while ((line_Metric = reader.readLine()) != null) {
if (line_Metric.contains("performance:" + metricSearch)
&& line_Metric.contains("key=" + appPackage)) {
timer = splitToTimer(line_Metric);
}
}
catch (Exception e) {
System.out.println(e);
}
finally {
reader.close();
}
return timer;
}
import java.io.File
fun getVitalsTimer(logFile: File, appPackage: String, metricSearch: String): Int {
var reader: BufferedReader? = null
var lineMetric: String
var timer = 0
try {
reader = logFile.bufferedReader()
while (reader.readLine().also { lineMetric = it } != null) {
if (lineMetric.contains("performance:$metricSearch") && lineMetric.contains("key=$appPackage")) {
timer = splitToTimer(lineMetric)
}
}
} catch (e: Exception) {
println(e)
} finally {
reader?.close()
}
return timer
}
アクティビティマネージャーのバッファーログからタイマー値をキャプチャ
次のコードは、アクティビティまたはウィンドウマネージャーのバッファーを使用してアプリのタイマー値を除外します。
public int get_displayed_timer(File adb_log_file, String appPackage) {
BufferedReader br = null;
int timer = 0;
String displayedMarker = null;
try {
br = new BufferedReader(new FileReader(adb_log_file));
while ((displayedMarker = br.readLine()) != null) {
if ((displayedMarker.contains(AM_ACTIVITY_LAUNCH_TIME) ||
displayedMarker.contains(WM_ACTIVITY_LAUNCH_TIME) ||
displayedMarker.contains(WARM_ACTIVITY_LAUNCH_TIME)) &&
displayedMarker.contains(appPackage))
break;
}
if (displayedMarker != null) {
displayedMarker = displayedMarker.split("]")[0].split("\\[")[1];
ffValue = Integer.parseInt(displayedMarker);
}
} catch (Exception e) {
System.out.println(e);
} finally {
br.close();
}
return timer;
}
fun getDisplayedTimer(adbLogFile: File, appPackage: String): Int {
var br: BufferedReader? = null
var timer: Int = 0
var displayedMarker: String? = null
try {
br = BufferedReader(FileReader(adbLogFile))
while (displayedMarker != null) {
if (displayedMarker.contains(AM_ACTIVITY_LAUNCH_TIME) ||
displayedMarker.contains(WM_ACTIVITY_LAUNCH_TIME) ||
displayedMarker.contains(WARM_ACTIVITY_LAUNCH_TIME)) &&
displayedMarker.contains(appPackage)) {
break
} else {
displayedMarker = br.readLine()
}
}
if (displayedMarker != null) {
displayedMarker = displayedMarker.split("]")[0].split("\\[")[1]
timer = Integer.parseInt(displayedMarker)
}
} catch (e: Exception) {
println(e)
} finally {
br?.close()
}
return timer
}
クール起動から最初のフレームまで
クール起動から最初のフレームまでのレイテンシは、ホーム画面からアプリを起動するか、強制停止後にアプリを起動してから、アプリの最初のフレームを描画するまでにかかる時間を測定します。この指標は、このタイプの起動におけるアプリのパフォーマンスを測定します。これは、強制停止後にサービスを読み込んでキャッシュに保持する場合、通常、アプリの起動により多くの時間がかかるためです。
クール起動から最初のフレームまでのレイテンシを測定するには、次の手順に従います。
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- logcatコマンド
adb shell logcat -v threadtime -b all -d
を実行して、cool_app_launch_time
タイマー値をキャプチャします。 - アプリが完全に起動したら、次のコマンドを実行しアプリを強制停止します:
adb shell am force-stop <app_pkg>
- 必要なイテレーションの数だけ、この手順を繰り返します。
クールイテレーション
次のコードサンプルを使用して、クール起動のレイテンシテストを実行し、要求されたイテレーションのレイテンシ値を出力します。
for (int i = 0; i < iterations; i++) {
if (!launchAppUsingMonkey(DSN, appPackage))
launchAppUsingIntent(DSN, appIntent);
Thread.sleep(10);
BufferedReader read = new BufferedReader
(new InputStreamReader(Runtime.getRuntime()
.exec("adb -s "+ DSN +" shell logcat -v threadtime -b all -d")
.getInputStream()));
String line_CoolApp;
String line_CoolActivity;
log_file_writer(read);
File adb_log_file = new File( kpi_log_file_path + "/KPI_Log.txt");
if (deviceType == "Tablet") {
timer = get_vitals_timer(adb_log_file, appPackage, COOL_APP);
} else if (deviceType == "FTV") {
timer = get_displayed_timer(adb_log_file, appPackage);
if (timer == 0) {
timer = get_vitals_timer(adb_log_file, appPackage, COOL_APP);
}
}
if (timer == 0) {
timer = get_vitals_timer(adb_log_file, appPackage, COOL_ACTIVITY);
}
forceStopApp(DSN, appPackage);
Thread.sleep(10);
Runtime.getRuntime().exec("adb -s "+ DSN +" shell logcat -b all -c");
}
for (int i = 0; i < iterations; i++) {
if (!launchAppUsingMonkey(DSN, appPackage)) {
launchAppUsingIntent(DSN, appIntent)
}
Thread.sleep(10)
val read = BufferedReader(InputStreamReader(Runtime.getRuntime().exec("adb -s ${DSN} shell logcat -v threadtime -b all -d").getInputStream()))
val line_CoolApp = read.readLine()
val line_CoolActivity = read.readLine()
log_file_writer(read)
val adb_log_file = File(kpi_log_file_path + "/KPI_Log.txt")
when (deviceType) {
"Tablet" -> timer = get_vitals_timer(adb_log_file, appPackage, COOL_APP)
"FTV" -> timer = get_displayed_timer(adb_log_file, appPackage)
.takeIf { it != 0 }
?: get_vitals_timer(adb_log_file, appPackage, COOL_APP)
}
if (timer == 0) {
timer = get_vitals_timer(adb_log_file, appPackage, COOL_ACTIVITY)
}
forceStopApp(DSN, appPackage)
Thread.sleep(10)
Runtime.getRuntime().exec("adb -s ${DSN} shell logcat -b all -c")
}
デバイスのクールバイタルログ
デバイスのクールバイタルログの出力例を次に示します。
03-03 12:42:32.589 892 992 I Vlog : PhoneWindowManager:ScreenTime:fgtracking=false;DV;1,Timer=1.0;TI;1,unit=count;DV;1,metadata=!{"d"#{"groupId"#"<APP_PACKAGE_NAME>"$"schemaId"#"123"$"startTimeMs"#"1.677827544773E12"$"packageName"#"<APP_PACKAGE_NAME>"$"endTimeMs"#"1.677827552587E12"$"durationMs"#"7813.0"}};DV;1:HI
03-03 12:42:33.657 892 1092 I Vlog : performance:cool_app_launch_time:fgtracking=false;DV;1,key=<APP_PACKAGE_NAME>;DV;1,**Timer****=****1333.0**;TI;1,unit=ms;DV;1,metadata=App_Metadata!{"d"#{"groupId"#"abc"$"schemaId"#"abc"$"app_version"#"123"$"isChild"#"false"}$"m"#{"prev"#"<APP_PACKAGE_NAME>"$"context"#"abc"}};DV;1:HI
03-03 12:42:33.661 892 1092 I Vlog : performance:user_app_launch_cool:fgtracking=false;DV;1,key=<APP_PACKAGE_NAME>;DV;1,Counter=1;CT;1,unit=count;DV;1,metadata=App_Metadata!{"d"#{"groupId"#"abc"$"schemaId"#"abc"$"isChild"#"false"}};DV;1:HI
03-03 12:49:20.880 892 1092 I Vlog : performance:cool_app_launch_time:fgtracking=false;DV;1,key=<APP_PACKAGE_NAME>;DV;1,**Timer****=****1225.0**;TI;1,unit=ms;DV;1,metadata=App_Metadata!{"d"#{"groupId"#"abc"$"schemaId"#"abc"$"app_version"#"123"$"isChild"#"false"}$"m"#{"prev"#"<APP_PACKAGE_NAME>"$"context"#"abc"}};DV;1:HI
03-03 12:49:20.918 892 1092 I Vlog : performance:user_app_launch_cool:fgtracking=false;DV;1,key=<APP_PACKAGE_NAME>;DV;1,Counter=1;CT;1,unit=count;DV;1,metadata=App_Metadata!{"d"#{"groupId"#"abc"$"schemaId"#"abc"$"isChild"#"false"}};DV;1:HI
ウォーム起動から最初のフレームまで
ウォーム起動から最初のフレームまでのレイテンシは、バックグラウンドまたは最近使用したアプリからアプリを起動してから、最初のフレームを描画するまでにかかる時間を測定します。この指標はアプリのパフォーマンスを測定するために使用されます。バックグラウンドから起動する場合は、必要なサービスがキャッシュ内で利用でき、アプリの起動にかかる時間が短くなるためです。
ウォーム起動から最初のフレームまでのレイテンシを測定するには、次の手順に従います。
- アプリがバックグラウンドで実行されていることを確認します。
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- logcatコマンド
adb shell logcat -v threadtime -b all -d
を実行して、warm_app_warm_transition_launch_time
タイマー値をキャプチャします。 - アプリが完全に起動したら、
adb shell input keyevent KEYCODE_HOME
を実行します。 - 必要なイテレーションの数だけ、この手順を繰り返します。
ウォームイテレーション
次のコードサンプルを使用して、ウォーム起動のレイテンシテストを実行し、要求されたイテレーションのレイテンシ値を出力します。
for (int i = 0; i < iterations; i++) {
if (!launchAppUsingMonkey(DSN, appPackage))
launchAppUsingIntent(DSN, appIntent);
Thread.sleep(10);
BufferedReader read = new BufferedReader
(new InputStreamReader(Runtime.getRuntime()
.exec("adb -s "+ DSN +" shell logcat -v threadtime -b all -d")
.getInputStream()));
String line_CoolApp;
String line_CoolActivity;
log_file_writer(read);
String adb_log_file = kpi_log_file_path + "/KPI_Log.txt";
if (deviceType == "Tablet") {
timer = get_vitals_timer(adb_log_file, appPackage, WARM_APP_WARM);
} else if (deviceType == "FTV") {
timer = get_displayed_timer(adb_log_file, appPackage);
if (timer == 0) {
timer = get_vitals_timer(adb_log_file, appPackage, WARM_APP_WARM);
}
}
if (timer == 0) {
timer = get_vitals_timer(adb_log_file, appPackage, WARM_APP_COOL);
}
Runtime.getRuntime().exec("adb -s "+ DSN +" shell input keyevent KEYCODE_HOME");
Thread.sleep(10);
Runtime.getRuntime().exec("adb -s "+ DSN +" shell logcat -b all -c");
}
for (int i = 0; i < iterations; i++) {
if (!launchAppUsingMonkey(DSN, appPackage))
launchAppUsingIntent(DSN, appIntent)
Thread.sleep(10)
val read = BufferedReader(InputStreamReader(Runtime.getRuntime().exec("adb -s ${DSN} shell logcat -v threadtime -b all -d").getInputStream()))
val line_CoolApp = read.readLine()
val line_CoolActivity = read.readLine()
log_file_writer(read)
val adb_log_file = kpi_log_file_path + "/KPI_Log.txt"
when (deviceType) {
"Tablet" -> timer = get_vitals_timer(adb_log_file, appPackage, WARM_APP_WARM)
"FTV" -> timer = get_displayed_timer(adb_log_file, appPackage)
.takeIf { it != 0 } ?: get_vitals_timer(adb_log_file, appPackage, WARM_APP_WARM)
}
if (timer == 0) {
timer = get_vitals_timer(adb_log_file, appPackage, WARM_APP_COOL)
}
Runtime.getRuntime().exec("adb -s ${DSN} shell input keyevent KEYCODE_HOME")
Thread.sleep(10)
Runtime.getRuntime().exec("adb -s ${DSN} shell logcat -b all -c")
}
デバイスのウォームバイタルログ
デバイスのウォームバイタルログの出力例を次に示します。
03-03 12:51:16.367 892 1066 I Vlog : Thermal:ScreenOn_SensorThrottling_P2P_Off:fgtracking=false;DV;1,key=0;DV;1,Timer=8.072222222222222E-4;TI;1,unit=hours;DV;1,metadata=<APP_PACKAGE_NAME>!{"d"#{"groupId"#"123"$"schemaId"#"123"$"zone"#"soc"}};DV;1:HI
03-03 12:51:16.367 892 1066 I Vlog : Thermal:ScreenOn_SensorThrottling_P2P_Off:fgtracking=false;DV;1,key=0;DV;1,Timer=8.072222222222222E-4;TI;1,unit=hours;DV;1,metadata=<APP_PACKAGE_NAME>!{"d"#{"groupId"#"123"$"schemaId"#"123"$"zone"#"bottom"}};DV;1:HI
03-03 12:51:16.367 892 1066 I Vlog : Thermal:ScreenOn_SensorThrottling_P2P_Off:fgtracking=false;DV;1,key=0;DV;1,Timer=8.072222222222222E-4;TI;1,unit=hours;DV;1,metadata=<APP_PACKAGE_NAME>!{"d"#{"groupId"#"123"$"schemaId"#"123"$"zone"#"side"}};DV;1:HI
03-03 12:51:16.368 892 1066 I Vlog : Thermal:Screen_On_Thermal_Throttling_P2P_Off:fgtracking=false;DV;1,key=0;DV;1,Timer=8.075E-4;TI;1,unit=hours;DV;1,metadata=<APP_PACKAGE_NAME>!{"d"#{"groupId"#"123"$"schemaId"#"abc"}};DV;1:HI
03-03 12:51:16.384 892 1092 I Vlog : performance:warm_app_warm_transition_launch_time:fgtracking=false;DV;1,key=<APP_PACKAGE_NAME>;DV;1,**Timer****=****191.0**;TI;1,unit=ms;DV;1,metadata=<APP_PACKAGE_NAME>!{"d"#{"groupId"#"123"$"schemaId"#"abc"$"app_version"#"123"$"isChild"#"false"}$"m"#{"prev"#"<APP_PACKAGE_NAME>"$"context"#"1234"}};DV;1:HI
03-03 12:51:16.395 892 1092 I Vlog : performance:user_app_launch_warm:fgtracking=false;DV;1,key=<APP_PACKAGE_NAME>;DV;1,Counter=1;CT;1,unit=count;DV;1,metadata=<APP_PACKAGE_NAME>!{"d"#{"groupId"#"123"$"schemaId"#"abc"$"isChild"#"false"}};DV;1:HI
使用準備完了
使用準備完了(RTU)KPIは、アプリが起動してから使用できる状態(アプリのサインインページやホームページなど)になるまでにかかった時間として測定されます。RTUは、アプリ起動時のアプリのパフォーマンスを測定するために使用され、アプリ内の問題を特定するのに役立ちます。このKPIはユーザーのユースケースを再現し、エンドユーザーに最適なエクスペリエンスを提供します。
- サインイン前のRTUとは、サインイン前にRTU画面を測定することを意味します。サインイン前のクール起動またはウォーム起動を実行する場合は、アプリにログインしていないことを確認してください。
- サインイン後のRTUとは、サインイン後にRTU画面を測定することを意味します。サインイン後のクール起動またはウォーム起動を実行する場合は、テスト用の認証情報を使用してアプリにログインしていることを確認してください。
サインイン前のクール起動のRTUを測定する手順
サインイン前のクール起動のRTUとは、ホーム画面からアプリを起動するか、強制停止後にアプリを起動した後、アプリのRTU状態を描画したり、アプリのサインインページを描画したりするのにかかる時間を測定することです。
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- logcatコマンド
adb shell logcat -v threadtime -b all -d
を実行して、fully_drawn
タイマー値をキャプチャします。 - アプリが完全に起動し、タイマー値がキャプチャされたら、次のコマンドを実行してアプリを強制停止します:
adb shell am force-stop <app_pkg>
- 必要なイテレーションの数だけ、この手順を繰り返します。
サインイン前のウォーム起動のRTUを測定する手順
サインイン前のウォーム起動のRTUとは、バックグラウンドまたは最近使用したアプリからアプリを起動した後、アプリのRTU状態を描画したり、アプリのサインインページを描画したりするのにかかる時間を測定することです。
- アプリがバックグラウンドで実行されていることを確認します。
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- logcatコマンド
adb shell logcat -v threadtime -b all -d
を実行して、fully_drawn
タイマー値をキャプチャします。 - アプリが完全に起動したら、次のコマンドを実行してアプリをバックグラウンドに移動します:
adb shell input keyevent KEYCODE_HOME
- 必要なイテレーションの数だけ、この手順を繰り返します。
サインイン後のクール起動のRTUを測定する手順
サインイン後のクール起動のRTUとは、ホームページからアプリを起動するか、強制停止後にアプリを起動した後、サインインしてからアプリのRTU状態を描画したり、アプリのホームページを描画したりするのにかかる時間を測定することです。
- テスト用の認証情報でアプリにサインインしていることを確認します。
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- 次のlogcatコマンドを実行して、完全描画のタイマー値を取得します:
adb shell logcat -v threadtime -b all -d
- アプリが完全に起動し、タイマー値がキャプチャされたら、次のコマンドを実行してアプリを強制停止します:
adb shell am force-stop <app_pkg>
- 必要なイテレーションの数だけ、この手順を繰り返します。
サインイン後のウォーム起動のRTUを測定する手順
サインイン後のウォーム起動のRTUとは、バックグラウンドまたは最近使用したアプリからアプリを起動した後、サインインしてからアプリのRTU状態を描画したり、アプリのホームページを描画したりするのにかかる時間を測定することです。
- アプリがバックグラウンドで実行され、テスト用の認証情報でアプリにログインしていることを確認します。
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- 次のlogcatコマンドを実行して、完全描画のタイマー値を取得します:
adb shell logcat -v threadtime -b all -d
- アプリが完全に起動したら、次のコマンドを実行してアプリをバックグラウンドに移動します:
adb shell input keyevent KEYCODE_HOME
- 必要なイテレーションの数だけ、この手順を繰り返します。
RTU ADBログ
Amazon FireタブレットデバイスのMonster Strikeアプリのアクティビティfully_drawn
のログを以下に示します。
**`10`****`-`****`06`****` `****`10`****`:`****`28`****`:`****`03.932`****` `****`678`****` `****`703`****` I `****`ActivityTaskManager`****`:`****` `****`Fully`****` drawn `****`<APP_PACKAGE_NAME`****`>`****`:`****` `****`+`****`5s36ms`**
Amazon Fire TVデバイスのPeacock TVアプリのアクティビティfully_drawn
のログを以下に示します。
03-27 13:19:26.362 527 537 I am_activity_fully_drawn_time: [0,180804427,<APP_MAIN_ACTIVITY>,7949,7859]
**03****-****27** **** **13****:****19****:****26.362** **** **527** **** **537** **I** **ActivityManager****:** **** **Fully** **drawn** **<****APP_MAIN_ACTIVITY****>****:** **** **+****7s949ms** **** **(****total** **+****7s859ms****)**
03-27 13:19:26.362 527 537 I sysui_multi_action: [324,0,757,1090,758,12,806,<APP_PACKAGE_NAME>,871,<APP_MAIN_ACTIVITY>,1091,7998]
Amazon Fire TVデバイスのMaxアプリのアクティビティfully_drawn
のログを以下に示します。
03-27 14:26:57.063 527 1134 I am_activity_fully_drawn_time: [0,170114187,<APP_PACKAGE_NAME>,3588,3462]
**03****-****27** **** **14****:****26****:****57.063** **** **527** **** **1134** **I** **ActivityManager****:** **** **Fully** **drawn <APP_MAIN_ACTIVITY>****:** **** **+****3s588ms** **** **(****total** **+****3s462ms****)**
メモリKPI
メモリKPIは、メモリ消費量の詳細な概要を示し、アプリのフォアグラウンドアクティビティとバックグラウンドアクティビティによるメモリ消費量またはメモリ消費量とその他のメモリ値を表します。このKPIは、メモリ値に加えて、フォアグラウンドまたはバックグラウンドのCPU使用率、RAM使用量、RAM空き容量、その他の詳細も測定します。
フォアグラウンドメモリ
フォアグラウンドメモリKPIは、フォアグラウンドでアプリが消費したメモリをキャプチャします。そのために、アプリのビデオまたはゲームをフォアグラウンドで15分間再生してから、アプリによるメモリ消費量を取得して計算します。
フォアグラウンドでのメモリ消費量の計算
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- アプリにサインインして、アプリのコアコンテンツ(ゲームまたはビデオ)を再生します。
- 次の
dumpsys
コマンドを実行し、total_pss
メモリ値を取得します:adb shell dumpsys meminfo -a <app_pkg>
- メモリ値がキャプチャされたら、次のコマンドを実行してアプリを強制停止します:
adb shell am force-stop <app_pkg>
- 必要なイテレーションの数だけ、この手順を繰り返します。
バックグラウンドメモリ
バックグラウンドメモリKPIは、バックグラウンドでアプリが消費したメモリをキャプチャします。そのために、アプリのビデオまたはゲームをフォアグラウンドで15分間再生し、バックグラウンドに移動してから、アプリによるメモリ消費量を取得して計算します。
バックグラウンドでのメモリ使用量の計算
- 次のlogcat消去コマンドを実行します:
adb shell logcat -v threadtime -b all -c
- アプリを起動します:
adb shell am start <app_pkg>/<app_main_activity>
- アプリにサインインして、アプリのコアコンテンツ(ゲームまたはビデオ)を再生します。
- アプリのナビゲーションが完了したら、次のコマンドを実行してアプリをバックグラウンドに移動します:
adb shell input keyevent KEYCODE_HOME
- アプリがバックグラウンドで安定するまで10~15秒待ちます。
- 次の
dumpsys
コマンドを実行し、total_pss
メモリ値を取得します:adb shell dumpsys meminfo -a <app_pkg>
- 必要なイテレーションの数だけ、この手順を繰り返します。
メモリADBダンプログ
比例セットサイズ(PSS)の合計は、デバイス上のアプリによって消費されるメモリ量です。PSS合計は、アプリがフォアグラウンドまたはバックグラウンドにあるときのメモリ消費量の計算に使用されます。
Pss Pss Shared Private Shared Private SwapPss Heap Heap Heap
Total Clean Dirty Dirty Clean Clean Dirty Size Alloc Free
Native Heap 115268 0 384 115020 100 208 22 151552 119143 32408
Dalvik Heap 15846 0 264 15124 140 676 11 21026 14882 6144
Dalvik Other 8864 0 40 8864 0 0 0
Stack 136 0 4 136 0 0 0
Ashmem 132 0 264 0 12 0 0
Other dev 48 0 156 0 0 48 0
.so mmap 15819 9796 656 596 26112 9796 20
.apk mmap 2103 432 0 0 26868 432 0
.dex mmap 39396 37468 0 4 17052 37468 0
.oat mmap 1592 452 0 0 13724 452 0
.art mmap 2699 304 808 1956 12044 304 0
Other mmap 274 0 12 4 636 244 0
GL mtrack 42152 0 0 42152 0 0 0
Unknown 2695 0 92 2684 60 0 0
TOTAL **247077** 48452 2680 186540 96748 49628 53 172578 134025 38552
その他のリンク
その他の便利なADBコマンドについては、ADBコマンドリストを参照してください。詳細については、テスト基準グループ2: Fire TVデバイスでのアプリの動作を参照してください。
KPIログファイルの作成
次のコードサンプルを使用して、1回のイテレーションのKPIログファイルを作成します。
public void log_file_writer(BufferedReader bf_read) {
File adb_log_file = new File(kpi_log_file_path + "/KPI_Log.txt");
FileWriter fileWriter = new FileWriter(adb_log_file);
try {
String reader = null;
while ((reader = bf_read.readLine()) != null) {
fileWriter.write(reader.trim() + "\n");
}
}
catch (Exception e) {
System.out.println(e);
}
finally {
fileWriter.flush();
fileWriter.close();
bf_read.close();
}
}
import java.io.File
import java.io.FileWriter
import java.io.BufferedReader
fun logFileWriter(bfRead: BufferedReader) {
val adbLogFile = File("$kpi_log_file_path/KPI_Log.txt")
val fileWriter = FileWriter(adbLogFile)
try {
var reader: String?
while (bfRead.readLine().also { reader = it } != null) {
fileWriter.write(reader?.trim() + "\n")
}
} catch (e: Exception) {
println(e)
} finally {
fileWriter.flush()
fileWriter.close()
bfRead.close()
}
}
Last updated: 2023年12月11日