Application resposne time is typically measured as the duration from when a user initiates an action to when the UI becomes fully interactive. For Android apps, this often focuses on cold start (app launched from scratch) and warm start (app relaunched after being backgrounded). Two broad approaches exist: hardware-based measurement using high-speed cameras and software-based measurement using logs and command-line tools.
Hardware Approach
With a high-speed camera, define the start frame (e.g., tap event) and the end frame (e.g., first visible content). The system automatically calculates the elapsed time between these frames.
Software Approach
The software approach can be manual or automated. One manual method involves monitoring logcat output. Clear the buffer with adb logcat -c, then filter events with adb logcat -v threadtime -b events and look for activity_launch_time entries which include both response and initialization time.
A more systematic method uses Python scripts to invoke adb shell am start -W -n. The output contains three key metrics:
- ThisTime: Time taken by the target activity to start.
- TotalTime: Total app startup time (ThisTime + app resource initialization).
- WaitTime: System-wide startup time (TotalTime + system resource initialization).
For automated measurement, many testers parse ThisTime from the command output. The variation across runs can be visualized as a trend curve, and the average is compared against competitors (competitive analysis) or against previous versions (horizontal comparison).
Cold Start Measurement with Python
Cold start requires the app to be fully stopped before each launch. The following script forces the app to stop, launches it via am start, extracts ThisTime, and stores timestamped data into a CSV file.
import csv
import os
import time
class AppTester:
def __init__(self):
self.cmd_output = ""
self.start_time = 0
def launch_app(self):
cmd = 'adb shell am start -W -n com.android.contacts/.activities.PeopleActivity'
self.cmd_output = os.popen(cmd)
def stop_app_cold(self):
cmd = 'adb shell am force-stop com.android.contacts'
os.popen(cmd)
def get_launch_time(self):
for line in self.cmd_output.readlines():
if "ThisTime" in line:
self.start_time = line.split(":")[1].strip()
break
return self.start_time
class TestController:
def __init__(self, iteration_count):
self.tester = AppTester()
self.remaining = iteration_count
self.records = [("timestamp", "launch_time_ms")]
def single_run(self):
self.tester.launch_app()
time.sleep(5)
elapsed = self.tester.get_launch_time()
self.tester.stop_app_cold()
time.sleep(3)
current = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self.records.append((current, elapsed))
def run_all(self):
while self.remaining > 0:
self.single_run()
self.remaining -= 1
def save_to_csv(self):
with open('cold_launch.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerows(self.records)
if __name__ == "__main__":
controller = TestController(10)
controller.run_all()
controller.save_to_csv()
Warm Start Measurement with Python
Warm start simulates the user pressing the Home button and then relaunching the app. The app is sent to the background using the KEYCODE_HOME event (keycode 3), preserving its process in memory.
import csv
import os
import time
class AppTester:
def __init__(self):
self.cmd_output = ""
self.start_time = 0
def launch_app(self):
cmd = 'adb shell am start -W -n com.android.contacts/.activities.PeopleActivity'
self.cmd_output = os.popen(cmd)
def stop_app_warm(self):
cmd = 'adb shell input keyevent 3'
os.popen(cmd)
def get_launch_time(self):
for line in self.cmd_output.readlines():
if "ThisTime" in line:
self.start_time = line.split(":")[1].strip()
break
return self.start_time
class TestController:
def __init__(self, iteration_count):
self.tester = AppTester()
self.remaining = iteration_count
self.records = [("timestamp", "launch_time_ms")]
def single_run(self):
self.tester.launch_app()
time.sleep(5)
elapsed = self.tester.get_launch_time()
self.tester.stop_app_warm()
time.sleep(3)
current = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self.records.append((current, elapsed))
def run_all(self):
while self.remaining > 0:
self.single_run()
self.remaining -= 1
def save_to_csv(self):
with open('warm_launch.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerows(self.records)
if __name__ == "__main__":
controller = TestController(10)
controller.run_all()
controller.save_to_csv()
Data Analysis
After collecting several measurements (e.g., 10 runs), you can plot the data to observe variance and compute the average. Excluding the first sample (which may be an outlier due to system caching) is common. Use the resulting metrics for comparative benchmarks between app versions or competing products.