開発者コンソール

アプリのパフォーマンススクリプト

アプリのパフォーマンススクリプト

パフォーマンステストは、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デバイスオブジェクトは、ティアダウンの一環として強制停止されます。

デバイスタイプの取得

テスト自動化スクリプトに追加できるテストメソッドの例として、次のコードサンプルを使用してください。

Java
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;
}
Kotlin
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
}

アプリのメインランチャーアクティビティの生成

次のコードサンプルを使用して、メインランチャーアクティビティを生成します。このメソッドは、テスト対象のアプリのメインアクティビティを取得し、アプリパッケージとメインアクティビティを組み合わせてメインランチャーアクティビティを生成します。

Java
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);
}            
Kotlin
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")
}

メインランチャーアクティビティ(アプリインテント)でのアプリの起動

次のコードサンプルを使用して、アプリインテントまたはアプリパッケージとメインアクティビティを使用してアプリを起動します。

Java
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);
        }
Kotlin
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ツールを使用してアプリを起動するには、次のコードを使用します。

Java
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;
}  
Kotlin
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
}

アプリの強制停止

次のコードサンプルを使用して、アプリを強制停止します。

Java
try {
       Runtime.getRuntime().exec("adb -s "+ DSN +" shell am force-stop " + appPackage);
       System.out.println("アプリを強制停止しました - " + appPackage);
} 
catch (Exception e) {
       System.out.println("アプリを強制停止中に例外が発生しました" + e);
}
Kotlin
try {
    Runtime.getRuntime().exec("adb -s ${DSN} shell am force-stop ${appPackage}")
    println("アプリを強制停止しました - $appPackage")
} catch (e: Exception) {
    println("アプリを強制停止中に例外が発生しました: $e")
}

レイテンシKPI

最初のフレームまでのレイテンシは、アプリが起動してから、アプリによって最初のフレームが描画されるまでにかかる時間です。これは、さまざまな方法で起動中のアプリのパフォーマンスを測定するために使用されます。このKPIは、ユーザーの行動を再現し、ユーザーエクスペリエンスを向上させるために使用されます。

アプリをプログラムによって起動する前に、アプリのパッケージ名、main_activity、その他の詳細など、基本的なパラメーターが必要です。

ログで使用される一般的な略語

Java
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:";
Kotlin
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ログをスレッドタイムでキャプチャし、アプリのパフォーマンスのレイテンシ指標を取得します。

Java
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;
}
Kotlin
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
    }
}

バイタルバッファーログからタイマー値をキャプチャ

次のコードサンプルを使用して、バイタルバッファーからパフォーマンスレイテンシタイマー値を除外します。

Java
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;     
} 
Kotlin
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
}

アクティビティマネージャーのバッファーログからタイマー値をキャプチャ

次のコードは、アクティビティまたはウィンドウマネージャーのバッファーを使用してアプリのタイマー値を除外します。

Java
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;
    }
Kotlin
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
}

クール起動から最初のフレームまで

クール起動から最初のフレームまでのレイテンシは、ホーム画面からアプリを起動するか、強制停止後にアプリを起動してから、アプリの最初のフレームを描画するまでにかかる時間を測定します。この指標は、このタイプの起動におけるアプリのパフォーマンスを測定します。これは、強制停止後にサービスを読み込んでキャッシュに保持する場合、通常、アプリの起動により多くの時間がかかるためです。

クール起動から最初のフレームまでのレイテンシを測定するには、次の手順に従います。

  1. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  2. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  3. logcatコマンドadb shell logcat -v threadtime -b all -dを実行して、cool_app_launch_timeタイマー値をキャプチャします。
  4. アプリが完全に起動したら、次のコマンドを実行しアプリを強制停止します:adb shell am force-stop <app_pkg>
  5. 必要なイテレーションの数だけ、この手順を繰り返します。

クールイテレーション

次のコードサンプルを使用して、クール起動のレイテンシテストを実行し、要求されたイテレーションのレイテンシ値を出力します。

Java
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");
}
Kotlin
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

ウォーム起動から最初のフレームまで

ウォーム起動から最初のフレームまでのレイテンシは、バックグラウンドまたは最近使用したアプリからアプリを起動してから、最初のフレームを描画するまでにかかる時間を測定します。この指標はアプリのパフォーマンスを測定するために使用されます。バックグラウンドから起動する場合は、必要なサービスがキャッシュ内で利用でき、アプリの起動にかかる時間が短くなるためです。

ウォーム起動から最初のフレームまでのレイテンシを測定するには、次の手順に従います。

  1. アプリがバックグラウンドで実行されていることを確認します。
  2. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  3. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  4. logcatコマンドadb shell logcat -v threadtime -b all -dを実行して、warm_app_warm_transition_launch_timeタイマー値をキャプチャします。
  5. アプリが完全に起動したら、adb shell input keyevent KEYCODE_HOMEを実行します。
  6. 必要なイテレーションの数だけ、この手順を繰り返します。

ウォームイテレーション

次のコードサンプルを使用して、ウォーム起動のレイテンシテストを実行し、要求されたイテレーションのレイテンシ値を出力します。

Java
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");
}
Kotlin
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状態を描画したり、アプリのサインインページを描画したりするのにかかる時間を測定することです。

  1. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  2. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  3. logcatコマンドadb shell logcat -v threadtime -b all -dを実行して、fully_drawnタイマー値をキャプチャします。
  4. アプリが完全に起動し、タイマー値がキャプチャされたら、次のコマンドを実行してアプリを強制停止します:adb shell am force-stop <app_pkg>
  5. 必要なイテレーションの数だけ、この手順を繰り返します。

サインイン前のウォーム起動のRTUを測定する手順

サインイン前のウォーム起動のRTUとは、バックグラウンドまたは最近使用したアプリからアプリを起動した後、アプリのRTU状態を描画したり、アプリのサインインページを描画したりするのにかかる時間を測定することです。

  1. アプリがバックグラウンドで実行されていることを確認します。
  2. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  3. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  4. logcatコマンドadb shell logcat -v threadtime -b all -dを実行して、fully_drawnタイマー値をキャプチャします。
  5. アプリが完全に起動したら、次のコマンドを実行してアプリをバックグラウンドに移動します:adb shell input keyevent KEYCODE_HOME
  6. 必要なイテレーションの数だけ、この手順を繰り返します。

サインイン後のクール起動のRTUを測定する手順

サインイン後のクール起動のRTUとは、ホームページからアプリを起動するか、強制停止後にアプリを起動した後、サインインしてからアプリのRTU状態を描画したり、アプリのホームページを描画したりするのにかかる時間を測定することです。

  1. テスト用の認証情報でアプリにサインインしていることを確認します。
  2. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  3. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  4. 次のlogcatコマンドを実行して、完全描画のタイマー値を取得します:adb shell logcat -v threadtime -b all -d
  5. アプリが完全に起動し、タイマー値がキャプチャされたら、次のコマンドを実行してアプリを強制停止します:adb shell am force-stop <app_pkg>
  6. 必要なイテレーションの数だけ、この手順を繰り返します。

サインイン後のウォーム起動のRTUを測定する手順

サインイン後のウォーム起動のRTUとは、バックグラウンドまたは最近使用したアプリからアプリを起動した後、サインインしてからアプリのRTU状態を描画したり、アプリのホームページを描画したりするのにかかる時間を測定することです。

  1. アプリがバックグラウンドで実行され、テスト用の認証情報でアプリにログインしていることを確認します。
  2. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  3. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  4. 次のlogcatコマンドを実行して、完全描画のタイマー値を取得します:adb shell logcat -v threadtime -b all -d
  5. アプリが完全に起動したら、次のコマンドを実行してアプリをバックグラウンドに移動します:adb shell input keyevent KEYCODE_HOME
  6. 必要なイテレーションの数だけ、この手順を繰り返します。

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分間再生してから、アプリによるメモリ消費量を取得して計算します。

フォアグラウンドでのメモリ消費量の計算

  1. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  2. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  3. アプリにサインインして、アプリのコアコンテンツ(ゲームまたはビデオ)を再生します。
  4. 次のdumpsysコマンドを実行し、total_pssメモリ値を取得します:adb shell dumpsys meminfo -a <app_pkg>
  5. メモリ値がキャプチャされたら、次のコマンドを実行してアプリを強制停止します:adb shell am force-stop <app_pkg>
  6. 必要なイテレーションの数だけ、この手順を繰り返します。

バックグラウンドメモリ

バックグラウンドメモリKPIは、バックグラウンドでアプリが消費したメモリをキャプチャします。そのために、アプリのビデオまたはゲームをフォアグラウンドで15分間再生し、バックグラウンドに移動してから、アプリによるメモリ消費量を取得して計算します。

バックグラウンドでのメモリ使用量の計算

  1. 次のlogcat消去コマンドを実行します:adb shell logcat -v threadtime -b all -c
  2. アプリを起動します:adb shell am start <app_pkg>/<app_main_activity>
  3. アプリにサインインして、アプリのコアコンテンツ(ゲームまたはビデオ)を再生します。
  4. アプリのナビゲーションが完了したら、次のコマンドを実行してアプリをバックグラウンドに移動します:adb shell input keyevent KEYCODE_HOME
  5. アプリがバックグラウンドで安定するまで10~15秒待ちます。
  6. 次のdumpsysコマンドを実行し、total_pssメモリ値を取得します:adb shell dumpsys meminfo -a <app_pkg>
  7. 必要なイテレーションの数だけ、この手順を繰り返します。

メモリ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ログファイルを作成します。

Java
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();
  }   
}
Kotlin
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日